libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1079029 [6/13] - in /incubator/libcloud/trunk: ./ demos/ dist/ libcloud/ libcloud/common/ libcloud/compute/ libcloud/compute/drivers/ libcloud/drivers/ libcloud/storage/ libcloud/storage/drivers/ test/ test/compute/ test/compute/fixtures/ ...
Date Mon, 07 Mar 2011 23:44:12 GMT
Added: incubator/libcloud/trunk/libcloud/drivers/rimuhosting.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/rimuhosting.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/rimuhosting.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/rimuhosting.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.rimuhosting import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/drivers/slicehost.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/slicehost.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/slicehost.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/slicehost.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.slicehost import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/drivers/softlayer.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/softlayer.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/softlayer.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/softlayer.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.softlayer import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/drivers/vcloud.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/vcloud.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/vcloud.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/vcloud.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.vcloud import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/drivers/voxel.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/voxel.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/voxel.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/voxel.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.voxel import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/drivers/vpsnet.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/vpsnet.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/vpsnet.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/vpsnet.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.drivers.vpsnet import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/httplib_ssl.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/httplib_ssl.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/httplib_ssl.py (added)
+++ incubator/libcloud/trunk/libcloud/httplib_ssl.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,157 @@
+# 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.
+"""
+Subclass for httplib.HTTPSConnection with optional certificate name
+verification, depending on libcloud.security settings.
+"""
+import httplib
+import os
+import re
+import socket
+import ssl
+import warnings
+
+import libcloud.security
+
+class LibcloudHTTPSConnection(httplib.HTTPSConnection):
+    """LibcloudHTTPSConnection
+
+    Subclass of HTTPSConnection which verifies certificate names
+    if and only if CA certificates are available.
+    """
+    verify = False        # does not verify
+    ca_cert = None        # no default CA Certificate
+
+    def __init__(self, *args, **kwargs):
+        """Constructor
+        """
+        self._setup_verify()
+        httplib.HTTPSConnection.__init__(self, *args, **kwargs)
+
+    def _setup_verify(self):
+        """Setup Verify SSL or not
+
+        Reads security module's VERIFY_SSL_CERT and toggles whether
+        the class overrides the connect() class method or runs the
+        inherited httplib.HTTPSConnection connect()
+        """
+        self.verify = libcloud.security.VERIFY_SSL_CERT
+
+        if self.verify:
+            self._setup_ca_cert()
+        else:
+            warnings.warn(libcloud.security.VERIFY_SSL_DISABLED_MSG)
+
+    def _setup_ca_cert(self):
+        """Setup CA Certs
+
+        Search in CA_CERTS_PATH for valid candidates and
+        return first match.  Otherwise, complain about certs
+        not being available.
+        """
+        if not self.verify:
+            return
+
+        ca_certs_available = [cert
+                              for cert in libcloud.security.CA_CERTS_PATH
+                              if os.path.exists(cert)]
+        if ca_certs_available:
+            # use first available certificate
+            self.ca_cert = ca_certs_available[0]
+        else:
+            # no certificates found; toggle verify to False
+            warnings.warn(libcloud.security.CA_CERTS_UNAVAILABLE_MSG)
+            self.ca_cert = None
+            self.verify = False
+
+    def connect(self):
+        """Connect
+
+        Checks if verification is toggled; if not, just call
+        httplib.HTTPSConnection's connect
+        """
+        if not self.verify:
+            return httplib.HTTPSConnection.connect(self)
+
+        # otherwise, create a connection and verify the hostname
+        # use socket.create_connection (in 2.6+) if possible
+        if getattr(socket, 'create_connection', None):
+            sock = socket.create_connection((self.host, self.port),
+                                            self.timeout)
+        else:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.connect((self.host, self.port))
+        self.sock = ssl.wrap_socket(sock,
+                                    self.key_file,
+                                    self.cert_file,
+                                    cert_reqs=ssl.CERT_REQUIRED,
+                                    ca_certs=self.ca_cert,
+                                    ssl_version=ssl.PROTOCOL_TLSv1)
+        cert = self.sock.getpeercert()
+        if not self._verify_hostname(self.host, cert):
+            raise ssl.SSLError('Failed to verify hostname')
+
+    def _verify_hostname(self, hostname, cert):
+        """Verify hostname against peer cert
+
+        Check both commonName and entries in subjectAltName, using a
+        rudimentary glob to dns regex check to find matches
+        """
+        common_name = self._get_common_name(cert)
+        alt_names = self._get_subject_alt_names(cert)
+
+        # replace * with alphanumeric and dash
+        # replace . with literal .
+        valid_patterns = [
+            re.compile(
+                pattern.replace(
+                    r".", r"\."
+                ).replace(
+                    r"*", r"[0-9A-Za-z]+"
+                )
+            )
+            for pattern
+            in (set(common_name) | set(alt_names))
+        ]
+
+        return any(
+            pattern.search(hostname)
+            for pattern in valid_patterns
+        )
+
+    def _get_subject_alt_names(self, cert):
+        """Get SubjectAltNames
+
+        Retrieve 'subjectAltName' attributes from cert data structure
+        """
+        if 'subjectAltName' not in cert:
+            values = []
+        else:
+            values = [value
+                      for field, value in cert['subjectAltName']
+                      if field == 'DNS']
+        return values
+
+    def _get_common_name(self, cert):
+        """Get Common Name
+
+        Retrieve 'commonName' attribute from cert data structure
+        """
+        if 'subject' not in cert:
+            return None
+        values = [value[0][1]
+                  for value in cert['subject']
+                  if value[0][0] == 'commonName']
+        return values

Added: incubator/libcloud/trunk/libcloud/providers.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/providers.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/providers.py (added)
+++ incubator/libcloud/trunk/libcloud/providers.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.providers import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/security.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/security.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/security.py (added)
+++ incubator/libcloud/trunk/libcloud/security.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,54 @@
+# 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.
+"""
+Security (SSL) Settings
+
+Usage:
+    import libcloud.security
+    libcloud.security.VERIFY_SSL_CERT = True
+
+    # optional
+    libcloud.security.CA_CERTS_PATH.append("/path/to/cacert.txt")
+"""
+# For backward compatibility this option is disabled by default
+VERIFY_SSL_CERT = False
+
+# File containing one or more PEM-encoded CA certificates
+# concatenated together
+CA_CERTS_PATH = [
+    # centos/fedora: openssl
+    '/etc/pki/tls/certs/ca-bundle.crt',
+
+    # debian/ubuntu/arch/gentoo: ca-certificates
+    '/etc/ssl/certs/ca-certificates.crt',
+
+    # freebsd: ca_root_nss
+    '/usr/local/share/certs/ca-root-nss.crt',
+
+    # macports: curl-ca-bundle
+    '/opt/local/share/curl/curl-ca-bundle.crt',
+]
+
+CA_CERTS_UNAVAILABLE_MSG = (
+   'Warning: No CA Certificates were found in CA_CERTS_PATH. '
+   'Toggling VERIFY_SSL_CERT to False.'
+)
+
+VERIFY_SSL_DISABLED_MSG = (
+    'SSL certificate verification is disabled, this can pose a '
+    'security risk. For more information how to enable the SSL '
+    'certificate verification, please visit the libcloud '
+    'documentation.'
+)

