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 2012/02/16 08:05:44 UTC

svn commit: r1244862 - in /libcloud/trunk: ./ libcloud/common/ libcloud/compute/ libcloud/compute/drivers/ libcloud/dns/drivers/ libcloud/loadbalancer/drivers/ libcloud/storage/drivers/ test/ test/compute/ test/compute/fixtures/openstack/ test/dns/ tes...

Author: tomaz
Date: Thu Feb 16 07:05:43 2012
New Revision: 1244862

URL: http://svn.apache.org/viewvc?rev=1244862&view=rev
Log:
Make parsing of the Auth API responses in the OpenStack drivers more flexible
and extensible. This patch is part of LIBCLOUD-151 and has been contributed by
Brad Morgan <morgabra at cloudkick dot com>.

Added:
    libcloud/trunk/libcloud/compute/drivers/rackspacenova.py
    libcloud/trunk/test/compute/test_rackspacenova.py
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/common/openstack.py
    libcloud/trunk/libcloud/compute/drivers/openstack.py
    libcloud/trunk/libcloud/compute/drivers/rackspace.py
    libcloud/trunk/libcloud/compute/providers.py
    libcloud/trunk/libcloud/compute/types.py
    libcloud/trunk/libcloud/dns/drivers/rackspace.py
    libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
    libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
    libcloud/trunk/test/compute/fixtures/openstack/_v1_1__auth.json
    libcloud/trunk/test/compute/fixtures/openstack/_v2_0__auth.json
    libcloud/trunk/test/compute/test_deployment.py
    libcloud/trunk/test/compute/test_openstack.py
    libcloud/trunk/test/dns/test_rackspace.py
    libcloud/trunk/test/loadbalancer/test_rackspace.py
    libcloud/trunk/test/secrets.py-dist
    libcloud/trunk/test/storage/test_cloudfiles.py

Modified: libcloud/trunk/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Thu Feb 16 07:05:43 2012
@@ -2,6 +2,26 @@
 
 Changes with Apache Libcloud in development:
 
+  *) General:
+
+    - Make parsing of the Auth API responses in the OpenStack drivers more
+      flexible and extensible.
+
+      Now, every connection class that inherits from the openstack base
+      connection must implement get_endpoint(), who's job is to return the
+      correct endpoint out of the service catalog.
+
+      Note: The openstack.py base driver no longer works by default with
+      Rackspace nova. The default endpoint parsed from the service catalog
+      is the default compute endpoint for devstack.
+      [Brad Morgan]
+
+  *) Compute:
+
+    - Add new RackspaceNovaBeta and RackspaveNovaDfw driver based on the
+      OpenStack.
+      [Brad Morgan]
+
   *) Storage:
 
     - Don't lowercase special header names in the Amazon S3 storage driver. ;

Modified: libcloud/trunk/libcloud/common/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/openstack.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/openstack.py (original)
+++ libcloud/trunk/libcloud/common/openstack.py Thu Feb 16 07:05:43 2012
@@ -29,7 +29,7 @@ try:
 except ImportError:
     import json
 
