You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2011/10/15 23:22:57 UTC

svn commit: r1183722 - in /libcloud/trunk: libcloud/common/ libcloud/compute/ libcloud/compute/drivers/ test/compute/ test/compute/fixtures/openstack_v1.1/

Author: tomaz
Date: Sat Oct 15 21:22:57 2011
New Revision: 1183722

URL: http://svn.apache.org/viewvc?rev=1183722&view=rev
Log:
Add support for 1.1 API and many other improvements for the OpenStack driver.
This patch is part of LIBCLOUD-83. External contributors include Mike Nerone and
Brad Morgan.

Added:
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/README
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_7.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_detail.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_13.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_detail.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_os_quota_sets_aTenantId.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064_updated_name_bob.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail.json
    libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail_EMPTY.json
Modified:
    libcloud/trunk/libcloud/common/base.py
    libcloud/trunk/libcloud/compute/base.py
    libcloud/trunk/libcloud/compute/drivers/openstack.py
    libcloud/trunk/libcloud/compute/providers.py
    libcloud/trunk/test/compute/test_openstack.py
    libcloud/trunk/test/compute/test_rackspace.py

Modified: libcloud/trunk/libcloud/common/base.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/base.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/base.py (original)
+++ libcloud/trunk/libcloud/common/base.py Sat Oct 15 21:22:57 2011
@@ -520,7 +520,8 @@ class BaseDriver(object):
 
     connectionCls = ConnectionKey
 
-    def __init__(self, key, secret=None, secure=True, host=None, port=None):
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 api_version=None):
         """
         @keyword    key:    API key or username to used
         @type       key:    str
@@ -537,6 +538,11 @@ class BaseDriver(object):
 
         @keyword    port: Override port used for connections.
         @type       port: int
+
+        @keyword    api_version: Optional API version. Only used by drivers
+                                 which support multiple API versions.
+        @type       api_version: str
+
         """
         self.key = key
         self.secret = secret

Modified: libcloud/trunk/libcloud/compute/base.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/base.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/base.py (original)
+++ libcloud/trunk/libcloud/compute/base.py Sat Oct 15 21:22:57 2011
@@ -351,9 +351,11 @@ class NodeDriver(BaseDriver):
 
     NODE_STATE_MAP = {}
 
-    def __init__(self, key, secret=None, secure=True, host=None, port=None):
+    def __init__(self, key, secret=None, secure=True, host=None, port=None,
+                 api_version=None):
       super(NodeDriver, self).__init__(key=key, secret=secret, secure=secure,
-                                       host=host, port=port)
+                                       host=host, port=port,
+                                       api_version=api_version)
 
     def create_node(self, **kwargs):
         """Create a new node instance.

Modified: libcloud/trunk/libcloud/compute/drivers/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/openstack.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/openstack.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/openstack.py Sat Oct 15 21:22:57 2011
@@ -15,15 +15,21 @@
 """
 OpenStack driver
 """
+
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
 import os
 
 import base64
+import httplib
 import warnings
 
 from xml.etree import ElementTree as ET
-from xml.parsers.expat import ExpatError
 
-from libcloud.pricing import get_size_price, PRICING_DATA
+from libcloud.pricing import get_size_price
 from libcloud.common.base import Response
 from libcloud.common.types import MalformedResponseError
 from libcloud.compute.types import NodeState, Provider
@@ -37,9 +43,38 @@ __all__ = [
     'OpenStack_1_0_NodeDriver',
     'OpenStack_1_0_SharedIpGroup',
     'OpenStack_1_0_NodeIpAddresses',
+    'OpenStack_1_1_Response',
+    'OpenStack_1_1_Connection',
+    'OpenStack_1_1_NodeDriver',
+    'OpenStackDriver'
     ]
 
-class OpenStack_1_0_Response(Response):
+
+ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
+
+OPENSTACK_NODE_STATE_MAP = {'BUILD': NodeState.PENDING,
+                            'REBUILD': NodeState.PENDING,
+                            'ACTIVE': NodeState.RUNNING,
+                            'SUSPENDED': NodeState.TERMINATED,
+                            'QUEUE_RESIZE': NodeState.PENDING,
+                            'PREP_RESIZE': NodeState.PENDING,
+                            'VERIFY_RESIZE': NodeState.RUNNING,
+                            'PASSWORD': NodeState.PENDING,
+                            'RESCUE': NodeState.PENDING,
+                            'REBUILD': NodeState.PENDING,
+                            'REBOOT': NodeState.REBOOTING,
+                            'HARD_REBOOT': NodeState.REBOOTING,
+                            'SHARE_IP': NodeState.PENDING,
+                            'SHARE_IP_NO_CONFIG': NodeState.PENDING,
+                            'DELETE_IP': NodeState.PENDING,
+                            'UNKNOWN': NodeState.UNKNOWN}
+
+DEFAULT_API_VERSION = '1.1'
+
+
+class OpenStack_Response(Response):
+
+    node_driver = None
 
     def success(self):
         i = int(self.status)
@@ -51,6 +86,9 @@ class OpenStack_1_0_Response(Response):
         return content_type_value.find(content_type.lower()) > -1
 
     def parse_body(self):
+        if self.status == httplib.NO_CONTENT or not self.body:
+            return None
+
         if self.has_content_type('application/xml'):
             try:
                 return ET.XML(self.body)
@@ -58,46 +96,40 @@ class OpenStack_1_0_Response(Response):
                 raise MalformedResponseError(
                     'Failed to parse XML',
                     body=self.body,
-                    driver=OpenStack_1_0_NodeDriver)
+                    driver=self.node_driver)
 
+        elif self.has_content_type('application/json'):
+            try:
+                return json.loads(self.body)
+            except:
+                raise MalformedResponseError(
+                    'Failed to parse JSON',
+                    body=self.body,
+                    driver=self.node_driver)
         else:
             return self.body
 
     def parse_error(self):
-        # TODO: fixup; only uses response codes really!
-        try:
-            body = ET.XML(self.body)
-        except:
-            raise MalformedResponseError(
-                "Failed to parse XML",
-                body=self.body, driver=OpenStack_1_0_NodeDriver)
-        try:
-            text = "; ".join([err.text or ''
-                              for err in
-                              body.getiterator()
-                              if err.text])
-        except ExpatError:
-            text = self.body
-        return '%s %s %s' % (self.status, self.error, text)
+        text = None
+        body = self.parse_body()
 
+        if self.has_content_type('application/xml'):
+            text = "; ".join([err.text or '' for err in body.getiterator()
+                              if err.text])
+        elif self.has_content_type('application/json'):
+            text = ';'.join([fault_data['message'] for fault_data
+                             in body.values()])
+        else:
+            # while we hope a response is always one of xml or json, we have
+            # seen html or text in the past, its not clear we can really do
+            # something to make it more readable here, so we will just pass
+            # it along as the whole response body in the text variable.
+            text = body
 
-class OpenStack_1_0_Connection(OpenStackBaseConnection):
+        return '%s %s %s' % (self.status, self.error, text)
 
-    responseCls = OpenStack_1_0_Response
-    _url_key = "server_url"
-    XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
 
-    def __init__(self, user_id, key, secure=True, host=None, port=None,
-                 ex_force_base_url=None,
-                 ex_force_auth_url=None,
-                 ex_force_auth_version=None):
-        super(OpenStack_1_0_Connection, self).__init__(
-            user_id, key, host=host, port=port,
-            ex_force_base_url=ex_force_base_url,
-            ex_force_auth_url=ex_force_auth_url,
-            ex_force_auth_version=ex_force_auth_version)
-        self.api_version = 'v1.0'
-        self.accept_format = 'application/xml'
+class OpenStackComputeConnection(OpenStackBaseConnection):
 
     def request(self, action, params=None, data='', headers=None,
                 method='GET'):
@@ -107,17 +139,106 @@ class OpenStack_1_0_Connection(OpenStack
             params = {}
 
         if method in ("POST", "PUT"):
-            headers = {'Content-Type': 'application/xml; charset=UTF-8'}
+            headers = {'Content-Type': self.default_content_type}
+
         if method == "GET":
             params['cache-busting'] = os.urandom(8).encode('hex')
-        return super(OpenStack_1_0_Connection, self).request(
+
+        return super(OpenStackComputeConnection, self).request(
             action=action,
             params=params, data=data,
             method=method, headers=headers
         )
 
 
-class OpenStack_1_0_NodeDriver(NodeDriver):
+class OpenStackNodeDriver(NodeDriver):
+
+    def __new__(cls, key, secret=None, secure=True, host=None, port=None,
+                 api_version=DEFAULT_API_VERSION, **kwargs):
+        if cls is OpenStackNodeDriver:
+            if api_version == '1.0':
+                cls = OpenStack_1_0_NodeDriver
+            elif api_version == '1.1':
+                cls = OpenStack_1_1_NodeDriver
+            else:
+                raise NotImplementedError(
+                    "No OpenStackNodeDriver found for API version %s" %
+                    (api_version)
+                )
+        return super(OpenStackNodeDriver, cls).__new__(cls)
+
+    def _ex_connection_class_kwargs(self):
+        rv = {}
+        if self._ex_force_base_url:
+            rv['ex_force_base_url'] = self._ex_force_base_url
+        if self._ex_force_auth_url:
+            rv['ex_force_auth_url'] = self._ex_force_auth_url
+        if self._ex_force_auth_version:
+            rv['ex_force_auth_version'] = self._ex_force_auth_version
+        return rv
+
+    def list_nodes(self):
+        return self._to_nodes(self.connection.request('/servers/detail')
+                                             .object)
+
+    def list_sizes(self, location=None):
+        return self._to_sizes(self.connection.request('/flavors/detail')
+                                             .object)
+
+    def list_images(self, location=None, ex_only_active=True):
+        return self._to_images(self.connection.request('/images/detail')
+                                              .object, ex_only_active)
+
+    def ex_get_node_details(self, node_id):
+        # @TODO: Remove this if in 0.6
+        if isinstance(node_id, Node):
+            node_id = node_id.id
+
+        uri = '/servers/%s' % (node_id)
+        resp = self.connection.request(uri, method='GET')
+        if resp.status == httplib.NOT_FOUND:
+            return None
+
+        return self._to_node_from_obj(resp.object)
+
+    def destroy_node(self, node):
+        uri = '/servers/%s' % (node.id)
+        resp = self.connection.request(uri, method='DELETE')
+        # The OpenStack and Rackspace documentation both say this API will
+        # return a 204, but in-fact, everyone everywhere agrees it actually
+        # returns a 202, so we are going to accept either, and someday,
+        # someone will fix either the implementation or the documentation to
+        # agree.
+        return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
+
+    def ex_soft_reboot_node(self, node):
+        return self._reboot_node(node, reboot_type='SOFT')
+
+    def ex_hard_reboot_node(self, node):
+        return self._reboot_node(node, reboot_type='HARD')
+
+    def reboot_node(self, node):
+        return self._reboot_node(node, reboot_type='HARD')
+
+
+class OpenStack_1_0_Response(OpenStack_Response):
+
+    def __init__(self, *args, **kwargs):
+        # done because of a circular reference from
+        # NodeDriver -> Connection -> Response
+        self.node_driver = OpenStack_1_0_NodeDriver
+        super(OpenStack_1_0_Response, self).__init__(*args, **kwargs)
+
+
+class OpenStack_1_0_Connection(OpenStackComputeConnection):
+    responseCls = OpenStack_1_0_Response
+    _url_key = "server_url"
+    default_content_type = 'application/xml; charset=UTF-8'
+    accept_format = 'application/xml'
+    XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
+
+
+class OpenStack_1_0_NodeDriver(OpenStackNodeDriver):
     """
     OpenStack node driver.
 
