libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1406447 - in /libcloud/trunk: CHANGES libcloud/storage/drivers/local.py libcloud/storage/providers.py libcloud/storage/types.py libcloud/test/storage/test_local.py setup.py tox.ini
Date Wed, 07 Nov 2012 04:11:07 GMT
Author: tomaz
Date: Wed Nov  7 04:11:06 2012
New Revision: 1406447

URL: http://svn.apache.org/viewvc?rev=1406447&view=rev
Log:
Add a new "local storage" storage driver. Contributed by Mahendra M, part of LIBCLOUD-252.

Added:
    libcloud/trunk/libcloud/storage/drivers/local.py
    libcloud/trunk/libcloud/test/storage/test_local.py
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/storage/providers.py
    libcloud/trunk/libcloud/storage/types.py
    libcloud/trunk/setup.py
    libcloud/trunk/tox.ini

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1406447&r1=1406446&r2=1406447&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Wed Nov  7 04:11:06 2012
@@ -40,6 +40,11 @@ Changes with Apache Libcloud in developm
      the code to make it easier to maintain.
      [Tomaz Muraus]
 
+  *) Storage
+
+    - Add a new local storage driver.
+      [Mahendra M]
+
   *) DNS
 
     - Update 'if type' checks in the update_record methods to behave correctly