-AUTH_API_VERSION = 'v1.0'
+AUTH_API_VERSION = '1.1'
 
 __all__ = [
     "OpenStackBaseConnection",
@@ -71,6 +71,7 @@ class OpenStackAuthResponse(Response):
 
         return data
 
+
 class OpenStackAuthConnection(ConnectionUserAndKey):
 
     responseCls = OpenStackAuthResponse
@@ -166,19 +167,19 @@ class OpenStackAuthConnection(Connection
     def authenticate_2_0_with_apikey(self):
         # API Key based authentication uses the RAX-KSKEY extension.
         # https://github.com/openstack/keystone/tree/master/keystone/content/service
-        reqbody = json.dumps({'auth':{'RAX-KSKEY:apiKeyCredentials':{'username':self.user_id, 'apiKey':self.key}}})
+        reqbody = json.dumps({'auth': {'RAX-KSKEY:apiKeyCredentials': {'username': self.user_id, 'apiKey': self.key}}})
         return self.authenticate_2_0_with_body(reqbody)
 
     def authenticate_2_0_with_password(self):
         # Password based authentication is the only 'core' authentication method in Keystone at this time.
         # 'keystone' - http://docs.openstack.org/api/openstack-identity-service/2.0/content/Identity-Service-Concepts-e1362.html
-        reqbody = json.dumps({'auth':{'passwordCredentials':{'username':self.user_id, 'password':self.key}}})
+        reqbody = json.dumps({'auth': {'passwordCredentials': {'username': self.user_id, 'password': self.key}}})
         return self.authenticate_2_0_with_body(reqbody)
 
     def authenticate_2_0_with_body(self, reqbody):
         resp = self.request('/v2.0/tokens/',
                     data=reqbody,
-                    headers={'Content-Type':'application/json'},
+                    headers={'Content-Type': 'application/json'},
                     method='POST')
         if resp.status == httplib.UNAUTHORIZED:
             raise InvalidCredsError()
@@ -202,65 +203,125 @@ class OpenStackAuthConnection(Connection
                 e = sys.exc_info()[1]
                 raise MalformedResponseError('Auth JSON response is missing required elements', e)
 
+
+class OpenStackServiceCatalog(object):
+    """
+    http://docs.openstack.org/api/openstack-identity-service/2.0/content/
+
+    This class should be instanciated with the contents of the 'serviceCatalog'
+    in the auth response. This will do the work of figuring out which services
+    actually exist in the catalog as well as split them up by type, name, and
+    region if available
+    """
+
+    _auth_version = None
+    _service_catalog = None
+
+    def __init__(self, service_catalog, ex_force_auth_version=None):
+        self._auth_version = ex_force_auth_version or AUTH_API_VERSION
+        self._service_catalog = {}
+
+        # check this way because there are a couple of different 2.0_* auth types
+        if '2.0' in self._auth_version:
+            self._parse_auth_v2(service_catalog)
+        elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
+            self._parse_auth_v1(service_catalog)
+        else:
+            raise LibcloudError('auth version "%s" not supported' % (self._auth_version))
+
+    def get_endpoint(self, service_type=None, name=None, region=None):
+
+        if '2.0' in self._auth_version:
+            endpoint = self._service_catalog.get(service_type, {}).get(name, {}).get(region, [])
+        elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
+            endpoint = self._service_catalog.get(name, {}).get(region, [])
+
+        # ideally an endpoint either isn't found or only one match is found.
+        if len(endpoint) == 1:
+            return endpoint[0]
+        else:
+            return {}
+
+    def _parse_auth_v1(self, service_catalog):
+
+        for service, endpoints in service_catalog.items():
+
+            self._service_catalog[service] = {}
+
+            for endpoint in endpoints:
+                region = endpoint.get('region')
+
+                if region not in self._service_catalog[service]:
+                    self._service_catalog[service][region] = []
+
+                self._service_catalog[service][region].append(endpoint)
+
+    def _parse_auth_v2(self, service_catalog):
+
+        for service in service_catalog:
+
+            service_type = service['type']
+            service_name = service.get('name', None)
+
+            if service_type not in self._service_catalog:
+                self._service_catalog[service_type] = {}
+
+            if service_name not in self._service_catalog[service_type]:
+                self._service_catalog[service_type][service_name] = {}
+
+            for endpoint in service.get('endpoints', []):
+                region = endpoint.get('region', None)
+                if region not in self._service_catalog[service_type][service_name]:
+                    self._service_catalog[service_type][service_name][region] = []
+
+                self._service_catalog[service_type][service_name][region].append(endpoint)
+
+
 class OpenStackBaseConnection(ConnectionUserAndKey):
 
     auth_url = None
+    auth_token = None
+    service_catalog = None
 
     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):
-        self.server_url = None
-        self.cdn_management_url = None
-        self.storage_url = None
-        self.lb_url = None
-        self.auth_token = None
-        self._force_base_url = ex_force_base_url
+
+        self._ex_force_base_url = ex_force_base_url
         self._ex_force_auth_url = ex_force_auth_url
         self._auth_version = ex_force_auth_version
 
         if not self._auth_version:
-            self._auth_version = '1.1'
+            self._auth_version = AUTH_API_VERSION
 
         super(OpenStackBaseConnection, self).__init__(
             user_id, key, secure=secure)
 
+    def get_endpoint(self):
+        """
+        Every openstack driver must have a connection class that subclasses this
+        class and it must implement this method.
+
+        @returns: url of the relevant endpoint for the driver
+
+        Example implementation:
+        ep = self.service_catalog.get_endpoint(service_type='compute',
+                                               name='ServiceName',
+                                               region='US1')
+        return ep['publicURL']
+        """
+        raise NotImplementedError
+
     def add_default_headers(self, headers):
         headers['X-Auth-Token'] = self.auth_token
         headers['Accept'] = self.accept_format
         return headers
 
     def morph_action_hook(self, action):
-        if self._force_base_url:
-            _, _, request_path, _, _, _ = urlparse.urlparse(self._force_base_url)
-            return request_path + action
-
-        value = getattr(self, self._url_key, None)
-        if not value:
-            self._populate_hosts_and_request_paths()
-        request_path = getattr(self, '__request_path_%s' % (self._url_key), '')
-        action = request_path + action
-
-        return action
-
-    @property
-    def base_url(self):
-        return self._get_base_url(url_key=self._url_key)
-
-    def _get_base_url(self, url_key):
-        value = getattr(self, url_key, None)
-        if not value:
-            self._populate_hosts_and_request_paths()
-            value = getattr(self, url_key, None)
-        if self._force_base_url != None:
-            value = self._force_base_url
-        return value
-
-    def _get_default_region(self, arr):
-        if len(arr):
-            return arr[0]['publicURL']
-        return None
+        self._populate_hosts_and_request_paths()
+        return super(OpenStackBaseConnection, self).morph_action_hook(action)
 
     def request(self, **kwargs):
         self._populate_hosts_and_request_paths()
@@ -269,9 +330,9 @@ class OpenStackBaseConnection(Connection
     def _populate_hosts_and_request_paths(self):
         """
         OpenStack 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.
+        after an initial authentication request.
         """
+
         if not self.auth_token:
             aurl = self.auth_url
 
@@ -288,39 +349,8 @@ class OpenStackBaseConnection(Connection
 
             self.auth_token = osa.auth_token
 
-            # TODO: Multi-region support
-            if self._auth_version in ['2.0', '2.0_apikey', '2.0_password']:
-                self.tenant_ids = {}
-
-                for service in osa.urls:
-                    service_type = service['type']
-                    if service_type == 'compute':
-                        self.server_url = self._get_default_region(service.get('endpoints', []))
-
-                    self.tenant_ids[service_type] = service['endpoints'][0]['tenantId']
-            elif self._auth_version in ['1.1', '1.0']:
-                self.server_url = self._get_default_region(osa.urls.get('cloudServers', []))
-                self.cdn_management_url = self._get_default_region(osa.urls.get('cloudFilesCDN', []))
-                self.storage_url = self._get_default_region(osa.urls.get('cloudFiles', []))
-                # TODO: this is even more broken, the service catalog does NOT show load
-                # balanacers :(  You must hard code in the Rackspace Load balancer URLs...
-                self.lb_url = self.server_url.replace("servers", "ord.loadbalancers")
-                self.dns_url = self.server_url.replace("servers", "dns")
-            else:
-                raise LibcloudError('auth version "%s" not supported' % (self._auth_version))
-
-            for key in ['server_url', 'storage_url', 'cdn_management_url',
-                        'lb_url', 'dns_url']:
-                base_url = None
-                if self._force_base_url != None:
-                    base_url = self._force_base_url
-                else:
-                    base_url = getattr(self, key)
-
-                scheme, server, request_path, param, query, fragment = (
-                    urlparse.urlparse(base_url))
-                # Set host to where we want to make further requests to
-                setattr(self, '__%s' % (key), server+request_path)
-                setattr(self, '__request_path_%s' % (key), request_path)
+            # pull out and parse the service catalog
+            self.service_catalog = OpenStackServiceCatalog(osa.urls, ex_force_auth_version=self._auth_version)
 
-            (self.host, self.port, self.secure, self.request_path) = self._tuple_from_url(self.base_url)
+            # Set up connection info
+            (self.host, self.port, self.secure, self.request_path) = self._tuple_from_url(self._ex_force_base_url or self.get_endpoint())

Modified: libcloud/trunk/libcloud/compute/drivers/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/openstack.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/openstack.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/openstack.py Thu Feb 16 07:05:43 2012
@@ -37,7 +37,7 @@ import base64
 from xml.etree import ElementTree as ET
 
 from libcloud.common.openstack import OpenStackBaseConnection
-from libcloud.common.types import MalformedResponseError
+from libcloud.common.types import MalformedResponseError, LibcloudError
 from libcloud.compute.types import NodeState, Provider
 from libcloud.compute.base import NodeSize, NodeImage
 from libcloud.compute.base import NodeDriver, Node, NodeLocation
@@ -121,6 +121,17 @@ class OpenStackResponse(Response):
 
 class OpenStackComputeConnection(OpenStackBaseConnection):
 
+    def get_endpoint(self):
+
+        # default config for http://devstack.org/
+        ep = self.service_catalog.get_endpoint(service_type='compute',
+                                               name='nova',
+                                               region='RegionOne')
+        if 'publicURL' in ep:
+            return ep['publicURL']
+
+        raise LibcloudError('Could not find specified endpoint')
+
     def request(self, action, params=None, data='', headers=None,
                 method='GET'):
         if not headers:
@@ -245,7 +256,6 @@ class OpenStack_1_0_Response(OpenStackRe
 
 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'
@@ -766,7 +776,6 @@ class OpenStack_1_1_Response(OpenStackRe
 
 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'
 

Modified: libcloud/trunk/libcloud/compute/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/rackspace.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/rackspace.py Thu Feb 16 07:05:43 2012
@@ -15,7 +15,7 @@
 """
 Rackspace driver
 """
-from libcloud.compute.types import Provider
+from libcloud.compute.types import Provider, LibcloudError
 from libcloud.compute.base import NodeLocation
 from libcloud.compute.drivers.openstack import OpenStack_1_0_Connection, OpenStack_1_0_NodeDriver, OpenStack_1_0_Response
 
@@ -32,6 +32,20 @@ class RackspaceConnection(OpenStack_1_0_
     auth_url = AUTH_URL_US
     XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
 
+    def get_endpoint(self):
+
+        ep = {}
+        if '2.0' in self._auth_version:
+            ep = self.service_catalog.get_endpoint(service_type='compute',
+                                                     name='cloudServers')
+        elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
+            ep = self.service_catalog.get_endpoint(name='cloudServers')
+
+        if 'publicURL' in ep:
+            return ep['publicURL']
+
+        raise LibcloudError('Could not find specified endpoint')
+
 
 class RackspaceNodeDriver(OpenStack_1_0_NodeDriver):
     name = 'Rackspace'

Added: libcloud/trunk/libcloud/compute/drivers/rackspacenova.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/rackspacenova.py?rev=1244862&view=auto
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/rackspacenova.py (added)
+++ libcloud/trunk/libcloud/compute/drivers/rackspacenova.py Thu Feb 16 07:05:43 2012
@@ -0,0 +1,70 @@
+# 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.
+"""
+Rackspace driver
+"""
+from libcloud.compute.providers import Provider
+from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection, OpenStack_1_1_NodeDriver
+from libcloud.common.types import LibcloudError
+
+
+class RackspaceNovaConnection(OpenStack_1_1_Connection):
+    get_endpoint_args = {}
+
+    def get_endpoint(self):
+
+        if not self.get_endpoint_args:
+            raise LibcloudError('RackspaceNovaConnection must have get_endpoint_args set')
+
+        # Only support auth 2.0_*
+        if '2.0' in self._auth_version:
+            ep = self.service_catalog.get_endpoint(**self.get_endpoint_args)
+        else:
+            raise LibcloudError('Auth version "%s" not supported' % (self._auth_version))
+
+        # It's possible to authenticate but the service catalog not have the correct
+        # endpoint for this driver, so we throw here.
+        if 'publicURL' in ep:
+            return ep['publicURL']
+        else:
+            raise LibcloudError('Could not find specified endpoint')
+
+
+class RackspaceNovaBetaConnection(RackspaceNovaConnection):
+
+    get_endpoint_args = {'service_type': 'compute',
+                         'name': 'cloudServersPreprod',
+                         'region': 'DFW'}
+
+
+class RackspaceNovaDfwConnection(RackspaceNovaConnection):
+
+    get_endpoint_args = {'service_type': 'compute',
+                         'name': 'cloudServersOpenStack',
+                         'region': 'DFW'}
+
+
+class RackspaceNovaDfwNodeDriver(OpenStack_1_1_NodeDriver):
+    name = 'RackspaceNovadfw'
+    connectionCls = RackspaceNovaDfwConnection
+    type = Provider.RACKSPACE_NOVA_DFW
+    api_name = 'rackspacenovadfw'
+
+
+class RackspaceNovaBetaNodeDriver(OpenStack_1_1_NodeDriver):
+    name = 'RackspaceNovaBeta'
+    connectionCls = RackspaceNovaBetaConnection
+    type = Provider.RACKSPACE_NOVA_BETA
+    api_name = 'rackspacenovabeta'

Modified: libcloud/trunk/libcloud/compute/providers.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/providers.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/providers.py (original)
+++ libcloud/trunk/libcloud/compute/providers.py Thu Feb 16 07:05:43 2012
@@ -100,8 +100,13 @@ DRIVERS = {
     Provider.TERREMARK:
         ('libcloud.compute.drivers.vcloud', 'TerremarkDriver'),
     Provider.CLOUDSTACK:
-        ('libcloud.compute.drivers.cloudstack', 'CloudStackNodeDriver')
+        ('libcloud.compute.drivers.cloudstack', 'CloudStackNodeDriver'),
+    Provider.RACKSPACE_NOVA_BETA:
+        ('libcloud.compute.drivers.rackspacenova', 'RackspaceNovaBetaNodeDriver'),
+    Provider.RACKSPACE_NOVA_DFW:
+        ('libcloud.compute.drivers.rackspacenova', 'RackspaceNovaDfwNodeDriver')
 }
 
+
 def get_driver(provider):
     return _get_provider_driver(DRIVERS, provider)

Modified: libcloud/trunk/libcloud/compute/types.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/types.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/types.py (original)
+++ libcloud/trunk/libcloud/compute/types.py Thu Feb 16 07:05:43 2012
@@ -30,6 +30,8 @@ __all__ = [
     "InvalidCredsError",
     "InvalidCredsException"
     ]
+
+
 class Provider(object):
     """
     Defines for each of the supported providers
@@ -59,6 +61,7 @@ class Provider(object):
     @cvar: EC2_US_WEST_OREGON: Amazon AWS US West 2 (Oregon)
     @cvar CLOUDSTACK: CloudStack
     @cvar CLOUDSIGMA_US: CloudSigma US Las Vegas
+    @cvar RACKSPACE_NOVA_BETA: Rackspace Nova Private Beta (ORD)
     """
     DUMMY = 0
     EC2 = 1  # deprecated name
@@ -102,6 +105,9 @@ class Provider(object):
     CLOUDSTACK = 37
     CLOUDSIGMA_US = 38
     EC2_SA_EAST = 39
+    RACKSPACE_NOVA_BETA = 40
+    RACKSPACE_NOVA_DFW = 41
+
 
 class NodeState(object):
     """
@@ -119,6 +125,7 @@ class NodeState(object):
     PENDING = 3
     UNKNOWN = 4
 
+
 class Architecture(object):
     """
     Image and size architectures.
@@ -129,6 +136,7 @@ class Architecture(object):
     I386 = 0
     X86_X64 = 1
 
+
 class DeploymentError(LibcloudError):
     """
     Exception used when a Deployment Task failed.

Modified: libcloud/trunk/libcloud/dns/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/dns/drivers/rackspace.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/dns/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/dns/drivers/rackspace.py Thu Feb 16 07:05:43 2012
@@ -72,7 +72,6 @@ class RackspaceDNSConnection(OpenStack_1
     """
 
     responseCls = RackspaceDNSResponse
-    _url_key = 'dns_url'
     XML_NAMESPACE = None
     poll_interval = 2.5
     timeout = 30
@@ -91,6 +90,25 @@ class RackspaceDNSConnection(OpenStack_1
 
         return status == 'COMPLETED'
 
+    def get_endpoint(self):
+        """
+        FIXME:
+        Dirty, dirty hack. DNS doesn't get returned in the auth 1.1 service
+        catalog, so we build it from the servers url.
+        """
+
+        if self._auth_version == "1.1":
+            ep = self.service_catalog.get_endpoint(name="cloudServers")
+
+            if 'publicURL' in ep:
+                return ep['publicURL'].replace("servers", "dns")
+            else:
+                raise LibcloudError('Could not find specified endpoint')
+
+        else:
+            raise LibcloudError("Auth version %s not supported" % \
+                self._auth_version)
+
 
 class RackspaceUSDNSConnection(RackspaceDNSConnection):
     auth_url = AUTH_URL_US

Modified: libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py Thu Feb 16 07:05:43 2012
@@ -226,7 +226,6 @@ class RackspaceAccessRule(object):
 class RackspaceConnection(OpenStackBaseConnection, PollingConnection):
     responseCls = RackspaceResponse
     auth_url = AUTH_URL_US
-    _url_key = "lb_url"
     poll_interval = 2
     timeout = 80
 
@@ -263,6 +262,25 @@ class RackspaceConnection(OpenStackBaseC
 
         return state == 'ACTIVE'
 
+    def get_endpoint(self):
+        """
+        FIXME:
+        Dirty, dirty hack. Loadbalancers so not show up in the auth 1.1 service
+        catalog, so we build it from the servers url.
+        """
+
+        if self._auth_version == "1.1":
+            ep = self.service_catalog.get_endpoint(name="cloudServers")
+
+            if 'publicURL' in ep:
+                return ep['publicURL'].replace("servers", "ord.loadbalancers")
+            else:
+                raise LibcloudError('Could not find specified endpoint')
+
+        else:
+            raise LibcloudError("Auth version %s not supported" % \
+                self._auth_version)
+
 
 class RackspaceUKConnection(RackspaceConnection):
     auth_url = AUTH_URL_UK

Modified: libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (original)
+++ libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Thu Feb 16 07:05:43 2012
@@ -86,9 +86,11 @@ class CloudFilesResponse(Response):
 
         return data
 
+
 class CloudFilesRawResponse(CloudFilesResponse, RawResponse):
     pass
 
+
 class CloudFilesConnection(OpenStackBaseConnection):
     """
     Base connection class for the Cloudfiles driver.
@@ -97,7 +99,6 @@ class CloudFilesConnection(OpenStackBase
     auth_url = AUTH_URL_US
     responseCls = CloudFilesResponse
     rawResponseCls = CloudFilesRawResponse
-    _url_key = "storage_url"
 
     def __init__(self, user_id, key, secure=True, **kwargs):
         super(CloudFilesConnection, self).__init__(user_id, key, secure=secure,
@@ -105,6 +106,31 @@ class CloudFilesConnection(OpenStackBase
         self.api_version = API_VERSION
         self.accept_format = 'application/json'
 
+    def get_endpoint(self, cdn_request=False):
+
+        # First, we parse out both files and cdn endpoints for each auth version
+        if '2.0' in self._auth_version:
+            ep = self.service_catalog.get_endpoint(service_type='object-store',
+                                                   name='cloudFiles',
+                                                   region='ORD')
+            cdn_ep = self.service_catalog.get_endpoint(service_type='object-store',
+                                                       name='cloudFilesCDN',
+                                                       region='ORD')
+        elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version):
+            ep = self.service_catalog.get_endpoint(name='cloudFiles',
+                                                   region='ORD')
+            cdn_ep = self.service_catalog.get_endpoint(name='cloudFilesCDN',
+                                                       region='ORD')
+
+        # if this is a CDN request, return the cdn url instead
+        if cdn_request:
+            ep = cdn_ep
+
+        if 'publicURL' in ep:
+            return ep['publicURL']
+        else:
+            raise LibcloudError('Could not find specified endpoint')
+
     def request(self, action, params=None, data='', headers=None, method='GET',
                 raw=False, cdn_request=False):
         if not headers:
@@ -112,14 +138,17 @@ class CloudFilesConnection(OpenStackBase
         if not params:
             params = {}
 
-        if cdn_request:
-            host = self._get_host(url_key='cdn_management_url')
-        else:
-            host = None
+        # FIXME: Massive hack.
+        # This driver dynamically changes the url in it's connection, based on arguments
+        # passed to request(). As such, we have to manually check and reset connection
+        # params each request
+        self._populate_hosts_and_request_paths()
+        ep = self.get_endpoint(cdn_request)
+        (self.host, self.port, self.secure, self.request_path) = self._ex_force_base_url or self._tuple_from_url(ep)
 
         params['format'] = 'json'
 
-        if method in [ 'POST', 'PUT' ]:
+        if method in ['POST', 'PUT']:
             headers.update({'Content-Type': 'application/json; charset=UTF-8'})
 
         return super(CloudFilesConnection, self).request(

Modified: libcloud/trunk/test/compute/fixtures/openstack/_v1_1__auth.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/_v1_1__auth.json?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/_v1_1__auth.json (original)
+++ libcloud/trunk/test/compute/fixtures/openstack/_v1_1__auth.json Thu Feb 16 07:05:43 2012
@@ -1 +1,31 @@
-{"auth":{"token":{"id":"603d2bd9-f45c-4583-b91c-2c8eac0b5654","expires":"2011-09-18T02:44:17.000-05:00"},"serviceCatalog":{"cloudFilesCDN":[{"region":"ORD","publicURL":"https:\/\/cdn2.clouddrive.com\/v1\/MossoCloudFS","v1Default":true}],"cloudFiles":[{"region":"ORD","publicURL":"https:\/\/storage101.ord1.clouddrive.com\/v1\/MossoCloudFS","v1Default":true,"internalURL":"https:\/\/snet-storage101.ord1.clouddrive.com\/v1\/MossoCloudFS"}],"cloudServers":[{"publicURL":"https:\/\/servers.api.rackspacecloud.com\/v1.0\/slug","v1Default":true}]}}}
+{
+    "auth": {
+        "token": {
+            "id": "603d2bd9-f45c-4583-b91c-2c8eac0b5654",
+            "expires": "2011-09-18T02:44:17.000-05:00"
+        },
+        "serviceCatalog": {
+            "cloudFilesCDN": [
+                {
+                    "region": "ORD",
+                    "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS",
+                    "v1Default": true
+                }
+            ],
+            "cloudFiles": [
+                {
+                    "region": "ORD",
+                    "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS",
+                    "v1Default": true,
+                    "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS"
+                }
+            ],
+            "cloudServers": [
+                {
+                    "publicURL": "https://servers.api.rackspacecloud.com/v1.0/slug",
+                    "v1Default": true
+                }
+            ]
+        }
+    }
+}

Modified: libcloud/trunk/test/compute/fixtures/openstack/_v2_0__auth.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/_v2_0__auth.json?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/_v2_0__auth.json (original)
+++ libcloud/trunk/test/compute/fixtures/openstack/_v2_0__auth.json Thu Feb 16 07:05:43 2012
@@ -47,6 +47,45 @@
                 ],
                 "name": "cloudServers",
                 "type": "compute"
+            },
+            {
+                "endpoints": [
+                    {
+                        "region": "RegionOne",
+                        "tenantId": "1337",
+                        "publicURL": "https://127.0.0.1/v2/1337",
+                        "versionInfo": "https://127.0.0.1/v2/",
+                        "versionList": "https://127.0.0.1/",
+                        "versionId": "2"
+                    }
+                ],
+                "name": "nova",
+                "type": "compute"
+            },
+            {
+                "endpoints": [
+                    {
+                        "region": "DFW",
+                        "tenantId": "613469",
+                        "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/1337",
+                        "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2/",
+                        "versionList": "https://dfw.servers.api.rackspacecloud.com/",
+                        "versionId": "2"
+                    }
+                ],
+                "name": "cloudServersOpenStack",
+                "type": "compute"
+            },
+            {
+                "endpoints": [
+                    {
+                        "region": "DFW",
+                        "tenantId": "1337",
+                        "publicURL": "https://preprod.dfw.servers.api.rackspacecloud.com/v2/1337"
+                    }
+                ],
+                "name": "cloudServersPreprod",
+                "type": "compute"
             }
         ],
         "user": {

Modified: libcloud/trunk/test/compute/test_deployment.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_deployment.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_deployment.py (original)
+++ libcloud/trunk/test/compute/test_deployment.py Thu Feb 16 07:05:43 2012
@@ -34,10 +34,12 @@ from mock import Mock, patch
 
 from test.secrets import RACKSPACE_PARAMS
 
+
 class MockDeployment(Deployment):
     def run(self, node, client):
         return node
 
+
 class MockClient(BaseSSHClient):
     def __init__(self, *args, **kwargs):
         self.stdout = ''
@@ -53,12 +55,15 @@ class MockClient(BaseSSHClient):
     def delete(self, name):
         return True
 
+
 class DeploymentTests(unittest.TestCase):
 
     def setUp(self):
         Rackspace.connectionCls.conn_classes = (None, RackspaceMockHttp)
         RackspaceMockHttp.type = None
         self.driver = Rackspace(*RACKSPACE_PARAMS)
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
         self.driver.features = {'create_node': ['generates_password']}
         self.node = Node(id=12345, name='test', state=NodeState.RUNNING,
                    public_ips=['1.2.3.4'], private_ips=['1.2.3.5'],
@@ -145,7 +150,6 @@ class DeploymentTests(unittest.TestCase)
         else:
             self.fail('Exception was not thrown')
 
-
     def test_wait_until_running_running_node_missing_from_list_nodes(self):
         RackspaceMockHttp.type = 'MISSING'
 
@@ -170,7 +174,6 @@ class DeploymentTests(unittest.TestCase)
         else:
             self.fail('Exception was not thrown')
 
-
     def test_ssh_client_connect_success(self):
         mock_ssh_client = Mock()
         mock_ssh_client.return_value = None
@@ -328,6 +331,7 @@ class DeploymentTests(unittest.TestCase)
         node = self.driver.deploy_node(deploy=Mock())
         self.assertEqual(self.node.id, node.id)
 
+
 class RackspaceMockHttp(MockHttp):
 
     fixtures = ComputeFileFixtures('openstack')

Modified: libcloud/trunk/test/compute/test_openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_openstack.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_openstack.py (original)
+++ libcloud/trunk/test/compute/test_openstack.py Thu Feb 16 07:05:43 2012
@@ -14,7 +14,6 @@
 # limitations under the License.
 import sys
 import unittest
-import types
 
 try:
     import simplejson as json
@@ -82,16 +81,26 @@ class OpenStack_1_0_Tests(unittest.TestC
         return self.driver_type(*self.driver_args, **self.driver_kwargs)
 
     def setUp(self):
+        # monkeypatch get_endpoint because the base openstack driver doesn't actually
+        # work with old devstack but this class/tests are still used by the rackspace
+        # driver
+        def get_endpoint(*args, **kwargs):
+            return "https://servers.api.rackspacecloud.com/v1.0/slug"
+        self.driver_klass.connectionCls.get_endpoint = get_endpoint
+
         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()
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
         clear_pricing_data()
 
     def test_auth(self):
         OpenStackMockHttp.type = 'UNAUTHORIZED'
         try:
             self.driver = self.create_driver()
+            self.driver.list_nodes()
         except InvalidCredsError:
             e = sys.exc_info()[1]
             self.assertEqual(True, isinstance(e, InvalidCredsError))
@@ -102,6 +111,7 @@ class OpenStack_1_0_Tests(unittest.TestC
         OpenStackMockHttp.type = 'UNAUTHORIZED_MISSING_KEY'
         try:
             self.driver = self.create_driver()
+            self.driver.list_nodes()
         except MalformedResponseError:
             e = sys.exc_info()[1]
             self.assertEqual(True, isinstance(e, MalformedResponseError))
@@ -112,6 +122,7 @@ class OpenStack_1_0_Tests(unittest.TestC
         OpenStackMockHttp.type = 'INTERNAL_SERVER_ERROR'
         try:
             self.driver = self.create_driver()
+            self.driver.list_nodes()
         except MalformedResponseError:
             e = sys.exc_info()[1]
             self.assertEqual(True, isinstance(e, MalformedResponseError))
@@ -384,7 +395,7 @@ class OpenStackMockHttp(MockHttpTestCase
             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])
+        return(httplib.NO_CONTENT, "", "", httplib.responses[httplib.NO_CONTENT])
 
     def _v1_0_slug_images(self, method, url, body, headers):
         if method != "POST":
@@ -485,13 +496,14 @@ 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'}
+    driver_kwargs = {'ex_force_auth_version': '2.0'}
 
     @classmethod
     def create_driver(self):
@@ -500,13 +512,27 @@ class OpenStack_1_1_Tests(unittest.TestC
         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_klass.connectionCls.conn_classes = (OpenStack_2_0_MockHttp, OpenStack_2_0_MockHttp)
+        self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/"
+        OpenStack_2_0_MockHttp.type = None
         self.driver = self.create_driver()
+
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
         clear_pricing_data()
         self.node = self.driver.list_nodes()[1]
 
+    def test_ex_force_base_url(self):
+        # change base url and trash the current auth token so we can re-authenticate
+        self.driver.connection._ex_force_base_url = 'http://ex_force_base_url.com:666/forced_url'
+        self.driver.connection.auth_token = None
+        self.driver.connection._populate_hosts_and_request_paths()
+
+        # assert that we use the base url and not the auth url
+        self.assertEqual(self.driver.connection.host, 'ex_force_base_url.com')
+        self.assertEqual(self.driver.connection.port, '666')
+        self.assertEqual(self.driver.connection.request_path, '/forced_url')
+
     def test_list_nodes(self):
         nodes = self.driver.list_nodes()
         self.assertEqual(len(nodes), 2)
@@ -534,9 +560,9 @@ class OpenStack_1_1_Tests(unittest.TestC
 
     def test_list_sizes_with_specified_pricing(self):
 
-        pricing = dict((str(i), i*5.0) for i in range(1, 9))
+        pricing = dict((str(i), i * 5.0) for i in range(1, 9))
 
-        set_pricing(driver_type='compute', driver_name='openstack', pricing=pricing)
+        set_pricing(driver_type='compute', driver_name=self.driver.api_name, pricing=pricing)
 
         sizes = self.driver.list_sizes()
         self.assertEqual(len(sizes), 8, 'Wrong sizes count')
@@ -544,6 +570,7 @@ class OpenStack_1_1_Tests(unittest.TestC
         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')
 
@@ -687,13 +714,15 @@ class OpenStack_1_1_Tests(unittest.TestC
         self.assertEqual(image_id, '1d4a8ea9-aae7-4242-a42d-5ff4702f2f14')
         self.assertEqual(image_id_two, '13')
 
+
 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',)
-    driver_kwargs = {'ex_force_auth_version': '1.0'}
+    driver_kwargs = {'ex_force_auth_version': '2.0'}
+
 
 class OpenStack_1_1_MockHttp(MockHttpTestCase):
     fixtures = ComputeFileFixtures('openstack_v1.1')
@@ -815,32 +844,22 @@ class OpenStack_1_1_MockHttp(MockHttpTes
         else:
             raise NotImplementedError()
 
-class OpenStack_1_1_Auth_2_0_MockHttp(OpenStack_1_1_MockHttp):
-    fixtures = ComputeFileFixtures('openstack_v1.1')
-    auth_fixtures = OpenStackFixtures()
-    json_content_headers = {'content-type': 'application/json; charset=UTF-8'}
 
+# This exists because the nova compute url in devstack has v2 in there but the v1.1 fixtures
+# work fine.
+class OpenStack_2_0_MockHttp(OpenStack_1_1_MockHttp):
     def __init__(self, *args, **kwargs):
-        super(OpenStack_1_1_Auth_2_0_MockHttp, self).__init__(*args, **kwargs)
+        super(OpenStack_2_0_MockHttp, self).__init__(*args, **kwargs)
 
-        # TODO Figure out why 1.1 tests are using some 1.0 endpoints
-        methods1 = OpenStackMockHttp.__dict__
-        methods2 = OpenStack_1_1_MockHttp.__dict__
+        methods1 = OpenStack_1_1_MockHttp.__dict__
 
-        names1 = [m for m in methods1 if m.find('_v1_0') == 0]
-        names2 = [m for m in methods2 if m.find('_v1_1') == 0]
+        names1 = [m for m in methods1 if m.find('_v1_1') == 0]
 
         for name in names1:
             method = methods1[name]
-            new_name = name.replace('_v1_0_slug_', '_v1_0_1337_')
+            new_name = name.replace('_v1_1_slug_', '_v2_1337_')
             setattr(self, new_name, method_type(method, self,
-                OpenStack_1_1_Auth_2_0_MockHttp))
-
-        for name in names2:
-            method = methods2[name]
-            new_name = name.replace('_v1_1_slug_', '_v1_0_1337_')
-            setattr(self, new_name, method_type(method, self,
-                OpenStack_1_1_Auth_2_0_MockHttp))
+                OpenStack_2_0_MockHttp))
 
 
 class OpenStack_1_1_Auth_2_0_Tests(OpenStack_1_1_Tests):
@@ -849,24 +868,14 @@ class OpenStack_1_1_Auth_2_0_Tests(OpenS
 
     def setUp(self):
         self.driver_klass.connectionCls.conn_classes = \
-                (OpenStack_1_1_Auth_2_0_MockHttp, OpenStack_1_1_Auth_2_0_MockHttp)
+                (OpenStack_2_0_MockHttp, OpenStack_2_0_MockHttp)
         self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/"
         OpenStack_1_1_MockHttp.type = None
         self.driver = self.create_driver()
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
         clear_pricing_data()
         self.node = self.driver.list_nodes()[1]
 
-        server_url = 'https://servers.api.rackspacecloud.com/v1.0/1337'
-        auth_token = 'aaaaaaaaaaaa-bbb-cccccccccccccc'
-        tenant_compute = '1337'
-        tenant_object_store = 'MossoCloudFS_11111-111111111-1111111111-1111111'
-
-        self.assertEqual(self.driver.connection.server_url, server_url)
-        self.assertEqual(self.driver.connection.auth_token, auth_token)
-        self.assertEqual(self.driver.connection.tenant_ids,
-              {'compute': tenant_compute, 'object-store': tenant_object_store})
-
-
-
 if __name__ == '__main__':
     sys.exit(unittest.main())

Added: libcloud/trunk/test/compute/test_rackspacenova.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_rackspacenova.py?rev=1244862&view=auto
==============================================================================
--- libcloud/trunk/test/compute/test_rackspacenova.py (added)
+++ libcloud/trunk/test/compute/test_rackspacenova.py Thu Feb 16 07:05:43 2012
@@ -0,0 +1,92 @@
+# 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 sys
+import unittest
+
+from libcloud.utils.py3 import method_type
+
+from libcloud.compute.drivers.rackspacenova import RackspaceNovaBetaNodeDriver, \
+                                                   RackspaceNovaDfwNodeDriver
+from test.compute.test_openstack import OpenStack_1_1_Tests, OpenStack_1_1_MockHttp
+from libcloud.pricing import clear_pricing_data
+
+from test.secrets import RACKSPACE_NOVA_PARAMS
+
+
+class RackspaceNovaMockHttp(OpenStack_1_1_MockHttp):
+    def __init__(self, *args, **kwargs):
+        super(RackspaceNovaMockHttp, self).__init__(*args, **kwargs)
+
+        methods1 = OpenStack_1_1_MockHttp.__dict__
+
+        names1 = [m for m in methods1 if m.find('_v1_1') == 0]
+
+        for name in names1:
+            method = methods1[name]
+            new_name = name.replace('_v1_1_slug_', '_v2_1337_')
+            setattr(self, new_name, method_type(method, self,
+                RackspaceNovaMockHttp))
+
+
+class RackspaceNovaBetaTests(OpenStack_1_1_Tests):
+
+    driver_klass = RackspaceNovaBetaNodeDriver
+    driver_type = RackspaceNovaBetaNodeDriver
+    driver_args = RACKSPACE_NOVA_PARAMS + ('1.1',)
+    driver_kwargs = {'ex_force_auth_version': '2.0'}
+
+    @classmethod
+    def create_driver(self):
+        return self.driver_type(*self.driver_args, **self.driver_kwargs)
+
+    def setUp(self):
+        self.driver_klass.connectionCls.conn_classes = (RackspaceNovaMockHttp, RackspaceNovaMockHttp)
+        self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/"
+        self.driver = self.create_driver()
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
+        clear_pricing_data()
+        self.node = self.driver.list_nodes()[1]
+
+    def test_service_catalog(self):
+        self.assertEqual('https://preprod.dfw.servers.api.rackspacecloud.com/v2/1337', self.driver.connection.get_endpoint())
+
+
+class RackspaceNovaDfwTests(OpenStack_1_1_Tests):
+
+    driver_klass = RackspaceNovaDfwNodeDriver
+    driver_type = RackspaceNovaDfwNodeDriver
+    driver_args = RACKSPACE_NOVA_PARAMS + ('1.1',)
+    driver_kwargs = {'ex_force_auth_version': '2.0'}
+
+    @classmethod
+    def create_driver(self):
+        return self.driver_type(*self.driver_args, **self.driver_kwargs)
+
+    def setUp(self):
+        self.driver_klass.connectionCls.conn_classes = (RackspaceNovaMockHttp, RackspaceNovaMockHttp)
+        self.driver_klass.connectionCls.auth_url = "https://auth.api.example.com/v2.0/"
+        self.driver = self.create_driver()
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
+        clear_pricing_data()
+        self.node = self.driver.list_nodes()[1]
+
+    def test_service_catalog(self):
+        self.assertEqual('https://dfw.servers.api.rackspacecloud.com/v2/1337', self.driver.connection.get_endpoint())
+
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

Modified: libcloud/trunk/test/dns/test_rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/dns/test_rackspace.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/dns/test_rackspace.py (original)
+++ libcloud/trunk/test/dns/test_rackspace.py Thu Feb 16 07:05:43 2012
@@ -37,6 +37,8 @@ class RackspaceUSTests(unittest.TestCase
         RackspaceMockHttp.type = None
         self.driver = self.klass(*DNS_PARAMS_RACKSPACE)
         self.driver.connection.poll_interval = 0.0
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
 
     def test_list_record_types(self):
         record_types = self.driver.list_record_types()

Modified: libcloud/trunk/test/loadbalancer/test_rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/test_rackspace.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/loadbalancer/test_rackspace.py (original)
+++ libcloud/trunk/test/loadbalancer/test_rackspace.py Thu Feb 16 07:05:43 2012
@@ -45,6 +45,8 @@ class RackspaceLBTests(unittest.TestCase
         RackspaceLBMockHttp.type = None
         self.driver = RackspaceLBDriver('user', 'key')
         self.driver.connection.poll_interval = 0.0
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
 
     def test_list_protocols(self):
         protocols = self.driver.list_protocols()
@@ -323,7 +325,7 @@ class RackspaceLBTests(unittest.TestCase
             address="10.45.13.5/12"
         )
 
-        balancer= self.driver.ex_destroy_balancer_access_rule(balancer, rule)
+        balancer = self.driver.ex_destroy_balancer_access_rule(balancer, rule)
 
         rule_ids = [r.id for r in balancer.extra['accessList']]
 
@@ -775,6 +777,9 @@ class RackspaceUKLBTests(RackspaceLBTest
                 RackspaceLBMockHttp)
         RackspaceLBMockHttp.type = None
         self.driver = RackspaceUKLBDriver('user', 'key')
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
+
 
 class RackspaceLBMockHttp(MockHttpTestCase):
     fixtures = LoadBalancerFileFixtures('rackspace')

Modified: libcloud/trunk/test/secrets.py-dist
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/secrets.py-dist?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/secrets.py-dist (original)
+++ libcloud/trunk/test/secrets.py-dist Thu Feb 16 07:05:43 2012
@@ -29,6 +29,7 @@ OPENSTACK_PARAMS = ('user_name', 'api_ke
 OPENNEBULA_PARAMS = ('user', 'key')
 OPSOURCE_PARAMS = ('user', 'password')
 RACKSPACE_PARAMS = ('user', 'key')
+RACKSPACE_NOVA_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
 SLICEHOST_PARAMS = ('key',)
 SOFTLAYER_PARAMS = ('user', 'api_key')
 VCLOUD_PARAMS = ('user', 'secret')

Modified: libcloud/trunk/test/storage/test_cloudfiles.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/storage/test_cloudfiles.py?rev=1244862&r1=1244861&r2=1244862&view=diff
==============================================================================
--- libcloud/trunk/test/storage/test_cloudfiles.py (original)
+++ libcloud/trunk/test/storage/test_cloudfiles.py Thu Feb 16 07:05:43 2012
@@ -42,6 +42,7 @@ from test.file_fixtures import StorageFi
 
 current_hash = None
 
+
 class CloudFilesTests(unittest.TestCase):
 
     def setUp(self):
@@ -52,6 +53,8 @@ class CloudFilesTests(unittest.TestCase)
         CloudFilesMockHttp.type = None
         CloudFilesMockRawResponse.type = None
         self.driver = CloudFilesStorageDriver('dummy', 'dummy')
+        # normally authentication happens lazily, but we force it here
+        self.driver.connection._populate_hosts_and_request_paths()
         self._remove_test_file()
 
     def tearDown(self):
@@ -162,7 +165,7 @@ class CloudFilesTests(unittest.TestCase)
                 'Container already exists but an exception was not thrown')
 
     def test_create_container_invalid_name_too_long(self):
-        name = ''.join([ 'x' for x in range(0, 257)])
+        name = ''.join(['x' for x in range(0, 257)])
         try:
             self.driver.create_container(container_name=name)
         except InvalidContainerNameError:
@@ -272,7 +275,7 @@ class CloudFilesTests(unittest.TestCase)
         file_path = os.path.abspath(__file__)
         container = Container(name='foo_bar_container', extra={}, driver=self)
         object_name = 'foo_test_upload'
-        extra = {'meta_data': { 'some-value': 'foobar'}}
+        extra = {'meta_data': {'some-value': 'foobar'}}
         obj = self.driver.upload_object(file_path=file_path, container=container,
                                         extra=extra, object_name=object_name)
         self.assertEqual(obj.name, 'foo_test_upload')