@@ -134,54 +255,17 @@ class OpenStack_1_0_NodeDriver(NodeDrive
 
     features = {"create_node": ["generates_password"]}
 
-    NODE_STATE_MAP = {'BUILD': NodeState.PENDING,
-                      'REBUILD': NodeState.PENDING,
-                      'ACTIVE': NodeState.RUNNING,
-                      'SUSPENDED': NodeState.TERMINATED,
-                      'QUEUE_RESIZE': NodeState.PENDING,
-                      'PREP_RESIZE': NodeState.PENDING,
-                      'VERIFY_RESIZE': NodeState.RUNNING,
-                      'PASSWORD': NodeState.PENDING,
-                      'RESCUE': NodeState.PENDING,
-                      'REBUILD': NodeState.PENDING,
-                      'REBOOT': NodeState.REBOOTING,
-                      'HARD_REBOOT': NodeState.REBOOTING,
-                      'SHARE_IP': NodeState.PENDING,
-                      'SHARE_IP_NO_CONFIG': NodeState.PENDING,
-                      'DELETE_IP': NodeState.PENDING,
-                      'UNKNOWN': NodeState.UNKNOWN}
+    NODE_STATE_MAP = OPENSTACK_NODE_STATE_MAP
 
     def __init__(self, *args, **kwargs):
         self._ex_force_base_url = kwargs.pop('ex_force_base_url', None)
         self._ex_force_auth_url = kwargs.pop('ex_force_auth_url', None)
         self._ex_force_auth_version = kwargs.pop('ex_force_auth_version', None)
+        self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
+                                                    None))
         self.XML_NAMESPACE = self.connectionCls.XML_NAMESPACE
         super(OpenStack_1_0_NodeDriver, self).__init__(*args, **kwargs)
 
-    def _ex_connection_class_kwargs(self):
-        rv = {}
-        if self._ex_force_base_url:
-            rv['ex_force_base_url'] = self._ex_force_base_url
-        if self._ex_force_auth_url:
-            rv['ex_force_auth_url'] = self._ex_force_auth_url
-        if self._ex_force_auth_version:
-            rv['ex_force_auth_version'] = self._ex_force_auth_version
-        return rv
-
-
-    def list_nodes(self):
-        return self._to_nodes(self.connection.request('/servers/detail')
-                                             .object)
-
-    def list_sizes(self, location=None):
-        return self._to_sizes(self.connection.request('/flavors/detail')
-                                             .object)
-
-    def list_images(self, location=None):
-        return self._to_images(self.connection.request('/images/detail')
-                                              .object)
-    # TODO: def list_locations: Is there an OpenStack way to do this? Rackspace-specific docstring says no.
-
     def _change_password_or_name(self, node, name=None, password=None):
         uri = '/servers/%s' % (node.id)
 
