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 |