Added: libcloud/trunk/libcloud/storage/drivers/local.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/local.py?rev=1406447&view=auto
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/local.py (added)
+++ libcloud/trunk/libcloud/storage/drivers/local.py Wed Nov  7 04:11:06 2012
@@ -0,0 +1,592 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Provides storage driver for working with local filesystem
+"""
+
+import errno
+import os
+import shutil
+import sys
+
+try:
+    from lockfile import mkdirlockfile
+except ImportError:
+    raise ImportError('Missing lockfile dependency, you can install it ' \
+                      'using pip: pip install lockfile')
+
+from libcloud.utils.files import read_in_chunks
+from libcloud.common.base import Connection
+from libcloud.storage.base import Object, Container, StorageDriver
+from libcloud.common.types import LibcloudError, LazyList
+from libcloud.storage.types import ContainerAlreadyExistsError
+from libcloud.storage.types import ContainerDoesNotExistError
+from libcloud.storage.types import ContainerIsNotEmptyError
+from libcloud.storage.types import ObjectError
+from libcloud.storage.types import ObjectDoesNotExistError
+from libcloud.storage.types import InvalidContainerNameError
+
+IGNORE_FOLDERS = ['.lock', '.hash']
+
+
+class LockLocalStorage(object):
+    """
+    A class to help in locking a local path before being updated
+    """
+    def __init__(self, path):
+        self.path = path
+        self.lock = mkdirlockfile.MkdirLockFile(self.path, threaded=True)
+
+    def __enter__(self):
+        try:
+            self.lock.acquire(timeout=0.1)
+        except lockfile.LockTimeout:
+            raise LibcloudError('Lock timeout')
+
+    def __exit__(self, type, value, traceback):
+        if self.lock.is_locked():
+            self.lock.release()
+
+        if value is not None:
+            raise value
+
+
+class LocalStorageDriver(StorageDriver):
+    """
+    Implementation of local file-system based storage. This is helpful
+    where the user would want to use the same code (using libcloud) and
+    switch between cloud storage and local storage
+    """
+
+    connectionCls = Connection
+    name = 'Local Storage'
+
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 **kwargs):
+
+        # Use the key as the path to the storage
+        self.base_path = key[0]
+
+        if not os.path.isdir(self.base_path):
+            raise LibcloudError('The base path is not a directory')
+
+        super(StorageDriver, self).__init__(key=key, secret=secret,
+                                            secure=secure, host=host,
+                                            port=port, **kwargs)
+
+    def _make_path(self, path, ignore_existing=True):
+        """
+        Create a path by checking if it already exists
+        """
+
+        try:
+            os.makedirs(path)
+        except OSError:
+            exp = sys.exc_info()[1]
+            if exp.errno == errno.EEXIST and not ignore_existing:
+                raise exp
+
+    def _check_container_name(self, container_name):
+        """
+        Check if the container name is valid
+
+        @param container_name: Container name
+        @type container_name: C{str}
+        """
+
+        if '/' in container_name or '\\' in container_name:
+            raise InvalidContainerNameError(value=None, driver=self,
+                                            container_name=container_name)
+
+    def _make_container(self, container_name):
+        """
+        Create a container instance
+
+        @param container_name: Container name.
+        @type container_name: C{str}
+
+        @return: Container instance.
+        @rtype: L{Container}
+        """
+
+        self._check_container_name(container_name)
+
+        full_path = os.path.join(self.base_path, container_name)
+
+        try:
+            stat = os.stat(full_path)
+            if not os.path.isdir(full_path):
+                raise OSError('Target path is not a directory')
+        except OSError:
+            raise ContainerDoesNotExistError(value=None, driver=self,
+                                             container_name=container_name)
+
+        extra = {}
+        extra['creation_time'] = stat.st_ctime
+        extra['access_time'] = stat.st_atime
+        extra['modify_time'] = stat.st_mtime
+
+        return Container(name=container_name, extra=extra, driver=self)
+
+    def _make_object(self, container, object_name):
+        """
+        Create an object instance
+
+        @param container: Container.
+        @type container: L{Container}
+
+        @param object_name: Object name.
+        @type object_name: C{str}
+
+        @return: Object instance.
+        @rtype: L{Object}
+        """
+
+        full_path = os.path.join(self.base_path, container.name, object_name)
+
+        if os.path.isdir(full_path):
+            raise ObjectError(value=None, driver=self, object_name=object_name)
+
+        try:
+            stat = os.stat(full_path)
+        except Exception:
+            raise ObjectDoesNotExistError(value=None, driver=self,
+                                          object_name=object_name)
+
+        extra = {}
+        extra['creation_time'] = stat.st_ctime
+        extra['access_time'] = stat.st_atime
+        extra['modify_time'] = stat.st_mtime
+
+        return Object(name=object_name, size=stat.st_size, extra=extra,
+                      driver=self, container=container, hash=None,
+                      meta_data=None)
+
+    def list_containers(self):
+        """
+        Return a list of containers.
+
+        @return: A list of Container instances.
+        @rtype: C{list} of L{Container}
+        """
+
+        containers = []
+
+        for container_name in os.listdir(self.base_path):
+            full_path = os.path.join(self.base_path, container_name)
+            if not os.path.isdir(full_path):
+                continue
+            containers.append(self._make_container(container_name))
+
+        return containers
+
+    def _get_objects(self, container):
+        """
+        Recursively iterate through the file-system and return the object names
+        """
+
+        cpath = self.get_container_cdn_url(container, check=True)
+
+        for folder, subfolders, files in os.walk(cpath, topdown=True):
+            # Remove unwanted subfolders
+            for subf in IGNORE_FOLDERS:
+                if subf in subfolders:
+                    subfolders.remove(subf)
+
+            for name in files:
+                full_path = os.path.join(folder, name)
+                object_name = os.path.relpath(full_path, start=cpath)
+                yield self._make_object(container, object_name)
+
+    def _get_more(self, last_key, value_dict):
+        """
+        A handler for using with LazyList
+        """
+        container = value_dict['container']
+        objects = [obj for obj in self._get_objects(container)]
+
+        return (objects, None, True)
+
+    def list_container_objects(self, container):
+        """
+        Return a list of objects for the given container.
+
+        @param container: Container instance
+        @type container: L{Container}
+
+        @return: A list of Object instances.
+        @rtype: C{list} of L{Object}
+        """
+
+        value_dict = {'container': container}
+        return LazyList(get_more=self._get_more, value_dict=value_dict)
+
+    def get_container(self, container_name):
+        """
+        Return a container instance.
+
+        @param container_name: Container name.
+        @type container_name: C{str}
+
+        @return: L{Container} instance.
+        @rtype: L{Container}
+        """
+        return self._make_container(container_name)
+
+    def get_container_cdn_url(self, container, check=False):
+        """
+        Return a container CDN URL.
+
+        @param container: Container instance
+        @type  container: L{Container}
+
+        @param check: Indicates if the path's existance must be checked
+        @type check: C{bool}
+
+        @return: A CDN URL for this container.
+        @rtype: C{str}
+        """
+        path = os.path.join(self.base_path, container.name)
+
+        if check and not os.path.isdir(path):
+            raise ContainerDoesNotExistError(value=None, driver=self,
+                                             container_name=container.name)
+
+        return path
+
+    def get_object(self, container_name, object_name):
+        """
+        Return an object instance.
+
+        @param container_name: Container name.
+        @type  container_name: C{str}
+
+        @param object_name: Object name.
+        @type  object_name: C{str}
+
+        @return: L{Object} instance.
+        @rtype: L{Object}
+        """
+        container = self._make_container(container_name)
+        return self._make_object(container, object_name)
+
+    def get_object_cdn_url(self, obj):
+        """
+        Return a object CDN URL.
+
+        @param obj: Object instance
+        @type  obj: L{Object}
+
+        @return: A CDN URL for this object.
+        @rtype: C{str}
+        """
+        return os.path.join(self.base_path, obj.container.name, obj.name)
+
+    def enable_container_cdn(self, container):
+        """
+        Enable container CDN.
+
+        @param container: Container instance
+        @type  container: L{Container}
+
+        @rtype: C{bool}
+        """
+
+        path = self.get_container_cdn_url(container)
+        lock = lockfile.MkdirFileLock(path, threaded=True)
+
+        with LockLocalStorage(path) as lock:
+            self._make_path(path)
+
+        return True
+
+    def enable_object_cdn(self, obj):
+        """
+        Enable object CDN.
+
+        @param obj: Object instance
+        @type  obj: L{Object}
+
+        @rtype: C{bool}
+        """
+        path = self.get_object_cdn_url(obj)
+
+        with LockLocalStorage(path) as lock:
+            if os.path.exists(path):
+                return False
+            try:
+                obj_file = open(path, 'w')
+                obj_file.close()
+            except:
+                return False
+
+        return True
+
+    def download_object(self, obj, destination_path, overwrite_existing=False,
+                        delete_on_failure=True):
+        """
+        Download an object to the specified destination path.
+
+        @param obj: Object instance.
+        @type obj: L{Object}
+
+        @param destination_path: Full path to a file or a directory where the
+                                incoming file will be saved.
+        @type destination_path: C{str}
+
+        @param overwrite_existing: True to overwrite an existing file,
+            defaults to False.
+        @type overwrite_existing: C{bool}
+
+        @param delete_on_failure: True to delete a partially downloaded file if
+        the download was not successful (hash mismatch / file size).
+        @type delete_on_failure: C{bool}
+
+        @return: True if an object has been successfully downloaded, False
+        otherwise.
+        @rtype: C{bool}
+        """
+
+        obj_path = self.get_object_cdn_url(obj)
+        base_name = os.path.basename(destination_path)
+
+        if not base_name and not os.path.exists(destination_path):
+            raise LibcloudError(
+                value='Path %s does not exist' % (destination_path),
+                driver=self)
+
+        if not base_name:
+            file_path = os.path.join(destination_path, obj.name)
+        else:
+            file_path = destination_path
+
+        if os.path.exists(file_path) and not overwrite_existing:
+            raise LibcloudError(
+                value='File %s already exists, but ' % (file_path) +
+                'overwrite_existing=False',
+                driver=self)
+
+        try:
+            shutil.copy(obj_path, file_path)
+        except IOError:
+            if delete_on_failure:
+                try:
+                    os.unlink(file_path)
+                except Exception:
+                    pass
+            return False
+
+        return True
+
+    def download_object_as_stream(self, obj, chunk_size=None):
+        """
+        Return a generator which yields object data.
+
+        @param obj: Object instance
+        @type obj: L{Object}
+
+        @param chunk_size: Optional chunk size (in bytes).
+        @type chunk_size: C{int}
+
+        @rtype: C{object}
+        """
+
+        path = self.get_object_cdn_url(obj)
+
+        with open(path) as obj_file:
+            for data in read_in_chunks(obj_file, chunk_size=chunk_size):
+                yield data
+
+    def upload_object(self, file_path, container, object_name, extra=None,
+                      verify_hash=True):
+        """
+        Upload an object currently located on a disk.
+
+        @param file_path: Path to the object on disk.
+        @type file_path: C{str}
+
+        @param container: Destination container.
+        @type container: L{Container}
+
+        @param object_name: Object name.
+        @type object_name: C{str}
+
+        @param verify_hash: Verify hast
+        @type verify_hash: C{bool}
+
+        @param extra: (optional) Extra attributes (driver specific).
+        @type extra: C{dict}
+
+        @rtype: C{object}
+        """
+
+        path = self.get_container_cdn_url(container, check=True)
+        obj_path = os.path.join(path, object_name)
+        base_path = os.path.dirname(obj_path)
+
+        self._make_path(base_path)
+
+        with LockLocalStorage(obj_path) as lock:
+            shutil.copy(file_path, obj_path)
+
+        os.chmod(obj_path, int('664', 8))
+
+        return self._make_object(container, object_name)
+
+    def upload_object_via_stream(self, iterator, container,
+                                 object_name,
+                                 extra=None):
+        """
+        Upload an object using an iterator.
+
+        If a provider supports it, chunked transfer encoding is used and you
+        don't need to know in advance the amount of data to be uploaded.
+
+        Otherwise if a provider doesn't support it, iterator will be exhausted
+        so a total size for data to be uploaded can be determined.
+
+        Note: Exhausting the iterator means that the whole data must be
+        buffered in memory which might result in memory exhausting when
+        uploading a very large object.
+
+        If a file is located on a disk you are advised to use upload_object
+        function which uses fs.stat function to determine the file size and it
+        doesn't need to buffer whole object in the memory.
+
+        @type iterator: C{object}
+        @param iterator: An object which implements the iterator interface.
+
+        @type container: L{Container}
+        @param container: Destination container.
+
+        @type object_name: C{str}
+        @param object_name: Object name.
+
+        @type extra: C{dict}
+        @param extra: (optional) Extra attributes (driver specific). Note:
+            This dictionary must contain a 'content_type' key which represents
+            a content type of the stored object.
+
+        @rtype: C{object}
+        """
+
+        path = self.get_container_cdn_url(container, check=True)
+        obj_path = os.path.join(path, object_name)
+        base_path = os.path.dirname(obj_path)
+
+        self._make_path(base_path)
+
+        with LockLocalStorage(obj_path) as lock:
+            obj_file = open(obj_path, 'w')
+            for data in iterator:
+                obj_file.write(data)
+
+            obj_file.close()
+
+        os.chmod(obj_path, int('664', 8))
+
+        return self._make_object(container, object_name)
+
+    def delete_object(self, obj):
+        """
+        Delete an object.
+
+        @type obj: L{Object}
+        @param obj: Object instance.
+
+        @return: C{bool} True on success.
+        @rtype: C{bool}
+        """
+
+        path = self.get_object_cdn_url(obj)
+
+        with LockLocalStorage(path) as lock:
+            try:
+                os.unlink(path)
+            except Exception:
+                return False
+
+        # Check and delete the folder if required
+        path = os.path.dirname(path)
+
+        try:
+            if path != obj.container.get_cdn_url():
+                os.rmdir(path)
+        except Exception:
+            pass
+
+        return True
+
+    def create_container(self, container_name):
+        """
+        Create a new container.
+
+        @type container_name: C{str}
+        @param container_name: Container name.
+
+        @return: C{Container} instance on success.
+        @rtype: L{Container}
+        """
+
+        self._check_container_name(container_name)
+
+        path = os.path.join(self.base_path, container_name)
+
+        try:
+            self._make_path(path, ignore_existing=False)
+        except OSError:
+            exp = sys.exc_info()[1]
+            if exp.errno == errno.EEXIST:
+                raise ContainerAlreadyExistsError(
+                    value='Container with this name already exists. The name '
+                          'must be unique among all the containers in the '
+                          'system',
+                    container_name=container_name, driver=self)
+            else:
+                raise LibcloudError(
+                    'Error creating container %s' % container_name,
+                    driver=self)
+        except Exception:
+            raise LibcloudError(
+                'Error creating container %s' % container_name, driver=self)
+
+        return self._make_container(container_name)
+
+    def delete_container(self, container):
+        """
+        Delete a container.
+
+        @type container: L{Container}
+        @param container: Container instance
+
+        @return: True on success, False otherwise.
+        @rtype: C{bool}
+        """
+
+        # Check if there are any objects inside this
+        for obj in self._get_objects(container):
+            raise ContainerIsNotEmptyError(value='Container is not empty',
+                                container_name=container.name, driver=self)
+
+        path = self.get_container_cdn_url(container, check=True)
+
+        with LockLocalStorage(path) as lock:
+            try:
+                shutil.rmtree(path)
+            except Exception:
+                return False
+
+        return True

Modified: libcloud/trunk/libcloud/storage/providers.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/providers.py?rev=1406447&r1=1406446&r2=1406447&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/providers.py (original)
+++ libcloud/trunk/libcloud/storage/providers.py Wed Nov  7 04:11:06 2012
@@ -43,7 +43,9 @@ DRIVERS = {
         ('libcloud.storage.drivers.cloudfiles',
          'CloudFilesSwiftStorageDriver'),
     Provider.NIMBUS:
-        ('libcloud.storage.drivers.nimbus', 'NimbusStorageDriver')
+        ('libcloud.storage.drivers.nimbus', 'NimbusStorageDriver'),
+    Provider.LOCAL:
+        ('libcloud.storage.drivers.local', 'LocalStorageDriver')
 }
 
 

Modified: libcloud/trunk/libcloud/storage/types.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/types.py?rev=1406447&r1=1406446&r2=1406447&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/types.py (original)
+++ libcloud/trunk/libcloud/storage/types.py Wed Nov  7 04:11:06 2012
@@ -42,6 +42,7 @@ class Provider(object):
     @cvar GOOGLE_STORAGE Google Storage
     @cvar S3_US_WEST_OREGON: Amazon S3 US West 2 (Oregon)
     @cvar NIMBUS: Nimbus.io driver
+    @cvar LOCAL: Local storage driver
     """
     DUMMY = 0
     CLOUDFILES_US = 1