@@ -264,6 +348,7 @@ class OpenStack_1_0_NodeDriver(NodeDrive
         files_elm = self._files_to_xml(kwargs.get("ex_files", {}))
         if files_elm:
             server_elm.append(files_elm)
+
         resp = self.connection.request("/servers",
                                        method='POST',
                                        data=ET.tostring(server_elm))
@@ -460,31 +545,6 @@ class OpenStack_1_0_NodeDriver(NodeDrive
         resp = self._node_action(node, ['reboot', ('type', reboot_type)])
         return resp.status == 202
 
-    def ex_soft_reboot_node(self, node):
-        return self._reboot_node(node, reboot_type='SOFT')
-
-    def ex_hard_reboot_node(self, node):
-        return self._reboot_node(node, reboot_type='HARD')
-
-    def reboot_node(self, node):
-        return self._reboot_node(node, reboot_type='HARD')
-
-    def destroy_node(self, node):
-        uri = '/servers/%s' % (node.id)
-        resp = self.connection.request(uri, method='DELETE')
-        return resp.status == 202
-
-    def ex_get_node_details(self, node_id):
-        # @TODO: Remove this if in 0.6
-        if isinstance(node_id, Node):
-            node_id = node_id.id
-
-        uri = '/servers/%s' % (node_id)
-        resp = self.connection.request(uri, method='GET')
-        if resp.status == 404:
-            return None
-        return self._to_node(resp.object)
-
     def _node_action(self, node, body):
         if isinstance(body, list):
             attr = ' '.join(['%s="%s"' % (item[0], item[1])
@@ -500,11 +560,15 @@ class OpenStack_1_0_NodeDriver(NodeDrive
 
     def _fixxpath(self, xpath):
         # ElementTree wants namespaces in its xpaths, so here we add them.
-        return "/".join(["{%s}%s" % (self.XML_NAMESPACE, e) for e in xpath.split("/")])
+        return "/".join(["{%s}%s" % (self.XML_NAMESPACE, e) for e
+                         in xpath.split("/")])
 
     def _findall(self, element, xpath):
         return element.findall(self._fixxpath(xpath))
 
+    def _to_node_from_obj(self, obj):
+        return self._to_node(self._findall(obj, 'server')[0])
+
     def _to_node(self, el):
         def get_ips(el):
             return [ip.get('addr') for ip in el]
@@ -549,16 +613,21 @@ class OpenStack_1_0_NodeDriver(NodeDrive
                      name=el.get('name'),
                      ram=int(el.get('ram')),
                      disk=int(el.get('disk')),
-                     bandwidth=None, # XXX: needs hardcode
-                     price=self._get_size_price(el.get('id')), # Hardcoded,
+                     # XXX: needs hardcode
+                     bandwidth=None,
+                     # Hardcoded
+                     price=self._get_size_price(el.get('id')),
                      driver=self.connection.driver)
         return s
 
-    def _to_images(self, object):
+    def _to_images(self, object, ex_only_active):
         elements = self._findall(object, "image")
-        return [self._to_image(el)
-                for el in elements
-                if el.get('status') == 'ACTIVE']
+        rv = []
+        for el in elements:
+            if ex_only_active and el.get('status') != 'ACTIVE':
+                continue
+            rv.append(self._to_image(el))
+        return rv
 
     def _to_image(self, el):
         i = NodeImage(id=el.get('id'),
@@ -676,3 +745,262 @@ class OpenStack_1_0_NodeIpAddresses(obje
     def __init__(self, public_addresses, private_addresses):
         self.public_addresses = public_addresses
         self.private_addresses = private_addresses
+
+
+class OpenStack_1_1_Response(OpenStack_Response):
+
+    def __init__(self, *args, **kwargs):
+        # done because of a circular reference from
+        # NodeDriver -> Connection -> Response
+        self.node_driver = OpenStack_1_1_NodeDriver
+        super(OpenStack_1_1_Response, self).__init__(*args, **kwargs)
+
+
+class OpenStack_1_1_Connection(OpenStackComputeConnection):
+    responseCls = OpenStack_1_1_Response
+    _url_key = "server_url"
+    accept_format = 'application/json'
+    default_content_type = 'application/json; charset=UTF-8'
+
+    def encode_data(self, data):
+        return json.dumps(data)
+
+
+class OpenStack_1_1_NodeDriver(OpenStackNodeDriver):
+    """
+    OpenStack node driver.
+    """
+    connectionCls = OpenStack_1_1_Connection
+    type = Provider.OPENSTACK
+    api_name = 'openstack'
+    name = 'OpenStack'
+
+    features = {"create_node": ["generates_password"]}
+
+    NODE_STATE_MAP = OPENSTACK_NODE_STATE_MAP
+
+    def __init__(self, *args, **kwargs):
+        self._ex_force_base_url = kwargs.pop('ex_force_base_url', None)
+        self._ex_force_auth_url = kwargs.pop('ex_force_auth_url', None)
+        self._ex_force_auth_version = kwargs.pop('ex_force_auth_version',
+                                                 None)
+        self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
+                                                    None))
+        super(OpenStack_1_1_NodeDriver, self).__init__(*args, **kwargs)
+
+    def _to_nodes(self, obj):
+        servers = obj['servers']
+        return [self._to_node(server) for server in servers]
+
+    def _to_sizes(self, obj):
+        flavors = obj['flavors']
+        return [self._to_size(flavor) for flavor in flavors]
+
+    def _to_images(self, obj, ex_only_active):
+        images = obj['images']
+        rv = []
+        for image in images:
+            if ex_only_active and image.get('status') != 'ACTIVE':
+                continue
+            rv.append(self._to_image(image))
+        return rv
+
+    def _create_args_to_params(self, node, kwargs):
+        server_params = {
+            'name': kwargs.get('name'),
+            'metadata': kwargs.get('ex_metadata', {}),
+            'personality': self._files_to_personality(kwargs.get("ex_files",
+                                                                 {}))
+        }
+
+        if 'name' in kwargs:
+            server_params['name'] = kwargs.get('name')
+        else:
+            server_params['name'] = node.name
+
+        if 'image' in kwargs:
+            server_params['imageRef'] = kwargs.get('image').id
+        else:
+            server_params['imageRef'] = node.extra.get('imageId')
+
+        if 'size' in kwargs:
+            server_params['flavorRef'] = kwargs.get('size').id
+        else:
+            server_params['flavorRef'] = node.extra.get('flavorId')
+
+        return server_params
+
+    def create_node(self, **kwargs):
+        """Create a new node
+
+        See L{NodeDriver.create_node} for more keyword args.
+        @keyword    ex_metadata: Key/Value metadata to associate with a node
+        @type       ex_metadata: C{dict}
+
+        @keyword    ex_files:   File Path => File contents to create on
+                                the node
+        @type       ex_files:   C{dict}
+        """
+
+        server_params = self._create_args_to_params(None, kwargs)
+
+        resp = self.connection.request("/servers",
+                                       method='POST',
+                                       data={'server': server_params})
+
+        return self._to_node(resp.object['server'])
+
+    def _files_to_personality(self, files):
+        rv = []
+
+        for k, v in files.items():
+            rv.append({'path': k, 'contents': base64.b64encode(v)})
+
+        return rv
+
+    def _reboot_node(self, node, reboot_type='SOFT'):
+        resp = self._node_action(node, 'reboot', type=reboot_type)
+        return resp.status == httplib.ACCEPTED
+
+    def ex_set_password(self, node, password):
+        resp = self._node_action(node, 'changePassword', adminPass=password)
+        node.extra['password'] = password
+        return resp.status == httplib.ACCEPTED
+
+    def ex_rebuild(self, node, **kwargs):
+        server_params = self._create_args_to_params(node, kwargs)
+        resp = self._node_action(node, 'rebuild', **server_params)
+        return resp.status == httplib.ACCEPTED
+
+    def ex_resize(self, node, size):
+        resp = self._node_action(node, 'resize', flavorRef=size.id)
+        return resp.status == httplib.ACCEPTED
+
+    def ex_confirm_resize(self, node):
+        resp = self._node_action(node, 'confirmResize')
+        return resp.status == httplib.NO_CONTENT
+
+    def ex_revert_resize(self, node):
+        resp = self._node_action(node, 'revertResize')
+        return resp.status == httplib.ACCEPTED
+
+    def ex_save_image(self, node, name, metadata=None):
+        # This has not yet been implemented by OpenStack 1.1
+        raise NotImplementedError()
+
+        optional_params = {}
+        if metadata:
+            optional_params['metadata'] = metadata
+        resp = self._node_action(node, 'createImage', name=name,
+                                 **optional_params)
+        # TODO: concevt location header into NodeImage object
+        return resp.status == httplib.NO_CONTENT
+
+    def ex_set_server_name(self, node, name):
+        """
+        Sets the Node's name.
+        """
+        return self.ex_update_node(node, name=name)
+
+    def ex_update_node(self, node, **node_updates):
+        # Currently only setting the name is supported in OpenStack, but we
+        # leave the function signature prepared to support metadata or other
+        # fields in the future.
+        potential_data = self._create_args_to_params(node, node_updates)
+        return self._to_node(
+            self.connection.request(
+                '/servers/%s' % (node.id,), method='PUT',
+                data={'server': {'name': potential_data['name']}}
+            ).object['server']
+        )
+
+    def ex_get_size(self, size_id):
+        return self._to_size(self.connection.request('/flavors/%s' %
+                                                     (size_id,))
+                   .object['flavor'])
+
+    def ex_get_image(self, image_id):
+        return self._to_image(self.connection.request('/images/%s' %
+                                                      (image_id,))
+                   .object['image'])
+
+    def ex_delete_image(self, image):
+        # This has not yet been implemented by OpenStack 1.1
+        raise NotImplementedError()
+
+        resp = self.connection.request('/images/%s' % (image.id,),
+                                       method='DELETE')
+        return resp.status == httplib.ACCEPTED
+
+    def _node_action(self, node, action, **params):
+        params = params or None
+        return self.connection.request('/servers/%s/action' % (node.id,),
+                                       method='POST', data={action: params})
+
+    def _to_node_from_obj(self, obj):
+        return self._to_node(obj['server'])
+
+    def _to_node(self, api_node):
+        return Node(
+            id=api_node['id'],
+            name=api_node['name'],
+            state=self.NODE_STATE_MAP.get(api_node['status'],
+                                          NodeState.UNKNOWN),
+            public_ip=[addr_desc['addr'] for addr_desc in
+                       api_node['addresses'].get('public', [])],
+            private_ip=[addr_desc['addr'] for addr_desc in
+                        api_node['addresses'].get('private', [])],
+            driver=self,
+            extra=dict(
+                hostId=api_node['hostId'],
+                # Docs says "tenantId", but actual is "tenant_id". *sigh*
+                # Best handle both.
+                tenantId=api_node.get('tenant_id') or api_node['tenantId'],
+                imageId=api_node['image']['id'],
+                flavorId=api_node['flavor']['id'],
+                uri=(link['href'] for link in api_node['links'] if
+                     link['rel'] == 'self').next(),
+                metadata=api_node['metadata'],
+                password=api_node.get('adminPass'),
+            ),
+        )
+
+    def _to_size(self, api_flavor, price=None, bandwidth=None):
+        # if provider-specific subclasses can get better values for
+        # price/bandwidth, then can pass them in when they super().
+        if not price:
+            price = self._get_size_price(str(api_flavor['id']))
+
+        return NodeSize(
+            id=api_flavor['id'],
+            name=api_flavor['name'],
+            ram=api_flavor['ram'],
+            disk=api_flavor['disk'],
+            bandwidth=bandwidth,
+            price=price,
+            driver=self,
+        )
+
+    def _to_image(self, api_image):
+        return NodeImage(
+            id=api_image['id'],
+            name=api_image['name'],
+            driver=self,
+            extra=dict(
+                updated=api_image['updated'],
+                created=api_image['created'],
+                status=api_image['status'],
+                progress=api_image.get('progress'),
+                metadata=api_image.get('metadata'),
+            ),
+        )
+
+    def _get_size_price(self, size_id):
+            try:
+                return get_size_price(
+                    driver_type='compute',
+                    driver_name=self.api_name,
+                    size_id=size_id,
+                )
+            except KeyError:
+                return(0.0)

Modified: libcloud/trunk/libcloud/compute/providers.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/providers.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/providers.py (original)
+++ libcloud/trunk/libcloud/compute/providers.py Sat Oct 15 21:22:57 2011
@@ -88,7 +88,7 @@ DRIVERS = {
     Provider.OPSOURCE:
         ('libcloud.compute.drivers.opsource', 'OpsourceNodeDriver'),
     Provider.OPENSTACK:
-        ('libcloud.compute.drivers.openstack', 'OpenStack_1_0_NodeDriver'),
+        ('libcloud.compute.drivers.openstack', 'OpenStackNodeDriver'),
     Provider.NINEFOLD:
         ('libcloud.compute.drivers.ninefold', 'NinefoldNodeDriver'),
 }

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/README
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/README?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/README (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/README Sat Oct 15 21:22:57 2011
@@ -0,0 +1,7 @@
+The json responses contained in this directory are copied directly from the
+OpenStack 1.1 documentation at
+http://docs.openstack.org/trunk/openstack-compute/developer/openstack-compute-api-1.1/
+as of this writing.
+
+The only exception is _os_quota_sets_aTenantId.json, which was captured (and
+perturbed) via packet capture.

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_7.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_7.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_7.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_7.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"flavor": {"rxtx_quota": 2500, "name": "15.5GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/7", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/7", "rel": "bookmark"}], "ram": 16384, "vcpus": 8, "rxtx_cap": 200, "swap": 0, "disk": 620, "id": 7}}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_detail.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_detail.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_detail.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_flavors_detail.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"flavors": [{"rxtx_quota": 2500, "name": "15.5GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/7", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/7", "rel": "bookmark"}], "ram": 16384, "vcpus": 8, "rxtx_cap": 200, "swap": 0, "disk": 620, "id": 7}, {"rxtx_quota": 600, "name": "1GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/3", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/3", "rel": "bookmark"}], "ram": 1024, "vcpus": 1, "rxtx_cap": 30, "swap": 0, "disk": 40, "id": 3}, {"rxtx_quota": 150, "name": "256 slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/1", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/1", "rel": "boo
 kmark"}], "ram": 256, "vcpus": 1, "rxtx_cap": 10, "swap": 0, "disk": 10, "id": 1}, {"rxtx_quota": 1200, "name": "2GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/4", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/4", "rel": "bookmark"}], "ram": 2048, "vcpus": 2, "rxtx_cap": 60, "swap": 0, "disk": 80, "id": 4}, {"rxtx_quota": 2500, "name": "30GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/8", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/8", "rel": "bookmark"}], "ram": 30720, "vcpus": 8, "rxtx_cap": 400, "swap": 0, "disk": 1200, "id": 8}, {"rxtx_quota": 2500, "name": "4GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/5", "rel": "self"}, {"href": "http://alpha.ord.serve
 rs.api.rackspacecloud.com:8774/rs-reach-project/flavors/5", "rel": "bookmark"}], "ram": 4096, "vcpus": 2, "rxtx_cap": 100, "swap": 0, "disk": 160, "id": 5}, {"rxtx_quota": 300, "name": "512 slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/2", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}], "ram": 512, "vcpus": 1, "rxtx_cap": 20, "swap": 0, "disk": 20, "id": 2}, {"rxtx_quota": 2500, "name": "8GB slice", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/flavors/6", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/6", "rel": "bookmark"}], "ram": 8192, "vcpus": 4, "rxtx_cap": 150, "swap": 0, "disk": 320, "id": 6}]}
\ No newline at end of file

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_13.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_13.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_13.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_13.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"image": {"status": "ACTIVE", "updated": "2011-08-06T18:14:02Z", "name": "Windows 2008 SP2 x86 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/13", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/13", "rel": "bookmark"}, {"href": "http://10.13.136.245:9292/rs-reach-project/images/13", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T18:13:11Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "13", "metadata": {"os_type": "windows"}}}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_detail.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_detail.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_detail.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_images_detail.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"images": [{"status": "ACTIVE", "updated": "2011-08-06T18:14:02Z", "name": "Windows 2008 SP2 x86 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/13", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/13", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/13", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T18:13:11Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "13", "metadata": {"os_type": "windows"}}, {"status": "ACTIVE", "updated": "2011-08-06T18:13:11Z", "name": "Windows 2003 R2 x86 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/12", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/12", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/12
 ", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T18:12:33Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "12", "metadata": {"os_type": "windows"}}, {"status": "ACTIVE", "updated": "2011-08-06T16:27:56Z", "name": "Windows 2008 SP2 x64 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/11", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/11", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/11", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T16:26:15Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "11", "metadata": {"os_type": "windows"}}, {"status": "ACTIVE", "updated": "2011-08-06T16:26:14Z", "name": "Windows 2008 R2 x64 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/10", "rel":
  "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/10", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/10", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T16:24:51Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "10", "metadata": {"os_type": "windows"}}, {"status": "ACTIVE", "updated": "2011-08-06T16:24:51Z", "name": "Windows 2003 R2 x64 (B24)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/9", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/9", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/9", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-06T16:23:52Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "9", "metadata": {"os_type": "windows"}}, {"status": "ACTIVE", "updat
 ed": "2011-08-05T22:58:29Z", "name": "Ubuntu Natty (11.04)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/8", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/8", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/8", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:58:20Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "8", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:58:19Z", "name": "Ubuntu Lucid (10.04)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/7", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/7", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created"
 : "2011-08-05T22:58:14Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "7", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:58:14Z", "name": "Fedora 15", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/6", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/6", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/6", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:58:01Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "6", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:58:00Z", "name": "Fedora 14", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/5", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/5", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292
 /rs-reach-project/images/5", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:57:47Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "5", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:57:47Z", "name": "Debian Squeeze (6.0)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/4", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/4", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/4", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:57:41Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "4", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:57:40Z", "name": "Debian Lenny (5.0)", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/3", "rel": "self"}, {"href": "http://alpha.
 ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/3", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/3", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:57:30Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "3", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:57:30Z", "name": "CentOS 6.0", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/2", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/2", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/2", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:57:20Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "2", "metadata": {}}, {"status": "ACTIVE", "updated": "2011-08-05T22:56:20Z", "name": "CentOS 5.6", "links": [{"href": "http://alpha.ord.ser
 vers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/images/1", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/1", "rel": "bookmark"}, {"href": "http://10.13.136.170:9292/rs-reach-project/images/1", "type": "application/vnd.openstack.image", "rel": "alternate"}], "created": "2011-08-05T22:56:03Z", "minDisk": 0, "progress": 100, "minRam": 0, "id": "1", "metadata": {}}]}
\ No newline at end of file

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_os_quota_sets_aTenantId.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_os_quota_sets_aTenantId.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_os_quota_sets_aTenantId.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_os_quota_sets_aTenantId.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"quota_set": {"metadata_items": 10, "injected_file_content_bytes": 1000, "injected_files": 10, "volumes": 0, "instances": 25, "gigabytes": 500, "cores": 50, "ram": 102400, "id": "aTenantId", "floating_ips": 10}}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1,78 @@
+{
+    "server": {
+        "id": "52415800-8b69-11e0-9b19-734f565bc83b",
+        "tenantId": "1234",
+        "userId": "5678",
+        "name": "new-server-test",
+        "created": "2010-11-11T12:00:00Z",
+        "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
+        "accessIPv4" : "67.23.10.138",
+        "accessIPv6" : "::babe:67.23.10.138",
+        "progress": 0,
+        "status": "BUILD",
+        "adminPass": "GFf1j9aP",
+        "image" : {
+            "id": "52415800-8b69-11e0-9b19-734f6f006e54",
+            "name": "CentOS 5.2",
+            "links": [
+                {
+                    "rel": "self",
+                    "href": "http://servers.api.openstack.org/v1.1/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
+                },
+                {
+                    "rel": "bookmark",
+                    "href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
+                }
+            ]
+        },
+        "flavor" : {
+            "id": "52415800-8b69-11e0-9b19-734f1195ff37",
+            "name": "256 MB Server",
+            "links": [
+                {
+                    "rel": "self",
+                    "href": "http://servers.api.openstack.org/v1.1/1234/flavors/52415800-8b69-11e0-9b19-734f1195ff37"
+                },
+                {
+                    "rel": "bookmark",
+                    "href": "http://servers.api.openstack.org/1234/flavors/52415800-8b69-11e0-9b19-734f1195ff37"
+                }
+            ]
+        },
+        "metadata": {
+            "My Server Name": "Apache1"
+        },
+        "addresses": {
+            "public" : [
+                {
+                    "version": 4,
+                    "addr": "67.23.10.138"
+                },
+                {
+                    "version": 6,
+                    "addr": "::babe:67.23.10.138"
+                }
+            ],
+            "private" : [
+                {
+                    "version": 4,
+                    "addr": "10.176.42.19"
+                },
+                {
+                    "version": 6,
+                    "addr": "::babe:10.176.42.19"
+                }
+            ]
+        },
+        "links": [
+            {
+                "rel": "self",
+                "href": "http://servers.api.openstack.org/v1.1/1234/servers/52415800-8b69-11e0-9b19-734fcece0043"
+            },
+            {
+                "rel": "bookmark",
+                "href": "http://servers.api.openstack.org/1234/servers/52415800-8b69-11e0-9b19-734fcece0043"
+            }
+        ]
+    }
+}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"server": {"status": "ACTIVE", "updated": "2011-10-11T00:44:20Z", "hostId": "a024053a6201e6c6c12660aab3d8fd879e332e663a5e1fdbc02a0307", "user_id": "rs-reach", "name": "lc-test", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12064", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12064", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.30"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe77:32e3"}], "private": [{"version": 4, "addr": "10.182.64.29"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe6e:b7e2"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:45:02Z", "uuid": "ec53630b-e4fb-442a-a748-c376f5c4345b", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progre
 ss": 100, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12064, "metadata": {}}}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064_updated_name_bob.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064_updated_name_bob.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064_updated_name_bob.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_12064_updated_name_bob.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"server": {"status": "ACTIVE", "updated": "2011-10-11T01:22:04Z", "hostId": "a024053a6201e6c6c12660aab3d8fd879e332e663a5e1fdbc02a0307", "user_id": "rs-reach", "name": "Bob", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12064", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12064", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.30"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe77:32e3"}], "private": [{"version": 4, "addr": "10.182.64.29"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe6e:b7e2"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:45:02Z", "uuid": "ec53630b-e4fb-442a-a748-c376f5c4345b", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress":
  100, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12064, "metadata": {}}}

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1 @@
+{"servers": [{"status": "BUILD", "updated": "2011-10-11T00:50:04Z", "hostId": "912566d83a13fbb357ea3f13c629363d9f7e1ba3f925b49f3d2ab725", "user_id": "rs-reach", "name": "lc-test-2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12065", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12065", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.35"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe47:788a"}], "private": [{"version": 4, "addr": "10.182.64.34"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe60:187d"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:51:39Z", "uuid": "02786501-714e-40af-8342-9c17eccb166d", "accessIPv4": "", "accessIPv6": "", "key_name": null, "pro
 gress": 25, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12065, "metadata": {}}, {"status": "ACTIVE", "updated": "2011-10-11T00:44:20Z", "hostId": "a024053a6201e6c6c12660aab3d8fd879e332e663a5e1fdbc02a0307", "user_id": "rs-reach", "name": "lc-test", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/v1.1/rs-reach-project/servers/12064", "rel": "self"}, {"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/servers/12064", "rel": "bookmark"}], "addresses": {"public": [{"version": 4, "addr": "50.57.94.30"}, {"version": 6, "addr": "2001:4801:7808:52:16:3eff:fe77:32e3"}], "private": [{"version": 4, "addr": "10.182.64.29"}, {"version": 6, "addr": "fec0:4801:7808:52:16:3eff:fe6e:b7e2"}]}, "tenant_id": "rs-reach-project", "image": {"id": "7", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:87
 74/rs-reach-project/images/7", "rel": "bookmark"}]}, "created": "2011-10-11T00:45:02Z", "uuid": "ec53630b-e4fb-442a-a748-c376f5c4345b", "accessIPv4": "", "accessIPv6": "", "key_name": null, "progress": 100, "flavor": {"id": "2", "links": [{"href": "http://alpha.ord.servers.api.rackspacecloud.com:8774/rs-reach-project/flavors/2", "rel": "bookmark"}]}, "config_drive": "", "id": 12064, "metadata": {}}]}
\ No newline at end of file

Added: libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail_EMPTY.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail_EMPTY.json?rev=1183722&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail_EMPTY.json (added)
+++ libcloud/trunk/test/compute/fixtures/openstack_v1.1/_servers_detail_EMPTY.json Sat Oct 15 21:22:57 2011
@@ -0,0 +1,3 @@
+{
+    "servers": []
+}

Modified: libcloud/trunk/test/compute/test_openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_openstack.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_openstack.py (original)
+++ libcloud/trunk/test/compute/test_openstack.py Sat Oct 15 21:22:57 2011
@@ -17,7 +17,12 @@ import unittest
 import httplib
 
 from libcloud.common.types import InvalidCredsError, MalformedResponseError
-from libcloud.compute.drivers.rackspace import OpenStack_1_0_NodeDriver, OpenStack_1_0_Response
+from libcloud.compute.types import Provider
+from libcloud.compute.providers import get_driver
+from libcloud.compute.drivers.openstack import (
+    OpenStack_1_0_NodeDriver, OpenStack_1_0_Response,
+    OpenStack_1_1_NodeDriver
+)
 from libcloud.compute.base import Node, NodeImage, NodeSize
 from libcloud.pricing import set_pricing, clear_pricing_data
 
@@ -28,18 +33,18 @@ from test.compute import TestCaseMixin
 from test.secrets import OPENSTACK_PARAMS
 
 
-class OpenStackResponseTestCase(unittest.TestCase):
+class OpenStack_1_0_ResponseTestCase(unittest.TestCase):
     XML = """<?xml version="1.0" encoding="UTF-8"?><root/>"""
 
     def test_simple_xml_content_type_handling(self):
-        http_response = MockResponse(200, OpenStackResponseTestCase.XML, headers={'content-type': 'application/xml'})
+        http_response = MockResponse(200, OpenStack_1_0_ResponseTestCase.XML, headers={'content-type': 'application/xml'})
         body = OpenStack_1_0_Response(http_response, None).parse_body()
 
         self.assertTrue(hasattr(body, 'tag'), "Body should be parsed as XML")
 
     def test_extended_xml_content_type_handling(self):
         http_response = MockResponse(200,
-                                     OpenStackResponseTestCase.XML,
+                                     OpenStack_1_0_ResponseTestCase.XML,
                                      headers={'content-type': 'application/xml; charset=UTF-8'})
         body = OpenStack_1_0_Response(http_response, None).parse_body()
 
@@ -54,19 +59,22 @@ class OpenStackResponseTestCase(unittest
         self.assertEqual(body, RESPONSE_BODY, "Non-XML body should be returned as is")
 
 
-class OpenStackTests(unittest.TestCase, TestCaseMixin):
+class OpenStack_1_0_Tests(unittest.TestCase, TestCaseMixin):
     should_list_locations = False
 
-    driver_type = OpenStack_1_0_NodeDriver
+    driver_klass = OpenStack_1_0_NodeDriver
     driver_args = OPENSTACK_PARAMS
+    driver_kwargs = {}
 
     @classmethod
     def create_driver(self):
-        return self.driver_type(*self.driver_args)
+        if self is not OpenStack_1_0_FactoryMethodTests:
+            self.driver_type = self.driver_klass
+        return self.driver_type(*self.driver_args, **self.driver_kwargs)
 
     def setUp(self):
-        self.driver_type.connectionCls.conn_classes = (OpenStackMockHttp, OpenStackMockHttp)
-        self.driver_type.connectionCls.auth_url = "https://auth.api.example.com/v1.1/"
+        self.driver_klass.connectionCls.conn_classes = (OpenStackMockHttp, OpenStackMockHttp)
+        self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v1.1/"
         OpenStackMockHttp.type = None
         self.driver = self.create_driver()
         clear_pricing_data()
@@ -287,7 +295,7 @@ class OpenStackTests(unittest.TestCase, 
         if self.driver.api_name != 'openstack':
             return
 
-        pricing = dict((str(i), i) for i in range(1, 9))
+        pricing = dict((str(i), i) for i in range(1, 8))
 
         set_pricing(driver_type='compute', driver_name='openstack',
                     pricing=pricing)
@@ -301,6 +309,22 @@ class OpenStackTests(unittest.TestCase, 
             self.assertEqual(float(size.price), float(pricing[size.id]))
 
 
+class OpenStack_1_0_FactoryMethodTests(OpenStack_1_0_Tests):
+    should_list_locations = False
+
+    driver_klass = OpenStack_1_0_NodeDriver
+    driver_type = get_driver(Provider.OPENSTACK)
+    driver_args = OPENSTACK_PARAMS + ('1.0',)
+
+    def test_factory_method_invalid_version(self):
+        try:
+            self.driver_type(*(OPENSTACK_PARAMS + ('15.5',)))
+        except NotImplementedError:
+            pass
+        else:
+            self.fail('Exception was not thrown')
+
+
 class OpenStackMockHttp(MockHttpTestCase):
     fixtures = ComputeFileFixtures('openstack')
     auth_fixtures = OpenStackFixtures()
@@ -341,14 +365,14 @@ class OpenStackMockHttp(MockHttpTestCase
 
     def _v1_0_slug_images_333111(self, method, url, body, headers):
         if method != "DELETE":
-            raise NotImplemented
+            raise NotImplementedError()
         # this is currently used for deletion of an image
         # as such it should not accept GET/POST
         return(httplib.NO_CONTENT,"","",httplib.responses[httplib.NO_CONTENT])
 
     def _v1_0_slug_images(self, method, url, body, headers):
         if method != "POST":
-            raise NotImplemented
+            raise NotImplementedError()
         # this is currently used for creation of new image with
         # POST request, don't handle GET to avoid possible confusion
         body = self.fixtures.load('v1_slug_images_post.xml')
@@ -375,7 +399,7 @@ class OpenStackMockHttp(MockHttpTestCase
 
     def _v1_0_slug_servers_72258_action(self, method, url, body, headers):
         if method != "POST" or body[:8] != "<reboot ":
-            raise NotImplemented
+            raise NotImplementedError()
         # only used by reboot() right now, but we will need to parse body someday !!!!
         return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
@@ -385,7 +409,7 @@ class OpenStackMockHttp(MockHttpTestCase
 
     def _v1_0_slug_servers_72258(self, method, url, body, headers):
         if method != "DELETE":
-            raise NotImplemented
+            raise NotImplementedError()
         # only used by destroy node()
         return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
@@ -395,7 +419,7 @@ class OpenStackMockHttp(MockHttpTestCase
 
     def _v1_0_slug_shared_ip_groups_5467(self, method, url, body, headers):
         if method != 'DELETE':
-            raise NotImplemented
+            raise NotImplementedError()
         return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT])
 
     def _v1_0_slug_shared_ip_groups(self, method, url, body, headers):
@@ -443,6 +467,260 @@ class OpenStackMockHttp(MockHttpTestCase
     def _v1_1__auth_INTERNAL_SERVER_ERROR(self, method, url, body, headers):
         return (httplib.INTERNAL_SERVER_ERROR, "<h1>500: Internal Server Error</h1>",  {'content-type': 'text/html'}, httplib.responses[httplib.INTERNAL_SERVER_ERROR])
 
+class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin):
+    should_list_locations = False
+
+    driver_klass = OpenStack_1_1_NodeDriver
+    driver_type = OpenStack_1_1_NodeDriver
+    driver_args = OPENSTACK_PARAMS
+    driver_kwargs = {'ex_force_auth_version': '1.0'}
+
+    @classmethod
+    def create_driver(self):
+        if self is not OpenStack_1_1_FactoryMethodTests:
+            self.driver_type = self.driver_klass
+        return self.driver_type(*self.driver_args, **self.driver_kwargs)
+
+    def setUp(self):
+        self.driver_klass.connectionCls.conn_classes = (OpenStack_1_1_MockHttp, OpenStack_1_1_MockHttp)
+        self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v1.0/"
+        OpenStack_1_1_MockHttp.type = None
+        self.driver = self.create_driver()
+        clear_pricing_data()
+        self.node = self.driver.list_nodes()[1]
+
+    def test_list_nodes(self):
+        nodes = self.driver.list_nodes()
+        self.assertEqual(len(nodes), 2)
+        node = nodes[0]
+
+        self.assertEqual('12065', node.id)
+        self.assertEqual('50.57.94.35', node.public_ip[0])
+        self.assertEqual('2001:4801:7808:52:16:3eff:fe47:788a', node.public_ip[1])
+        self.assertEqual('10.182.64.34', node.private_ip[0])
+        self.assertEqual('fec0:4801:7808:52:16:3eff:fe60:187d', node.private_ip[1])
+
+        self.assertEqual(node.extra.get('flavorId'), '2')
+        self.assertEqual(node.extra.get('imageId'), '7')
+        self.assertEqual(node.extra.get('metadata'), {})
+
+    def test_list_sizes(self):
+        sizes = self.driver.list_sizes()
+        self.assertEqual(len(sizes), 8, 'Wrong sizes count')
+
+        for size in sizes:
+            self.assertTrue(isinstance(size.price, float),
+                            'Wrong size price type')
+            self.assertEqual(size.price, 0,
+                             'Size price should be zero by default')
+
+    def test_list_sizes_with_specified_pricing(self):
+
+        pricing = dict((str(i), i*5.0) for i in range(1, 9))
+
+        set_pricing(driver_type='compute', driver_name='openstack', pricing=pricing)
+
+        sizes = self.driver.list_sizes()
+        self.assertEqual(len(sizes), 8, 'Wrong sizes count')
+
+        for size in sizes:
+            self.assertTrue(isinstance(size.price, float),
+                            'Wrong size price type')
+            self.assertEqual(size.price, pricing[size.id],
+                             'Size price should match')
+
+    def test_list_images(self):
+        images = self.driver.list_images()
+        self.assertEqual(len(images), 13, 'Wrong images count')
+
+        image = images[0]
+        self.assertEqual(image.id, '13')
+        self.assertEqual(image.name, 'Windows 2008 SP2 x86 (B24)')
+        self.assertEqual(image.extra['updated'], '2011-08-06T18:14:02Z')
+        self.assertEqual(image.extra['created'], '2011-08-06T18:13:11Z')
+        self.assertEqual(image.extra['status'], 'ACTIVE')
+        self.assertEqual(image.extra['metadata']['os_type'], 'windows')
+
+    def test_create_node(self):
+        image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver)
+        size = NodeSize(1, '256 slice', None, None, None, None, driver=self.driver)
+        node = self.driver.create_node(name='racktest', image=image, size=size)
+        self.assertEqual(node.id, '52415800-8b69-11e0-9b19-734f565bc83b')
+        self.assertEqual(node.name, 'new-server-test')
+        self.assertEqual(node.extra['password'], 'GFf1j9aP')
+        self.assertEqual(node.extra['metadata']['My Server Name'], 'Apache1')
+
+    def test_destroy_node(self):
+        self.assertTrue(self.node.destroy())
+
+    def test_reboot_node(self):
+        self.assertTrue(self.node.reboot())
+
+    def test_ex_set_password(self):
+        try:
+            self.driver.ex_set_password(self.node, 'New1&53jPass')
+        except Exception, e:
+            self.fail('An error was raised: ' + repr(e))
+
+    def test_ex_rebuild(self):
+        image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', driver=self.driver)
+        try:
+            self.driver.ex_rebuild(self.node, image=image)
+        except Exception, e:
+            self.fail('An error was raised: ' + repr(e))
+
+    def test_ex_resize(self):
+        size = NodeSize(1, '256 slice', None, None, None, None,
+                        driver=self.driver)
+        try:
+            self.driver.ex_resize(self.node, size)
+        except Exception, e:
+            self.fail('An error was raised: ' + repr(e))
+
+    def test_ex_confirm_resize(self):
+        try:
+            self.driver.ex_confirm_resize(self.node)
+        except Exception, e:
+            self.fail('An error was raised: ' + repr(e))
+
+    def test_ex_revert_resize(self):
+        try:
+            self.driver.ex_revert_resize(self.node)
+        except Exception, e:
+            self.fail('An error was raised: ' + repr(e))
+
+    def test_ex_save_image(self):
+        try:
+            self.driver.ex_save_image(self.node, 'new_image')
+        except NotImplementedError:
+            pass
+        else:
+            self.fail('An expected error was not raised')
+
+    def test_ex_update_node(self):
+        old_node = Node(
+            id='12064',
+            name=None, state=None, public_ip=None, private_ip=None, driver=self.driver,
+        )
+
+        new_node = self.driver.ex_update_node(old_node, name='Bob')
+
+        self.assertTrue(new_node)
+        self.assertEqual('Bob', new_node.name)
+        self.assertEqual('50.57.94.30', new_node.public_ip[0])
+
+    def test_ex_get_node_details(self):
+        node_id = '12064'
+        node = self.driver.ex_get_node_details(node_id)
+        self.assertEqual(node.id, '12064')
+        self.assertEqual(node.name, 'lc-test')
+
+    def test_ex_get_size(self):
+        size_id = '7'
+        size = self.driver.ex_get_size(size_id)
+        self.assertEqual(size.id, size_id)
+        self.assertEqual(size.name, '15.5GB slice')
+
+    def test_ex_get_image(self):
+        image_id = '13'
+        image = self.driver.ex_get_image(image_id)
+        self.assertEqual(image.id, image_id)
+        self.assertEqual(image.name, 'Windows 2008 SP2 x86 (B24)')
+
+    def test_ex_delete_image(self):
+        image = NodeImage(id='26365521-8c62-11f9-2c33-283d153ecc3a', name='My Backup', driver=self.driver)
+        try:
+            self.driver.ex_delete_image(image)
+        except NotImplementedError:
+            pass
+        else:
+            self.fail('An expected error was not raised')
+
+class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests):
+    should_list_locations = False
+
+    driver_klass = OpenStack_1_1_NodeDriver
+    driver_type = get_driver(Provider.OPENSTACK)
+    driver_args = OPENSTACK_PARAMS + ('1.1',)
+
+
+class OpenStack_1_1_MockHttp(MockHttpTestCase):
+    fixtures = ComputeFileFixtures('openstack_v1.1')
+    auth_fixtures = OpenStackFixtures()
+    json_content_headers = {'content-type': 'application/json; charset=UTF-8'}
+
+    def _v1_0_(self, method, url, body, headers):
+        headers = {
+            'x-auth-token': 'FE011C19-CF86-4F87-BE5D-9229145D7A06',
+            'x-server-management-url': 'https://api.example.com/v1.1/slug',
+        }
+        return (httplib.NO_CONTENT, "", headers, httplib.responses[httplib.NO_CONTENT])
+
+    def _servers_detail(self, method, url, body, headers):
+        body = self.fixtures.load('_servers_detail.json')
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _flavors_detail(self, method, url, body, headers):
+        body = self.fixtures.load('_flavors_detail.json')
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _images_detail(self, method, url, body, headers):
+        body = self.fixtures.load('_images_detail.json')
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _servers(self, method, url, body, headers):
+        body = self.fixtures.load('_servers.json')
+        return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+
+    def _servers_12065_action(self, method, url, body, headers):
+        if method != "POST":
+            self.fail('HTTP method other than POST to action URL')
+
+        return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+
+    def _servers_12064_action(self, method, url, body, headers):
+        if method != "POST":
+            self.fail('HTTP method other than POST to action URL')
+
+        return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+
+    def _servers_12065(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+        else:
+            raise NotImplementedError()
+
+    def _servers_12064(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_servers_12064.json')
+            return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+        elif method == "PUT":
+            body = self.fixtures.load('_servers_12064_updated_name_bob.json')
+            return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+        elif method == "DELETE":
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+        else:
+            raise NotImplementedError()
+
+    def _flavors_7(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_flavors_7.json')
+            return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def _images_13(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_images_13.json')
+            return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def _images_DELETEUUID(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+        else:
+            raise NotImplementedError()
 
 
 if __name__ == '__main__':

Modified: libcloud/trunk/test/compute/test_rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_rackspace.py?rev=1183722&r1=1183721&r2=1183722&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_rackspace.py (original)
+++ libcloud/trunk/test/compute/test_rackspace.py Sat Oct 15 21:22:57 2011
@@ -16,15 +16,16 @@ import sys
 import unittest
 
 from libcloud.compute.drivers.rackspace import RackspaceNodeDriver
-from test.compute.test_openstack import OpenStackTests
+from test.compute.test_openstack import OpenStack_1_0_Tests
 
 from test.secrets import RACKSPACE_PARAMS
 
 
-class RackspaceTests(OpenStackTests):
+class RackspaceTests(OpenStack_1_0_Tests):
     should_list_locations = True
     should_have_pricing = True
 
+    driver_klass = RackspaceNodeDriver
     driver_type = RackspaceNodeDriver
     driver_args = RACKSPACE_PARAMS