| Index: boto/storage_uri.py
|
| diff --git a/boto/storage_uri.py b/boto/storage_uri.py
|
| index 9c051a4be2b362e9a7b29de12e1d747441a2a853..96b8ecacd2393c11d57760e7ca5d966a815bc163 100755
|
| --- a/boto/storage_uri.py
|
| +++ b/boto/storage_uri.py
|
| @@ -1,4 +1,5 @@
|
| # Copyright 2010 Google Inc.
|
| +# Copyright (c) 2011, Nexenta Systems Inc.
|
| #
|
| # Permission is hereby granted, free of charge, to any person obtaining a
|
| # copy of this software and associated documentation files (the
|
| @@ -19,6 +20,7 @@
|
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
| # IN THE SOFTWARE.
|
|
|
| +import boto
|
| import os
|
| from boto.exception import BotoClientError
|
| from boto.exception import InvalidUriError
|
| @@ -34,6 +36,10 @@ class StorageUri(object):
|
| """
|
|
|
| connection = None
|
| + # Optional args that can be set from one of the concrete subclass
|
| + # constructors, to change connection behavior (e.g., to override
|
| + # https_connection_factory).
|
| + connection_args = None
|
|
|
| def __init__(self):
|
| """Uncallable constructor on abstract base StorageUri class.
|
| @@ -66,15 +72,28 @@ class StorageUri(object):
|
| @return: A connection to storage service provider of the given URI.
|
| """
|
|
|
| + connection_args = dict(self.connection_args or ())
|
| + # Use OrdinaryCallingFormat instead of boto-default
|
| + # SubdomainCallingFormat because the latter changes the hostname
|
| + # that's checked during cert validation for HTTPS connections,
|
| + # which will fail cert validation (when cert validation is enabled).
|
| + # Note: the following import can't be moved up to the start of
|
| + # this file else it causes a config import failure when run from
|
| + # the resumable upload/download tests.
|
| + from boto.s3.connection import OrdinaryCallingFormat
|
| + connection_args['calling_format'] = OrdinaryCallingFormat()
|
| + connection_args.update(kwargs)
|
| if not self.connection:
|
| if self.scheme == 's3':
|
| from boto.s3.connection import S3Connection
|
| self.connection = S3Connection(access_key_id,
|
| - secret_access_key, **kwargs)
|
| + secret_access_key,
|
| + **connection_args)
|
| elif self.scheme == 'gs':
|
| from boto.gs.connection import GSConnection
|
| self.connection = GSConnection(access_key_id,
|
| - secret_access_key, **kwargs)
|
| + secret_access_key,
|
| + **connection_args)
|
| elif self.scheme == 'file':
|
| from boto.file.connection import FileConnection
|
| self.connection = FileConnection(self)
|
| @@ -156,7 +175,7 @@ class BucketStorageUri(StorageUri):
|
| """
|
|
|
| def __init__(self, scheme, bucket_name=None, object_name=None,
|
| - debug=0):
|
| + debug=0, connection_args=None):
|
| """Instantiate a BucketStorageUri from scheme,bucket,object tuple.
|
|
|
| @type scheme: string
|
| @@ -167,6 +186,10 @@ class BucketStorageUri(StorageUri):
|
| @param object_name: object name
|
| @type debug: int
|
| @param debug: debug level to pass in to connection (range 0..2)
|
| + @type connection_args: map
|
| + @param connection_args: optional map containing args to be
|
| + passed to {S3,GS}Connection constructor (e.g., to override
|
| + https_connection_factory).
|
|
|
| After instantiation the components are available in the following
|
| fields: uri, scheme, bucket_name, object_name.
|
| @@ -175,6 +198,8 @@ class BucketStorageUri(StorageUri):
|
| self.scheme = scheme
|
| self.bucket_name = bucket_name
|
| self.object_name = object_name
|
| + if connection_args:
|
| + self.connection_args = connection_args
|
| if self.bucket_name and self.object_name:
|
| self.uri = ('%s://%s/%s' % (self.scheme, self.bucket_name,
|
| self.object_name))
|
| @@ -207,6 +232,22 @@ class BucketStorageUri(StorageUri):
|
| self.check_response(acl, 'acl', self.uri)
|
| return acl
|
|
|
| + def get_location(self, validate=True, headers=None):
|
| + if not self.bucket_name:
|
| + raise InvalidUriError('get_location on bucket-less URI (%s)' %
|
| + self.uri)
|
| + bucket = self.get_bucket(validate, headers)
|
| + return bucket.get_location()
|
| +
|
| + def get_subresource(self, subresource, validate=True, headers=None,
|
| + version_id=None):
|
| + if not self.bucket_name:
|
| + raise InvalidUriError(
|
| + 'get_subresource on bucket-less URI (%s)' % self.uri)
|
| + bucket = self.get_bucket(validate, headers)
|
| + return bucket.get_subresource(subresource, self.object_name, headers,
|
| + version_id)
|
| +
|
| def add_group_email_grant(self, permission, email_address, recursive=False,
|
| validate=True, headers=None):
|
| if self.scheme != 'gs':
|
| @@ -318,6 +359,15 @@ class BucketStorageUri(StorageUri):
|
| self.check_response(key, 'key', self.uri)
|
| key.set_canned_acl(acl_str, headers, version_id)
|
|
|
| + def set_subresource(self, subresource, value, validate=True, headers=None,
|
| + version_id=None):
|
| + if not self.bucket_name:
|
| + raise InvalidUriError(
|
| + 'set_subresource on bucket-less URI (%s)' % self.uri)
|
| + bucket = self.get_bucket(validate, headers)
|
| + bucket.set_subresource(subresource, value, self.object_name, headers,
|
| + version_id)
|
| +
|
| def set_contents_from_string(self, s, headers=None, replace=True,
|
| cb=None, num_cb=10, policy=None, md5=None,
|
| reduced_redundancy=False):
|
| @@ -325,6 +375,23 @@ class BucketStorageUri(StorageUri):
|
| key.set_contents_from_string(s, headers, replace, cb, num_cb, policy,
|
| md5, reduced_redundancy)
|
|
|
| + def enable_logging(self, target_bucket, target_prefix=None,
|
| + canned_acl=None, validate=True, headers=None,
|
| + version_id=None):
|
| + if not self.bucket_name:
|
| + raise InvalidUriError(
|
| + 'disable_logging on bucket-less URI (%s)' % self.uri)
|
| + bucket = self.get_bucket(validate, headers)
|
| + bucket.enable_logging(target_bucket, target_prefix, headers=headers,
|
| + canned_acl=canned_acl)
|
| +
|
| + def disable_logging(self, validate=True, headers=None, version_id=None):
|
| + if not self.bucket_name:
|
| + raise InvalidUriError(
|
| + 'disable_logging on bucket-less URI (%s)' % self.uri)
|
| + bucket = self.get_bucket(validate, headers)
|
| + bucket.disable_logging(headers=headers)
|
| +
|
|
|
|
|
| class FileStorageUri(StorageUri):
|
| @@ -335,7 +402,7 @@ class FileStorageUri(StorageUri):
|
| See file/README about how we map StorageUri operations onto a file system.
|
| """
|
|
|
| - def __init__(self, object_name, debug):
|
| + def __init__(self, object_name, debug, is_stream=False):
|
| """Instantiate a FileStorageUri from a path name.
|
|
|
| @type object_name: string
|
| @@ -353,6 +420,7 @@ class FileStorageUri(StorageUri):
|
| self.object_name = object_name
|
| self.uri = 'file://' + object_name
|
| self.debug = debug
|
| + self.stream = is_stream
|
|
|
| def clone_replace_name(self, new_name):
|
| """Instantiate a FileStorageUri from the current FileStorageUri,
|
| @@ -361,20 +429,33 @@ class FileStorageUri(StorageUri):
|
| @type new_name: string
|
| @param new_name: new object name
|
| """
|
| - return FileStorageUri(new_name, self.debug)
|
| + return FileStorageUri(new_name, self.debug, self.stream)
|
|
|
| def names_container(self):
|
| - """Returns True if this URI names a directory.
|
| + """Returns True if this URI is not representing input/output stream
|
| + and names a directory.
|
| """
|
| - return os.path.isdir(self.object_name)
|
| + if not self.stream:
|
| + return os.path.isdir(self.object_name)
|
| + else:
|
| + return False
|
|
|
| def names_singleton(self):
|
| - """Returns True if this URI names a file.
|
| + """Returns True if this URI names a file or
|
| + if URI represents input/output stream.
|
| """
|
| - return os.path.isfile(self.object_name)
|
| + if self.stream:
|
| + return True
|
| + else:
|
| + return os.path.isfile(self.object_name)
|
|
|
| def is_file_uri(self):
|
| return True
|
|
|
| def is_cloud_uri(self):
|
| return False
|
| +
|
| + def is_stream(self):
|
| + """Retruns True if this URI represents input/output stream.
|
| + """
|
| + return self.stream
|
|
|