@@ -56,6 +57,7 @@ class Provider(object):
     S3_US_WEST_OREGON = 10
     CLOUDFILES_SWIFT = 11
     NIMBUS = 12
+    LOCAL = 13
 
 
 class ContainerError(LibcloudError):

Added: libcloud/trunk/libcloud/test/storage/test_local.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/storage/test_local.py?rev=1406447&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/storage/test_local.py (added)
+++ libcloud/trunk/libcloud/test/storage/test_local.py Wed Nov  7 04:11:06 2012
@@ -0,0 +1,314 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+import shutil
+import unittest
+import tempfile
+
+from libcloud.utils.py3 import httplib
+
+from libcloud.common.types import InvalidCredsError
+from libcloud.common.types import LibcloudError
+from libcloud.storage.base import Container, Object
+from libcloud.storage.types import ContainerDoesNotExistError
+from libcloud.storage.types import ContainerAlreadyExistsError
+from libcloud.storage.types import ContainerIsNotEmptyError
+from libcloud.storage.types import InvalidContainerNameError
+from libcloud.storage.types import ObjectDoesNotExistError
+from libcloud.storage.types import ObjectHashMismatchError
+from libcloud.storage.drivers.local import LocalStorageDriver
+from libcloud.storage.drivers.dummy import DummyIterator
+
+
+class LocalTests(unittest.TestCase):
+    driver_type = LocalStorageDriver
+
+    @classmethod
+    def create_driver(self):
+        self.key = tempfile.mkdtemp()
+        return self.driver_type((self.key, None))
+
+    def setUp(self):
+        self.driver = self.create_driver()
+
+    def tearDown(self):
+        shutil.rmtree(self.key)
+        self.key = None
+
+    def make_tmp_file(self):
+        tmppath = tempfile.mktemp()
+        tmpfile = open(tmppath, 'w')
+        tmpfile.write('blah' * 1024)
+        tmpfile.close()
+        return tmppath
+
+    def remove_tmp_file(self, tmppath):
+        os.unlink(tmppath)
+
+    def test_list_containers_empty(self):
+        containers = self.driver.list_containers()
+        self.assertEqual(len(containers), 0)
+
+    def test_containers_success(self):
+        self.driver.create_container('test1')
+        self.driver.create_container('test2')
+        containers = self.driver.list_containers()
+        self.assertEqual(len(containers), 2)
+
+        container = containers[1]
+
+        self.assertTrue('creation_time' in container.extra)
+        self.assertTrue('modify_time' in container.extra)
+        self.assertTrue('access_time' in container.extra)
+
+        objects = self.driver.list_container_objects(container=container)
+        self.assertEqual(len(objects), 0)
+
+        objects = container.list_objects()
+        self.assertEqual(len(objects), 0)
+
+        for container in containers:
+            self.driver.delete_container(container)
+
+    def test_objects_success(self):
+        tmppath = self.make_tmp_file()
+        tmpfile = open(tmppath)
+
+        container = self.driver.create_container('test3')
+        obj1 = container.upload_object(tmppath, 'object1')
+        obj2 = container.upload_object(tmppath, 'path/object2')
+        obj3 = container.upload_object(tmppath, 'path/to/object3')
+        obj4 = container.upload_object(tmppath, 'path/to/object4.ext')
+        obj5 = container.upload_object_via_stream(tmpfile, 'object5')
+
+        objects = self.driver.list_container_objects(container=container)
+        self.assertEqual(len(objects), 5)
+
+        for obj in objects:
+            self.assertEqual(obj.hash, None)
+            self.assertEqual(obj.size, 4096)
+            self.assertEqual(obj.container.name, 'test3')
+            self.assertTrue('creation_time' in obj.extra)
+            self.assertTrue('modify_time' in obj.extra)
+            self.assertTrue('access_time' in obj.extra)
+
+        obj1.delete()
+        obj2.delete()
+
+        objects = container.list_objects()
+        self.assertEqual(len(objects), 3)
+
+        container.delete_object(obj3)
+        container.delete_object(obj4)
+        container.delete_object(obj5)
+
+        objects = container.list_objects()
+        self.assertEqual(len(objects), 0)
+
+        container.delete()
+        tmpfile.close()
+        self.remove_tmp_file(tmppath)
+
+    def test_get_container_doesnt_exist(self):
+        try:
+            self.driver.get_container(container_name='container1')
+        except ContainerDoesNotExistError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+    def test_get_container_success(self):
+        self.driver.create_container('test4')
+        container = self.driver.get_container(container_name='test4')
+        self.assertTrue(container.name, 'test4')
+        container.delete()
+
+    def test_get_object_container_doesnt_exist(self):
+        try:
+            self.driver.get_object(container_name='test-inexistent',
+                                   object_name='test')
+        except ContainerDoesNotExistError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+    def test_get_object_success(self):
+        tmppath = self.make_tmp_file()
+        container = self.driver.create_container('test5')
+        container.upload_object(tmppath, 'test')
+
+        obj = self.driver.get_object(container_name='test5',
+                                     object_name='test')
+
+        self.assertEqual(obj.name, 'test')
+        self.assertEqual(obj.container.name, 'test5')
+        self.assertEqual(obj.size, 4096)
+        self.assertEqual(obj.hash, None)
+        self.assertTrue('creation_time' in obj.extra)
+        self.assertTrue('modify_time' in obj.extra)
+        self.assertTrue('access_time' in obj.extra)
+
+        obj.delete()
+        container.delete()
+        self.remove_tmp_file(tmppath)
+
+    def test_create_container_invalid_name(self):
+        try:
+            self.driver.create_container(container_name='new/container')
+        except InvalidContainerNameError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+    def test_create_container_already_exists(self):
+        container = self.driver.create_container(
+            container_name='new-container')
+        try:
+            self.driver.create_container(container_name='new-container')
+        except ContainerAlreadyExistsError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+        # success
+        self.driver.delete_container(container)
+
+    def test_create_container_success(self):
+        name = 'new_container'
+        container = self.driver.create_container(container_name=name)
+        self.assertEqual(container.name, name)
+        self.driver.delete_container(container)
+
+    def test_delete_container_doesnt_exist(self):
+        container = Container(name='new_container', extra=None,
+                              driver=self.driver)
+        try:
+            self.driver.delete_container(container=container)
+        except ContainerDoesNotExistError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+    def test_delete_container_not_empty(self):
+        tmppath = self.make_tmp_file()
+        container = self.driver.create_container('test6')
+        obj = container.upload_object(tmppath, 'test')
+
+        try:
+            self.driver.delete_container(container=container)
+        except ContainerIsNotEmptyError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+        # success
+        obj.delete()
+        self.remove_tmp_file(tmppath)
+        self.assertTrue(self.driver.delete_container(container=container))
+
+    def test_delete_container_not_found(self):
+        container = Container(name='foo_bar_container', extra={},
+                              driver=self.driver)
+        try:
+            self.driver.delete_container(container=container)
+        except ContainerDoesNotExistError:
+            pass
+        else:
+            self.fail('Container does not exist but an exception was not' +
+                      'thrown')
+
+    def test_delete_container_success(self):
+        container = self.driver.create_container('test7')
+        self.assertTrue(self.driver.delete_container(container=container))
+
+    def test_download_object_success(self):
+        tmppath = self.make_tmp_file()
+        container = self.driver.create_container('test6')
+        obj = container.upload_object(tmppath, 'test')
+
+        destination_path = tmppath + '.temp'
+        result = self.driver.download_object(obj=obj,
+                                             destination_path=destination_path,
+                                             overwrite_existing=False,
+                                             delete_on_failure=True)
+
+        self.assertTrue(result)
+
+        obj.delete()
+        container.delete()
+        self.remove_tmp_file(tmppath)
+        os.unlink(destination_path)
+
+    def test_download_object_and_overwrite(self):
+        tmppath = self.make_tmp_file()
+        container = self.driver.create_container('test6')
+        obj = container.upload_object(tmppath, 'test')
+
+        destination_path = tmppath + '.temp'
+        result = self.driver.download_object(obj=obj,
+                                             destination_path=destination_path,
+                                             overwrite_existing=False,
+                                             delete_on_failure=True)
+
+        self.assertTrue(result)
+
+        try:
+            self.driver.download_object(obj=obj,
+                                        destination_path=destination_path,
+                                        overwrite_existing=False,
+                                        delete_on_failure=True)
+        except LibcloudError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+        result = self.driver.download_object(obj=obj,
+                                             destination_path=destination_path,
+                                             overwrite_existing=True,
+                                             delete_on_failure=True)
+
+        self.assertTrue(result)
+
+        # success
+        obj.delete()
+        container.delete()
+        self.remove_tmp_file(tmppath)
+        os.unlink(destination_path)
+
+    def test_download_object_as_stream_success(self):
+        tmppath = self.make_tmp_file()
+        container = self.driver.create_container('test6')
+        obj = container.upload_object(tmppath, 'test')
+
+        stream = self.driver.download_object_as_stream(obj=obj,
+                                                       chunk_size=1024)
+
+        self.assertTrue(hasattr(stream, '__iter__'))
+
+        data = ''
+        for buff in stream:
+            data += buff
+
+        self.assertTrue(len(data), 4096)
+
+        obj.delete()
+        container.delete()
+        self.remove_tmp_file(tmppath)
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