Added: incubator/libcloud/trunk/libcloud/ssh.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/ssh.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/ssh.py (added)
+++ incubator/libcloud/trunk/libcloud/ssh.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,19 @@
+# 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.
+
+from libcloud.utils import deprecated_warning
+from libcloud.compute.ssh import *
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/storage/__init__.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/__init__.py?rev=1079029&view=auto
==============================================================================
    (empty)

Added: incubator/libcloud/trunk/libcloud/storage/base.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/base.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/base.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/base.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,503 @@
+# 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.
+
+# Backward compatibility for Python 2.5
+from __future__ import with_statement
+
+import os
+import os.path
+import hashlib
+from os.path import join as pjoin
+
+from libcloud import utils
+from libcloud.common.types import LibcloudError
+from libcloud.common.base import ConnectionKey
+
+CHUNK_SIZE = 8096
+
+class Object(object):
+    """
+    Represents an object (BLOB).
+    """
+
+    def __init__(self, name, size, hash, extra, meta_data, container,
+                 driver):
+        """
+        @type name: C{str}
+        @param name: Object name (must be unique per container).
+
+        @type size: C{int}
+        @param size: Object size in bytes.
+
+        @type hash: C{string}
+        @param hash Object hash.
+
+        @type container: C{Container}
+        @param container: Object container.
+
+        @type extra: C{dict}
+        @param extra: Extra attributes.
+
+        @type meta_data: C{dict}
+        @param meta_data: Optional object meta data.
+
+        @type driver: C{StorageDriver}
+        @param driver: StorageDriver instance.
+        """
+
+        self.name = name
+        self.size = size
+        self.hash = hash
+        self.container = container
+        self.extra = extra or {}
+        self.meta_data = meta_data or {}
+        self.driver = driver
+
+    def download(self, destination_path, overwrite_existing=False,
+                 delete_on_failure=True):
+        return self.driver.download_object(self, destination_path,
+                                           overwrite_existing,
+                                           delete_on_failure)
+
+    def as_stream(self, chunk_size=None):
+        return self.driver.object_as_stream(self, chunk_size)
+
+    def delete(self):
+        return self.driver.delete_object(self)
+
+    def __repr__(self):
+        return '<Object: name=%s, size=%s, hash=%s, provider=%s ...>' % \
+        (self.name, self.size, self.hash, self.driver.name)
+
+class Container(object):
+    """
+    Represents a container (bucket) which can hold multiple objects.
+    """
+
+    def __init__(self, name, extra, driver):
+        """
+        @type name: C{str}
+        @param name: Container name (must be unique).
+
+        @type extra: C{dict}
+        @param extra: Extra attributes.
+
+        @type driver: C{StorageDriver}
+        @param driver: StorageDriver instance.
+        """
+
+        self.name = name
+        self.extra = extra or {}
+        self.driver = driver
+
+    def list_objects(self):
+        return self.driver.list_container_objects(self)
+
+    def get_object(self, object_name):
+        return self.driver.get_object(container_name=self.name,
+                                      object_name=object_name)
+
+    def upload_object(self, file_path, object_name, extra=None, file_hash=None):
+        return self.driver.upload_object(file_path, self, object_name, extra, file_hash)
+
+    def stream_object_data(self, iterator, object_name, extra=None):
+        return self.driver.stream_object_data(iterator, self, object_name, extra)
+
+    def download_object(self, obj, destination_path, overwrite_existing=False,
+                        delete_on_failure=True):
+        return self.driver.download_object(obj, destination_path)
+
+    def object_as_stream(self, obj, chunk_size=None):
+        return self.driver.object_as_stream(obj, chunk_size)
+
+    def delete_object(self, obj):
+        return self.driver.delete_object(obj)
+
+    def delete(self):
+        return self.driver.delete_container(self)
+
+    def __repr__(self):
+        return '<Container: name=%s, provider=%s>' % (self.name, self.driver.name)
+
+class StorageDriver(object):
+    """
+    A base StorageDriver to derive from.
+    """
+
+    connectionCls = ConnectionKey
+    name = None
+    hash_type = 'md5'
+
+    def __init__(self, key, secret=None, secure=True, host=None, port=None):
+        self.key = key
+        self.secret = secret
+        self.secure = secure
+        args = [self.key]
+
+        if self.secret != None:
+            args.append(self.secret)
+
+        args.append(secure)
+
+        if host != None:
+            args.append(host)
+
+        if port != None:
+            args.append(port)
+
+        self.connection = self.connectionCls(*args)
+
+        self.connection.driver = self
+        self.connection.connect()
+
+    def get_meta_data(self):
+        """
+        Return account meta data - total number of containers, objects and
+        number of bytes currently used.
+
+        @return A C{dict} with account meta data.
+        """
+        raise NotImplementedError, \
+            'get_account_meta_data not implemented for this driver'
+
+    def list_containters(self):
+        raise NotImplementedError, \
+            'list_containers not implemented for this driver'
+
+    def list_container_objects(self, container):
+        """
+        Return a list of objects for the given container.
+
+        @type container: C{Container}
+        @param container: Container instance
+
+        @return A list of Object instances.
+        """
+        raise NotImplementedError, \
+            'list_objects not implemented for this driver'
+
+    def get_container(self, container_name):
+        """
+        Return a container instance.
+
+        @type container_name: C{str}
+        @param container_name: Container name.
+
+        @return: C{Container} instance.
+        """
+        raise NotImplementedError, \
+            'get_object not implemented for this driver'
+
+    def get_object(self, container_name, object_name):
+        """
+        Return an object instance.
+
+        @type container_name: C{str}
+        @param container_name: Container name.
+
+        @type object_name: C{str}
+        @param object_name: Object name.
+
+        @return: C{Object} instance.
+        """
+        raise NotImplementedError, \
+            'get_object not implemented for this driver'
+
+    def download_object(self, obj, destination_path, delete_on_failure=True):
+        """
+        Download an object to the specified destination path.
+
+        @type obj; C{Object}
+        @param obj: Object instance.
+
+        @type destination_path: C{str}
+        @type destination_path: Full path to a file or a directory where the
+                                incoming file will be saved.
+
+        @type overwrite_existing: C{bool}
+        @type overwrite_existing: True to overwrite an existing file.
+
+        @type delete_on_failure: C{bool}
+        @param delete_on_failure: True to delete a partially downloaded file if
+        the download was not successful (hash mismatch / file size).
+
+        @return C{bool} True if an object has been successfully downloaded, False
+        otherwise.
+        """
+        raise NotImplementedError, \
+            'download_object not implemented for this driver'
+
+    def object_as_stream(self, obj, chunk_size=None):
+        """
+        Return a generator which yields object data.
+
+        @type obj: C{Object}
+        @param obj: Object instance
+
+        @type chunk_size: C{int}
+        @param chunk_size: Optional chunk size (in bytes).
+        """
+        raise NotImplementedError, \
+            'object_as_stream not implemented for this driver'
+
+    def upload_object(self, file_path, container, object_name, extra=None,
+                      file_hash=None):
+        """
+        Upload an object.
+
+        @type file_path: C{str}
+        @param file_path: Path to the object on disk.
+
+        @type container: C{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).
+
+        @type file_hash: C{str}
+        @param file_hash: (optional) File hash. If provided object hash is
+                          on upload and if it doesn't match the one provided an
+                          exception is thrown.
+        """
+        raise NotImplementedError, \
+            'upload_object not implemented for this driver'
+
+    def stream_object_data(self, iterator, container, object_name, extra=None):
+        """
+        @type iterator: C{object}
+        @param iterator: An object which implements the iterator interface.
+
+        @type container: C{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).
+        """
+        raise NotImplementedError, \
+            'stream_object_data not implemented for this driver'
+
+    def delete_object(self, obj):
+        """
+        Delete an object.
+
+        @type obj: C{Object}
+        @param obj: Object instance.
+
+        @return: C{bool} True on success.
+        """
+        raise NotImplementedError, \
+            'delete_object not implemented for this driver'
+
+    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.
+        """
+        raise NotImplementedError, \
+            'create_container not implemented for this driver'
+
+    def delete_container(self, container):
+        """
+        Delete a container.
+
+        @type container: C{Container}
+        @param container: Container instance
+
+        @return C{bool} True on success, False otherwise.
+        """
+        raise NotImplementedError, \
+            'delete_container not implemented for this driver'
+
+    def _save_object(self, response, obj, destination_path,
+                     overwrite_existing=False, delete_on_failure=True,
+                     chunk_size=None):
+        """
+        Save object to the provided path.
+
+        @type response: C{RawResponse}
+        @param response: RawResponse instance.
+
+        @type obj: C{Object}
+        @param obj: Object instance.
+
+        @type destination_path: C{Str}
+        @param destination_path: Destination directory.
+
+        @type delete_on_failure: C{bool}
+        @param delete_on_failure: True to delete partially downloaded object if
+                                  the download fails.
+        @type overwrite_existing: C{bool}
+        @param overwrite_existing: True to overwrite a local path if it already
+                                   exists.
+
+        @type chunk_size: C{int}
+        @param chunk_size: Optional chunk size (defaults to CHUNK_SIZE)
+
+        @return C{bool} True on success, False otherwise.
+        """
+
+        chunk_size = chunk_size or CHUNK_SIZE
+
+        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 = pjoin(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)
+
+        stream = utils.read_in_chunks(response, chunk_size)
+
+        try:
+            data_read = stream.next()
+        except StopIteration:
+            # Empty response?
+            return False
+
+        bytes_transferred = 0
+
+        with open(file_path, 'wb') as file_handle:
+            while len(data_read) > 0:
+                file_handle.write(data_read)
+                bytes_transferred += len(data_read)
+
+                try:
+                    data_read = stream.next()
+                except StopIteration:
+                    data_read = ''
+
+        if obj.size != bytes_transferred:
+            # Transfer failed, support retry?
+            if delete_on_failure:
+                try:
+                    os.unlink(file_path)
+                except Exception:
+                    pass
+
+            return False
+
+        return True
+
+    def _stream_data(self, response, iterator, chunked=False,
+                     calculate_hash=True, chunk_size=None):
+        """
+        Stream a data over an http connection.
+
+        @type response: C{RawResponse}
+        @param response: RawResponse object.
+
+        @type iterator: C{}
+        @param response: An object which implements an iterator interface
+                         or a File like object with read method.
+
+        @type chunk_size: C{int}
+        @param chunk_size: Optional chunk size (defaults to CHUNK_SIZE)
+
+        @return C{tuple} First item is a boolean indicator of success, second
+                         one is the uploaded data MD5 hash and the third one
+                         is the number of transferred bytes.
+        """
+
+        chunk_size = chunk_size or CHUNK_SIZE
+
+        data_hash = None
+        if calculate_hash:
+            data_hash = hashlib.md5()
+
+        generator = utils.read_in_chunks(iterator, chunk_size)
+
+        bytes_transferred = 0
+        try:
+            chunk = generator.next()
+        except StopIteration:
+            # No data?
+            return False, None, None
+
+        while len(chunk) > 0:
+            if calculate_hash:
+                try:
+                    if chunked:
+                        response.connection.connection.send('%X\r\n' %
+                                                           (len(chunk)))
+                        response.connection.connection.send(chunk)
+                        response.connection.connection.send('\r\n')
+                    else:
+                        response.connection.connection.send(chunk)
+                except Exception, e:
+                    # Timeout, etc.
+                    return False, None, bytes_transferred
+
+                bytes_transferred += len(chunk)
+                if calculate_hash:
+                    data_hash.update(chunk)
+
+                try:
+                    chunk = generator.next()
+                except StopIteration:
+                    chunk = ''
+
+        if chunked:
+                response.connection.connection.send('0\r\n\r\n')
+
+        if calculate_hash:
+            data_hash = data_hash.hexdigest()
+
+        return True, data_hash, bytes_transferred
+
+    def _upload_file(self, response, file_path, chunked=False,
+                     calculate_hash=True):
+        """
+        Upload a file to the server.
+
+        @type response: C{RawResponse}
+        @param response: RawResponse object.
+
+        @type file_path: C{str}
+        @param file_path: Path to a local file.
+
+        @type iterator: C{}
+        @param response: An object which implements an iterator interface (File
+                         object, etc.)
+
+        @return C{tuple} First item is a boolean indicator of success, second
+                         one is the uploaded data MD5 hash and the third one
+                         is the number of transferred bytes.
+        """
+        with open (file_path, 'rb') as file_handle:
+            success, data_hash, bytes_transferred = \
+                     self._stream_data(response=response,
+                                       iterator=iter(file_handle),
+                                       chunked=chunked,
+                                       calculate_hash=calculate_hash)
+
+        return success, data_hash, bytes_transferred

Added: incubator/libcloud/trunk/libcloud/storage/drivers/__init__.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/drivers/__init__.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/drivers/__init__.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/drivers/__init__.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,23 @@
+# 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.
+
+"""
+Drivers for working with different providers
+"""
+
+__all__ = [
+    'dummy',
+    'cloudfiles'
+]

Added: incubator/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,523 @@
+# 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 httplib
+import urlparse
+import os.path
+import urllib
+
+try:
+    import json
+except:
+    import simplejson as json
+
+from libcloud import utils
+from libcloud.common.types import MalformedResponseError, LibcloudError
+from libcloud.common.types import InvalidCredsError
+from libcloud.common.base import ConnectionUserAndKey, Response
+
+from libcloud.storage.providers import Provider
+from libcloud.storage.base import Object, Container, StorageDriver
+from libcloud.storage.types import ContainerAlreadyExistsError
+from libcloud.storage.types import ContainerDoesNotExistError
+from libcloud.storage.types import ContainerIsNotEmptyError
+from libcloud.storage.types import ObjectDoesNotExistError
+from libcloud.storage.types import ObjectHashMismatchError
+from libcloud.storage.types import InvalidContainerNameError
+
+AUTH_HOST_US = 'auth.api.rackspacecloud.com'
+AUTH_HOST_UK = 'lon.auth.api.rackspacecloud.com'
+API_VERSION = 'v1.0'
+
+class CloudFilesResponse(Response):
+
+    valid_response_codes = [ httplib.NOT_FOUND, httplib.CONFLICT ]
+
+    def success(self):
+        i = int(self.status)
+        return i >= 200 and i <= 299 or i in self.valid_response_codes
+
+    def parse_body(self):
+        if not self.body:
+            return None
+
+        if 'content-type' in self.headers:
+            key = 'content-type'
+        elif 'Content-Type' in self.headers:
+            key = 'Content-Type'
+        else:
+            raise LibcloudError('Missing content-type header')
+
+        content_type = self.headers[key]
+        if content_type.find(';') != -1:
+            content_type = content_type.split(';')[0]
+
+        if content_type == 'application/json':
+            try:
+                data = json.loads(self.body)
+            except:
+                raise MalformedResponseError('Failed to parse JSON',
+                                             body=self.body,
+                                             driver=CloudFilesStorageDriver)
+        elif content_type == 'text/plain':
+            data = self.body
+        else:
+            data = self.body
+
+        return data
+
+
+class CloudFilesConnection(ConnectionUserAndKey):
+    """
+    Base connection class for the Cloudfiles driver.
+    """
+
+    auth_host = None
+    api_version = API_VERSION
+    responseCls = CloudFilesResponse
+
+    def __init__(self, user_id, key, secure=True):
+        self.cdn_management_url = None
+        self.storage_url = None
+        self.auth_token = None
+        self.request_path = None
+
+        self.__host = None
+        super(CloudFilesConnection, self).__init__(user_id, key, secure)
+
+    def add_default_headers(self, headers):
+        headers['X-Auth-Token'] = self.auth_token
+        headers['Accept'] = 'application/json'
+        return headers
+
+    @property
+    def host(self):
+        """
+        Rackspace uses a separate host for API calls which is only provided
+        after an initial authentication request. If we haven't made that
+        request yet, do it here. Otherwise, just return the management host.
+        """
+        if not self.__host:
+            # Initial connection used for authentication
+            conn = self.conn_classes[self.secure](self.auth_host, self.port[self.secure])
+            conn.request(
+                method='GET',
+                url='/%s' % (self.api_version),
+                headers={
+                    'X-Auth-User': self.user_id,
+                    'X-Auth-Key': self.key
+                }
+            )
+
+            resp = conn.getresponse()
+
+            if resp.status != httplib.NO_CONTENT:
+                raise InvalidCredsError()
+
+            headers = dict(resp.getheaders())
+
+            try:
+                self.storage_url = headers['x-storage-url']
+                self.cdn_management_url = headers['x-cdn-management-url']
+                self.auth_token = headers['x-auth-token']
+            except KeyError:
+                raise InvalidCredsError()
+
+            scheme, server, self.request_path, param, query, fragment = (
+                urlparse.urlparse(self.storage_url)
+            )
+
+            if scheme is "https" and self.secure is not True:
+                raise InvalidCredsError()
+
+            # Set host to where we want to make further requests to;
+            self.__host = server
+            conn.close()
+
+        return self.__host
+
+    def request(self, action, params=None, data='', headers=None, method='GET',
+                raw=False):
+        if not headers:
+            headers = {}
+        if not params:
+            params = {}
+        # Due to first-run authentication request, we may not have a path
+        if self.request_path:
+            action = self.request_path + action
+            params['format'] = 'json'
+        if method in [ 'POST', 'PUT' ]:
+            headers = {'Content-Type': 'application/json; charset=UTF-8'}
+
+        return super(CloudFilesConnection, self).request(
+            action=action,
+            params=params, data=data,
+            method=method, headers=headers,
+            raw=raw
+        )
+
+
+class CloudFilesUSConnection(CloudFilesConnection):
+    """
+    Connection class for the Cloudfiles US endpoint.
+    """
+
+    auth_host = AUTH_HOST_US
+
+
+class CloudFilesUKConnection(CloudFilesConnection):
+    """
+    Connection class for the Cloudfiles UK endpoint.
+    """
+
+    auth_host = AUTH_HOST_UK
+
+
+class CloudFilesStorageDriver(StorageDriver):
+    """
+    Base CloudFiles driver.
+
+    You should never create an instance of this class directly but use US/US
+    class.
+    """
+    name = 'CloudFiles'
+    connectionCls = CloudFilesConnection
+    hash_type = 'md5'
+
+    def get_meta_data(self):
+        response = self.connection.request('', method='HEAD')
+
+        if response.status == httplib.NO_CONTENT:
+            container_count = response.headers.get('x-account-container-count', 'unknown')
+            object_count = response.headers.get('x-account-object-count', 'unknown')
+            bytes_used = response.headers.get('x-account-bytes-used', 'unknown')
+
+            return { 'container_count': int(container_count),
+                      'object_count': int(object_count),
+                      'bytes_used': int(bytes_used) }
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def list_containers(self):
+        response = self.connection.request('')
+
+        if response.status == httplib.NO_CONTENT:
+            return []
+        elif response.status == httplib.OK:
+            return self._to_container_list(json.loads(response.body))
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def list_container_objects(self, container):
+        response = self.connection.request('/%s' % (container.name))
+
+        if response.status == httplib.NO_CONTENT:
+            # Empty or inexistent container
+            return []
+        elif response.status == httplib.OK:
+            return self._to_object_list(json.loads(response.body), container)
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def get_container(self, container_name):
+        response = self.connection.request('/%s' % (container_name),
+                                                    method='HEAD')
+
+        if response.status == httplib.NO_CONTENT:
+            container = self._headers_to_container(container_name, response.headers)
+            return container
+        elif response.status == httplib.NOT_FOUND:
+            raise ContainerDoesNotExistError(None, self, container_name)
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def get_object(self, container_name, object_name):
+        container = self.get_container(container_name)
+        response = self.connection.request('/%s/%s' % (container_name,
+                                                       object_name),
+                                                       method='HEAD')
+
+        if response.status in [ httplib.OK, httplib.NO_CONTENT ]:
+            obj = self._headers_to_object(object_name, container, response.headers)
+            return obj
+        elif response.status == httplib.NOT_FOUND:
+            raise ObjectDoesNotExistError(None, self, object_name)
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def create_container(self, container_name):
+        container_name = self._clean_container_name(container_name)
+        response = self.connection.request('/%s' % (container_name), method='PUT')
+
+        if response.status == httplib.CREATED:
+            # Accepted mean that container is not yet created but it will be
+            # eventually
+            extra = { 'object_count': 0 }
+            container = Container(name=container_name, extra=extra, driver=self)
+
+            return container
+        elif response.status == httplib.ACCEPTED:
+            error = ContainerAlreadyExistsError(None, self, container_name)
+            raise error
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def delete_container(self, container):
+        name = self._clean_container_name(container.name)
+
+        # Only empty container can be deleted
+        response = self.connection.request('/%s' % (name), method='DELETE')
+
+        if response.status == httplib.NO_CONTENT:
+            return True
+        elif response.status == httplib.NOT_FOUND:
+            raise ContainerDoesNotExistError(value='',
+                                             container_name=name, driver=self)
+        elif response.status == httplib.CONFLICT:
+            # @TODO: Add "delete_all_objects" parameter?
+            raise ContainerIsNotEmptyError(value='',
+                                           container_name=name, driver=self)
+
+    def download_object(self, obj, destination_path, overwrite_existing=False,
+                        delete_on_failure=True):
+        return self._get_object(obj, self._save_object,
+                                {'obj': obj,
+                                 'destination_path': destination_path,
+                                 'overwrite_existing': overwrite_existing,
+                                 'delete_on_failure': delete_on_failure})
+
+    def object_as_stream(self, obj, chunk_size=None):
+        return self._get_object(obj, self._get_object_as_stream,
+                                {'chunk_size': chunk_size})
+
+    def upload_object(self, file_path, container, object_name, extra=None,
+                      file_hash=None):
+        """
+        Upload an object.
+
+        Note: This will override file with a same name if it already exists.
+        """
+        upload_func = self._upload_file
+        upload_func_args = { 'file_path': file_path }
+
+        return self._put_object(container=container, file_path=file_path,
+                                object_name=object_name, extra=extra,
+                                upload_func=upload_func,
+                                upload_func_args=upload_func_args)
+
+    def stream_object_data(self, iterator, container, object_name, extra=None):
+        if isinstance(iterator, file):
+            iterator = iter(iterator)
+
+        upload_func = self._stream_data
+        upload_func_args = { 'iterator': iterator }
+
+        return self._put_object(container=container, iterator=iterator,
+                                object_name=object_name, extra=extra,
+                                upload_func=upload_func,
+                                upload_func_args=upload_func_args)
+
+    def delete_object(self, obj):
+        container_name = self._clean_container_name(obj.container.name)
+        object_name = self._clean_object_name(obj.name)
+
+        response = self.connection.request('/%s/%s' % (container_name,
+                                                       object_name), method='DELETE')
+
+        if response.status == httplib.NO_CONTENT:
+            return True
+        elif response.status == httplib.NOT_FOUND:
+            raise ObjectDoesNotExistError(value='', object_name=object_name,
+                                          driver=self)
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def _get_object(self, obj, callback, callback_args):
+        container_name = obj.container.name
+        object_name = obj.name
+
+        response = self.connection.request('/%s/%s' % (container_name,
+                                                       object_name),
+                                           raw=True)
+
+        callback_args['response'] = response.response
+
+        if response.status == httplib.OK:
+            return callback(**callback_args)
+        elif response.status == httplib.NOT_FOUND:
+            raise ObjectDoesNotExistError(name=object_name)
+
+        raise LibcloudError('Unexpected status code: %s' % (response.status))
+
+    def _put_object(self, upload_func, upload_func_args, container, object_name,
+                    extra=None, file_path=None, iterator=None, file_hash=None):
+        container_name_cleaned = self._clean_container_name(container.name)
+        object_name_cleaned = self._clean_object_name(object_name)
+
+        extra = extra or {}
+        content_type = extra.get('content_type', None)
+        meta_data = extra.get('meta_data', None)
+
+        if not content_type:
+            if file_path:
+                name = file_path
+            else:
+                name = object_name
+            content_type, _ = utils.guess_file_mime_type(name)
+
+            if not content_type:
+                raise AttributeError('File content-type could not be guessed and' +
+                                     ' no content_type value provided')
+
+        headers = {}
+        if iterator:
+            headers['Transfer-Encoding'] = 'chunked'
+            upload_func_args['chunked'] = True
+        else:
+            file_size = os.path.getsize(file_path)
+            headers['Content-Length'] = file_size
+            upload_func_args['chunked'] = False
+
+            if file_hash:
+                headers['ETag'] = file_hash
+
+        headers['Content-Type'] = content_type
+
+        if meta_data:
+            for key, value in meta_data.iteritems():
+                key = 'X-Object-Meta-%s' % (key)
+                headers[key] = value
+
+        response = self.connection.request('/%s/%s' % (container_name_cleaned,
+                                                       object_name_cleaned),
+                                           method='PUT', data=None,
+                                           headers=headers, raw=True)
+
+        upload_func_args['response'] = response
+        success, data_hash, bytes_transferred = upload_func(**upload_func_args)
+
+        if not success:
+            raise LibcloudError('Object upload failed, Perhaps a timeout?')
+
+        response = response.response
+
+        if response.status == httplib.EXPECTATION_FAILED:
+            raise LibcloudError('Missing content-type header')
+        elif response.status == httplib.UNPROCESSABLE_ENTITY:
+            raise ObjectHashMismatchError(value='MD5 hash checksum does not match',
+                                          object_name=object_name, driver=self)
+        elif response.status == httplib.CREATED:
+            obj = Object(name=object_name, size=bytes_transferred, hash=file_hash,
+                         extra=None, meta_data=meta_data, container=container,
+                         driver=self)
+
+            return obj
+
+    def _clean_container_name(self, name):
+        """
+        Clean container name.
+        """
+        if name.startswith('/'):
+            name = name[1:]
+        name = urllib.quote(name)
+
+        if name.find('/') != -1:
+            raise InvalidContainerNameError(value='Container name cannot'
+                                                  ' contain slashes',
+                                            container_name=name, driver=self)
+
+        if len(name) > 256:
+            raise InvalidContainerNameError(value='Container name cannot be'
+                                                   ' longer than 256 bytes',
+                                            container_name=name, driver=self)
+
+
+        return name
+
+    def _clean_object_name(self, name):
+        name = urllib.quote(name)
+        return name
+
+    def _to_container_list(self, response):
+        # @TODO: Handle more then 10k containers - use "lazy list"?
+        containers = []
+
+        for container in response:
+            extra = { 'object_count': int(container['count']),
+                      'size': int(container['bytes'])}
+            containers.append(Container(name=container['name'], extra=extra,
+                                        driver=self))
+
+        return containers
+
+    def _to_object_list(self, response, container):
+        objects = []
+
+        for obj in response:
+            name = obj['name']
+            size = int(obj['bytes'])
+            hash = obj['hash']
+            extra = { 'content_type': obj['content_type'],
+                      'last_modified': obj['last_modified'] }
+            objects.append(Object(name=name, size=size, hash=hash, extra=extra,
+                                  meta_data=None, container=container, driver=self))
+
+        return objects
+
+    def _headers_to_container(self, name, headers):
+        size = int(headers.get('x-container-bytes-used', 0))
+        object_count = int(headers.get('x-container-object-count', 0))
+
+        extra = { 'object_count': object_count,
+                  'size': size }
+        container = Container(name=name, extra=extra, driver=self)
+        return container
+
+    def _headers_to_object(self, name, container, headers):
+        size = int(headers.pop('content-length', 0))
+        last_modified = headers.pop('last-modified', None)
+        etag = headers.pop('etag', None)
+        content_type = headers.pop('content-type', None)
+
+        meta_data = {}
+        for key, value in headers.iteritems():
+            if key.find('x-object-meta-') != -1:
+                key = key.replace('x-object-meta-', '')
+                meta_data[key] = value
+
+        extra = { 'content_type': content_type, 'last_modified': last_modified,
+                  'etag': etag }
+
+        obj = Object(name=name, size=size, hash=None, extra=extra,
+                     meta_data=meta_data, container=container, driver=self)
+        return obj
+
+class CloudFilesUSStorageDriver(CloudFilesStorageDriver):
+    """
+    Cloudfiles storage driver for the US endpoint.
+    """
+
+    type = Provider.CLOUDFILES_US
+    name = 'CloudFiles (US)'
+    connectionCls = CloudFilesUSConnection
+
+class CloudFilesUKStorageDriver(CloudFilesStorageDriver):
+    """
+    Cloudfiles storage driver for the UK endpoint.
+    """
+
+    type = Provider.CLOUDFILES_UK
+    name = 'CloudFiles (UK)'
+    connectionCls = CloudFilesUKConnection

Added: incubator/libcloud/trunk/libcloud/storage/drivers/dummy.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/drivers/dummy.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/drivers/dummy.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/drivers/dummy.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,337 @@
+# 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.path
+import random
+
+from libcloud.common.types import LibcloudError
+
+from libcloud.storage.base import Object, Container, StorageDriver
+from libcloud.storage.types import ContainerAlreadyExistsError
+from libcloud.storage.types import ContainerDoesNotExistError
+from libcloud.storage.types import ContainerIsNotEmptyError
+from libcloud.storage.types import ObjectDoesNotExistError
+
+
+class DummyFileObject(file):
+    def __init__(self, yield_count=5, chunk_len=10):
+        self._yield_count = yield_count
+        self._chunk_len = chunk_len
+
+    def read(self, size):
+        i = 0
+
+        while i < self._yield_count:
+            yield self._get_chunk(self._chunk_len)
+            i += 1
+
+        raise StopIteration
+
+    def _get_chunk(self, chunk_len):
+        chunk = [str(x) for x in random.randint(97, 120)]
+        return chunk
+
+    def __len__(self):
+        return self._yield_count * self._chunk_len
+
+class DummyIterator(object):
+  def __init__(self, data=None):
+    self._data = data or []
+    self._current_item = 0
+
+  def next(self):
+      if self._current_item == len(self._data):
+          raise StopIteration
+
+      value = self._data[self._current_item]
+      self._current_item += 1
+      return value
+
+class DummyStorageDriver(StorageDriver):
+    """
+    Dummy Storage driver.
+
+    >>> from libcloud.storage.drivers.dummy import DummyStorageDriver
+    >>> driver = DummyStorageDriver('key', 'secret')
+    >>> container = driver.create_container(container_name='test container')
+    >>> container
+    <Container: name=test container, provider=Dummy Storage Provider>
+    >>> container.name
+    'test container'
+    >>> container.extra['object_count']
+    0
+    """
+
+    name = 'Dummy Storage Provider'
+
+    def __init__(self, api_key, api_secret):
+        self._containers = {}
+
+    def get_meta_data(self):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> driver.get_meta_data()
+        {'object_count': 0, 'container_count': 0, 'bytes_used': 0}
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container = driver.create_container(container_name='test container 2')
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> driver.get_meta_data()
+        {'object_count': 1, 'container_count': 2, 'bytes_used': 50}
+        """
+
+        container_count = len(self._containers)
+        object_count = sum([ len(self._containers[container]['objects']) for
+                        container in self._containers ])
+
+        bytes_used = 0
+        for container in self._containers:
+            objects = self._containers[container]['objects']
+            for _, obj in objects.iteritems():
+                bytes_used += obj.size
+
+        return { 'container_count': int(container_count),
+                  'object_count': int(object_count),
+                  'bytes_used': int(bytes_used) }
+
+    def list_containers(self):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> driver.list_containers()
+        []
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container
+        <Container: name=test container 1, provider=Dummy Storage Provider>
+        >>> container.name
+        'test container 1'
+        >>> container = driver.create_container(container_name='test container 2')
+        >>> container
+        <Container: name=test container 2, provider=Dummy Storage Provider>
+        >>> container = driver.create_container(container_name='test container 2') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerAlreadyExistsError:
+        >>> container_list=driver.list_containers()
+        >>> sorted([container.name for container in container_list])
+        ['test container 1', 'test container 2']
+        """
+
+        return [container['container'] for container in
+                self._containers.values()]
+
+    def list_container_objects(self, container):
+        container = self.get_container(container.name)
+
+        return container.objects
+
+    def get_container(self, container_name):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> driver.get_container('unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerDoesNotExistError:
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container
+        <Container: name=test container 1, provider=Dummy Storage Provider>
+        >>> container.name
+        'test container 1'
+        >>> driver.get_container('test container 1')
+        <Container: name=test container 1, provider=Dummy Storage Provider>
+        """
+
+        if container_name not in self._containers:
+           raise ContainerDoesNotExistError(driver=self, value=None,
+                                            container_name=container_name)
+
+        return self._containers[container_name]['container']
+
+    def get_object(self, container_name, object_name):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> driver.get_object('unknown', 'unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerDoesNotExistError:
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container
+        <Container: name=test container 1, provider=Dummy Storage Provider>
+        >>> driver.get_object('test container 1', 'unknown') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ObjectDoesNotExistError:
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> obj
+        <Object: name=test object, size=50, hash=None, provider=Dummy Storage Provider ...>
+        """
+
+        container = self.get_container(container_name)
+
+        container_objects = self._containers[container_name]['objects']
+        if object_name not in container_objects:
+            raise ObjectDoesNotExistError(object_name=object_name, value=None,
+                                          driver=self)
+
+        return container_objects[object_name]
+
+    def create_container(self, container_name):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container
+        <Container: name=test container 1, provider=Dummy Storage Provider>
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerAlreadyExistsError:
+        """
+
+        if container_name in self._containers:
+            raise ContainerAlreadyExistsError(container_name=container_name,
+                                              value=None, driver=self)
+
+        extra = { 'object_count': 0 }
+        container = Container(name=container_name, extra=extra, driver=self)
+
+        self._containers[container_name] = { 'container': container,
+                                             'objects': {}
+                                           }
+        return container
+
+    def delete_container(self, container):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = Container(name = 'test container', extra={'object_count': 0}, driver=driver)
+        >>> driver.delete_container(container=container) #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerDoesNotExistError:
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        >>> len(driver._containers)
+        1
+        >>> driver.delete_container(container=container)
+        True
+        >>> len(driver._containers)
+        0
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> driver.delete_container(container=container) #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ContainerIsNotEmptyError:
+        """
+
+        container_name = container.name
+        if container_name not in self._containers:
+           raise ContainerDoesNotExistError(container_name=container_name,
+                                            value=None, driver=self)
+
+        container = self._containers[container_name]
+        if len(container['objects']) > 0:
+           raise ContainerIsNotEmptyError(container_name=container_name,
+                                          value=None, driver=self)
+
+        del self._containers[container_name]
+        return True
+
+    def download_object(self, obj, destination_path, overwrite_existing=False,
+                       delete_on_failure=True):
+      kwargs_dict =  {'obj': obj,
+                      'response': DummyFileObject(),
+                      'destination_path': destination_path,
+                      'overwrite_existing': overwrite_existing,
+                      'delete_on_failure': delete_on_failure}
+
+      return self._save_object(**kwargs_dict)
+
+    def object_as_stream(self, obj, chunk_size=None):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> stream = container.object_as_stream(obj)
+        >>> stream #doctest: +ELLIPSIS
+        <closed file '<uninitialized file>', mode '<uninitialized file>' at 0x...>
+        """
+
+        return DummyFileObject()
+
+    def upload_object(self, file_path, container, object_name, extra=None,
+                      file_hash=None):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = driver.create_container(container_name='test container 1')
+        >>> container.upload_object(file_path='/tmp/inexistent.file', object_name='test') #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        LibcloudError:
+        >>> file_path = path = os.path.abspath(__file__)
+        >>> file_size = os.path.getsize(file_path)
+        >>> obj = container.upload_object(file_path=file_path, object_name='test')
+        >>> obj #doctest: +ELLIPSIS
+        <Object: name=test, size=...>
+        >>> obj.size == file_size
+        True
+        """
+
+        if not os.path.exists(file_path):
+            raise LibcloudError(value='File %s does not exist' % (file_path),
+                                driver=self)
+
+        size = os.path.getsize(file_path)
+        return self._add_object(container=container, object_name=object_name,
+                                size=size, extra=extra)
+
+    def stream_object_data(self, iterator, container, object_name, extra=None):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> obj #doctest: +ELLIPSIS
+        <Object: name=test object, size=50, ...>
+        """
+
+        size = len(iterator)
+        return self._add_object(container=container, object_name=object_name,
+                                size=size, extra=extra)
+
+    def delete_object(self, obj):
+        """
+        >>> driver = DummyStorageDriver('key', 'secret')
+        >>> container = driver.create_container(container_name='test container 1') #doctest: +IGNORE_EXCEPTION_DETAIL
+        >>> obj = container.stream_object_data(object_name='test object', iterator=DummyFileObject(5, 10), extra={})
+        >>> obj #doctest: +ELLIPSIS
+        <Object: name=test object, size=50, ...>
+        >>> container.delete_object(obj=obj)
+        True
+        >>> obj = Object(name='test object 2', size=1000, hash=None, extra=None, meta_data=None, container=container,driver=None)
+        >>> container.delete_object(obj=obj) #doctest: +IGNORE_EXCEPTION_DETAIL
+        Traceback (most recent call last):
+        ObjectDoesNotExistError:
+        """
+
+        container_name = obj.container.name
+        object_name = obj.name
+        obj = self.get_object(container_name=container_name,
+                              object_name=object_name)
+
+        del self._containers[container_name]['objects'][object_name]
+        return True
+
+    def _add_object(self, container, object_name, size, extra=None):
+        container = self.get_container(container.name)
+
+        extra = extra or {}
+        meta_data = extra.get('meta_data', {})
+        obj = Object(name=object_name, size=size, extra=extra, hash=None,
+                     meta_data=meta_data, container=container, driver=self)
+
+        self._containers[container.name]['objects'][object_name] = obj
+        return obj
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

Added: incubator/libcloud/trunk/libcloud/storage/providers.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/providers.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/providers.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/providers.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,29 @@
+# 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.
+
+from libcloud.utils import get_driver as get_provider_driver
+from libcloud.storage.types import Provider
+
+DRIVERS = {
+    Provider.DUMMY:
+        ('libcloud.storage.drivers.dummy', 'DummyStorageDriver'),
+    Provider.CLOUDFILES_US:
+        ('libcloud.storage.drivers.cloudfiles', 'CloudFilesUSStorageDriver'),
+    Provider.CLOUDFILES_UK:
+        ('libcloud.storage.drivers.cloudfiles', 'CloudFilesUKStorageDriver'),
+}
+
+def get_driver(provider):
+    return get_provider_driver(DRIVERS, provider)

Added: incubator/libcloud/trunk/libcloud/storage/types.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/storage/types.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/storage/types.py (added)
+++ incubator/libcloud/trunk/libcloud/storage/types.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,68 @@
+# 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.
+
+from libcloud.common.types import LibcloudError
+
+class Provider(object):
+    """
+    Defines for each of the supported providers
+
+    @cvar DUMMY: Example provider
+    @cvar CLOUDFILES_US: CloudFiles US
+    @cvar CLOUDFILES_UK: CloudFiles UK
+    """
+    DUMMY = 0
+    CLOUDFILES_US = 1
+    CLOUDFILES_UK = 2
+
+class ContainerError(LibcloudError):
+    error_type = 'ContainerError'
+
+    def __init__(self, value, driver, container_name):
+        self.container_name = container_name
+        super(ContainerError, self).__init__(value=value, driver=driver)
+
+    def __str__(self):
+        return '<%s in %s, container = %s>' % (self.error_type, repr(self.driver),
+                                          self.container_name)
+
+class ObjectError(LibcloudError):
+    error_type = 'ContainerError'
+
+    def __init__(self, value, driver, object_name):
+        self.object_name = object_name
+        super(ObjectError, self).__init__(value=value, driver=driver)
+
+    def __str__(self):
+        return '<%s in %s, object = %s>' % (self.error_type, repr(self.driver),
+                                            self.object_name)
+
+class ContainerAlreadyExistsError(ContainerError):
+    error_type = 'ContainerAlreadyExistsError'
+
+class ContainerDoesNotExistError(ContainerError):
+    error_type = 'ContainerDoesNotExistError'
+
+class ContainerIsNotEmptyError(ContainerError):
+    error_type = 'ContainerIsNotEmptyError'
+
+class ObjectDoesNotExistError(ObjectError):
+    error_type = 'ObjectDoesNotExistError'
+
+class ObjectHashMismatchError(ObjectError):
+    error_type = 'ObjectHashMismatchError'
+
+class InvalidContainerNameError(ContainerError):
+    error_type = 'InvalidContainerNameError'

Added: incubator/libcloud/trunk/libcloud/types.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/types.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/types.py (added)
+++ incubator/libcloud/trunk/libcloud/types.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,23 @@
+# 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.
+
+from libcloud.common.types import LibcloudError, MalformedResponseError
+from libcloud.common.types import InvalidCredsError, InvalidCredsException
+from libcloud.compute.types import Provider, NodeState, DeploymentError
+from libcloud.compute.types import DeploymentException
+
+from libcloud.utils import deprecated_warning
+
+deprecated_warning(__name__)

Added: incubator/libcloud/trunk/libcloud/utils.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/utils.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/utils.py (added)
+++ incubator/libcloud/trunk/libcloud/utils.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,77 @@
+# 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 mimetypes
+import warnings
+from httplib import HTTPResponse
+
+SHOW_DEPRECATION_WARNING = True
+OLD_API_REMOVE_VERSION = '0.6.0'
+
+def read_in_chunks(iterator, chunk_size=None):
+    """
+    Return a generator which yields data in chunks.
+
+    @type iterator: C{Iterator}
+    @param response: An object which implements an iterator interface
+                     or a File like object with read method.
+
+    @type chunk_size: C{int}
+    @param chunk_size: Optional chunk size (defaults to CHUNK_SIZE)
+    """
+
+    if isinstance(iterator, (file, HTTPResponse)):
+        get_data = iterator.read
+        args = (chunk_size, )
+    else:
+        get_data = iterator.next
+        args = ()
+
+    while True:
+        chunk = str(get_data(*args))
+
+        if len(chunk) == 0:
+            raise StopIteration
+
+        yield chunk
+
+def guess_file_mime_type(file_path):
+    filename = os.path.basename(file_path)
+    (mimetype, encoding) = mimetypes.guess_type(filename)
+    return mimetype, encoding
+
+def deprecated_warning(module):
+    if SHOW_DEPRECATION_WARNING:
+        warnings.warn('This path has been deprecated and the module'
+                       ' is now available at "libcloud.compute.%s".'
+                       ' This path will be fully removed in libcloud %s.' % \
+                       (module, OLD_API_REMOVE_VERSION),
+                  category=DeprecationWarning)
+
+def get_driver(drivers, provider):
+    """
+    Get a driver.
+
+    @param drivers: Dictionary containing valid providers.
+    @param provider: Id of provider to get driver
+    @type provider: L{libcloud.types.Provider}
+    """
+    if provider in drivers:
+        mod_name, driver_name = drivers[provider]
+        _mod = __import__(mod_name, globals(), locals(), [driver_name])
+        return getattr(_mod, driver_name)
+
+    raise AttributeError('Provider %s does not exist' % (provider))

Added: incubator/libcloud/trunk/setup.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/setup.py?rev=1079029&view=auto
==============================================================================
--- incubator/libcloud/trunk/setup.py (added)
+++ incubator/libcloud/trunk/setup.py Mon Mar  7 23:44:06 2011
@@ -0,0 +1,136 @@
+# 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
+from distutils.core import setup
+from distutils.core import Command
+from unittest import TextTestRunner, TestLoader
+from glob import glob
+from os.path import splitext, basename, join as pjoin
+
+import libcloud.utils
+libcloud.utils.SHOW_DEPRECATION_WARNING = False
+
+HTML_VIEWSOURCE_BASE = 'https://svn.apache.org/viewvc/incubator/libcloud/trunk'
+PROJECT_BASE_DIR = 'http://incubator.apache.org/libcloud/'
+TEST_PATHS = [ 'test', 'test/compute', 'test/storage' ]
+
+class TestCommand(Command):
+    user_options = []
+
+    def initialize_options(self):
+        THIS_DIR = os.path.abspath(os.path.split(__file__)[0])
+        sys.path.insert(0, THIS_DIR)
+        for test_path in TEST_PATHS:
+          sys.path.insert(0, pjoin(THIS_DIR, test_path))
+        self._dir = os.getcwd()
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        secrets = pjoin(self._dir, 'test', 'secrets.py')
+        if not os.path.isfile(secrets):
+            print "Missing %s" % (secrets)
+            print "Maybe you forgot to copy it from -dist:"
+            print "  cp test/secrets.py-dist test/secrets.py"
+            sys.exit(1)
+
+        pre_python26 = (sys.version_info[0] == 2
+                        and sys.version_info[1] < 6)
+        if pre_python26:
+            missing = []
+            # test for dependencies
+            try:
+                import simplejson
+            except ImportError:
+                missing.append("simplejson")
+
+            try:
+                import ssl
+            except ImportError:
+                missing.append("ssl")
+
+            if missing:
+                print "Missing dependencies: %s" % ", ".join(missing)
+                sys.exit(1)
+
+        testfiles = []
+        for test_path in TEST_PATHS:
+          for t in glob(pjoin(self._dir, test_path, 'test_*.py')):
+              testfiles.append('.'.join(
+                  [test_path.replace('/', '.'), splitext(basename(t))[0]])
+              )
+
+        tests = TestLoader().loadTestsFromNames(testfiles)
+        t = TextTestRunner(verbosity = 2)
+        res = t.run(tests)
+        sys.exit(not res.wasSuccessful())
+
+class ApiDocsCommand(Command):
+    user_options = []
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        os.system(
+            'pydoctor'
+            ' --add-package=libcloud'
+            ' --project-name=libcloud'
+            ' --make-html'
+            ' --html-viewsource-base="%s"'
+            ' --project-base-dir=`pwd`'
+            ' --project-url="%s"'
+            % (HTML_VIEWSOURCE_BASE, PROJECT_BASE_DIR)
+        )
+
+# pre-2.6 will need the ssl PyPI package
+pre_python26 = (sys.version_info[0] == 2 and sys.version_info[1] < 6)
+
+setup(
+    name='apache-libcloud',
+    version='0.4.3',
+    description='A unified interface into many cloud server providers',
+    author='Apache Software Foundation',
+    author_email='libcloud@incubator.apache.org',
+    requires=([], ['ssl', 'simplejson'],)[pre_python26],
+    packages=[
+        'libcloud',
+        'libcloud.drivers'
+    ],
+    package_dir={
+        'libcloud': 'libcloud',
+        'libcloud.drivers': 'libcloud/drivers'
+    },
+    license='Apache License (2.0)',
+    url='http://incubator.apache.org/libcloud/',
+    cmdclass={
+        'test': TestCommand,
+        'apidocs': ApiDocsCommand
+    },
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Environment :: Console',
+        'Intended Audience :: System Administrators',
+        'License :: OSI Approved :: Apache Software License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Libraries :: Python Modules'
+    ],
+)



Mime
View raw message