Modified: libcloud/trunk/setup.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/setup.py?rev=1406447&r1=1406446&r2=1406447&view=diff
==============================================================================
--- libcloud/trunk/setup.py (original)
+++ libcloud/trunk/setup.py Wed Nov  7 04:11:06 2012
@@ -130,6 +130,12 @@ class TestCommand(Command):
         testfiles = []
         for test_path in TEST_PATHS:
             for t in glob(pjoin(self._dir, test_path, 'test_*.py')):
+                if sys.version_info >= (3, 2) and sys.version_info < (3, 3) \
+                   and t.find('test_local'):
+                    # Lockfile doesn't work with 3.2, temporary disable
+                    # local_storage test with 3.2 until fixes have been
+                    # submitted upstream
+                    continue
                 testfiles.append('.'.join(
                     [test_path.replace('/', '.'), splitext(basename(t))[0]]))
 

Modified: libcloud/trunk/tox.ini
URL: http://svn.apache.org/viewvc/libcloud/trunk/tox.ini?rev=1406447&r1=1406446&r2=1406447&view=diff
==============================================================================
--- libcloud/trunk/tox.ini (original)
+++ libcloud/trunk/tox.ini Wed Nov  7 04:11:06 2012
@@ -1,17 +1,22 @@
 [tox]
+
 envlist = py25,py26,py27,pypy,py32,py33
 
 [testenv]
 deps = mock
+       lockfile
 commands = python setup.py test
 
 [testenv:py25]
 deps = mock
+       lockfile
        ssl
        simplejson
 
 [testenv:py32]
 deps = mock
+       lockfile
 
 [testenv:py33]
 deps = mock
+       lockfile



Mime
View raw message