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/09/07 13:58:17 UTC
svn commit: r1166136 [1/2] - in /libcloud/trunk: libcloud/ libcloud/common/
libcloud/compute/drivers/ libcloud/loadbalancer/drivers/
libcloud/storage/drivers/ test/ test/compute/
test/compute/fixtures/openstack/ test/compute/fixtures/rackspace/
Author: tomaz
Date: Wed Sep 7 11:58:16 2011
New Revision: 1166136
URL: http://svn.apache.org/viewvc?rev=1166136&view=rev
Log:
Rackspace driver now inherits from the OpenStack one instead of doing it
vice-versa. The patch has been contributed by Mike Nerone <mike at nerone dot
org> and is part of LIBCLOUD-110.
Added:
libcloud/trunk/libcloud/common/openstack.py
libcloud/trunk/libcloud/compute/drivers/openstack.py
libcloud/trunk/test/compute/fixtures/openstack/
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_flavors_detail.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_detail.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_post.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_limits.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_missing.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_pending.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_same_uuid.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_success.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_empty.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_metadata.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_ips.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_metadata.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_group.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups.xml
libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups_detail.xml
libcloud/trunk/test/compute/test_openstack.py
Modified:
libcloud/trunk/libcloud/common/rackspace.py
libcloud/trunk/libcloud/compute/drivers/rackspace.py
libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
libcloud/trunk/libcloud/pricing.py
libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
libcloud/trunk/test/__init__.py
libcloud/trunk/test/compute/__init__.py
libcloud/trunk/test/compute/fixtures/rackspace/
libcloud/trunk/test/compute/test_deployment.py
libcloud/trunk/test/compute/test_rackspace.py
libcloud/trunk/test/test_pricing.py
Added: libcloud/trunk/libcloud/common/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/openstack.py?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/libcloud/common/openstack.py (added)
+++ libcloud/trunk/libcloud/common/openstack.py Wed Sep 7 11:58:16 2011
@@ -0,0 +1,133 @@
+# 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.
+
+"""
+Common utilities for OpenStack
+"""
+import httplib
+from urllib2 import urlparse
+from libcloud.common.base import ConnectionUserAndKey
+from libcloud.compute.types import InvalidCredsError, MalformedResponseError
+
+AUTH_API_VERSION = 'v1.0'
+
+__all__ = [
+ "OpenStackBaseConnection",
+ ]
+
+class OpenStackBaseConnection(ConnectionUserAndKey):
+
+ auth_host = None
+
+ def __init__(self, user_id, key, secure, host=None, port=None):
+ self.cdn_management_url = None
+ self.storage_url = None
+ self.auth_token = None
+ if host is not None:
+ self.auth_host = host
+ if port is not None:
+ self.port = (port, port)
+
+ super(OpenStackBaseConnection, self).__init__(
+ user_id, key, secure=secure)
+
+ def add_default_headers(self, headers):
+ headers['X-Auth-Token'] = self.auth_token
+ headers['Accept'] = self.accept_format
+ return headers
+
+ @property
+ def request_path(self):
+ return self._get_request_path(url_key=self._url_key)
+
+ @property
+ def host(self):
+ # Default to server_host
+ return self._get_host(url_key=self._url_key)
+
+ def _get_request_path(self, url_key):
+ value_key = '__request_path_%s' % (url_key)
+ value = getattr(self, value_key, None)
+
+ if not value:
+ self._populate_hosts_and_request_paths()
+ value = getattr(self, value_key, None)
+
+ return value
+
+ def _get_host(self, url_key):
+ value_key = '__%s' % (url_key)
+ value = getattr(self, value_key, None)
+
+ if not value:
+ self._populate_hosts_and_request_paths()
+ value = getattr(self, value_key, None)
+
+ return value
+
+ 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.
+ """
+ if not self.auth_token:
+ # Initial connection used for authentication
+ conn = self.conn_classes[self.secure](
+ self.auth_host, self.port[self.secure])
+ conn.request(
+ method='GET',
+ url='/%s' % (AUTH_API_VERSION),
+ headers={
+ 'X-Auth-User': self.user_id,
+ 'X-Auth-Key': self.key
+ }
+ )
+
+ resp = conn.getresponse()
+
+ if resp.status == httplib.NO_CONTENT:
+ # HTTP NO CONTENT (204): auth successful
+ headers = dict(resp.getheaders())
+
+ try:
+ self.server_url = headers['x-server-management-url']
+ self.storage_url = headers['x-storage-url']
+ self.cdn_management_url = headers['x-cdn-management-url']
+ self.lb_url = self.server_url.replace("servers", "ord.loadbalancers")
+ self.auth_token = headers['x-auth-token']
+ except KeyError, e:
+ # Returned 204 but has missing information in the header, something is wrong
+ raise MalformedResponseError('Malformed response',
+ body='Missing header: %s' % (str(e)),
+ driver=self.driver)
+ elif resp.status == httplib.UNAUTHORIZED:
+ # HTTP UNAUTHORIZED (401): auth failed
+ raise InvalidCredsError()
+ else:
+ # Any response code != 401 or 204, something is wrong
+ raise MalformedResponseError('Malformed response',
+ body='code: %s body:%s' % (resp.status, ''.join(resp.body.readlines())),
+ driver=self.driver)
+
+ for key in ['server_url', 'storage_url', 'cdn_management_url',
+ 'lb_url']:
+ scheme, server, request_path, param, query, fragment = (
+ urlparse.urlparse(getattr(self, key)))
+ # Set host to where we want to make further requests to
+ setattr(self, '__%s' % (key), server)
+ setattr(self, '__request_path_%s' % (key), request_path)
+
+ conn.close()
Modified: libcloud/trunk/libcloud/common/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/rackspace.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/rackspace.py (original)
+++ libcloud/trunk/libcloud/common/rackspace.py Wed Sep 7 11:58:16 2011
@@ -14,117 +14,13 @@
# limitations under the License.
"""
-Common utilities for Rackspace Cloud Servers and Cloud Files
+Common settings for Rackspace Cloud Servers and Cloud Files
"""
-import httplib
-from urllib2 import urlparse
-from libcloud.common.base import ConnectionUserAndKey
-from libcloud.compute.types import InvalidCredsError, MalformedResponseError
-AUTH_HOST_US='auth.api.rackspacecloud.com'
-AUTH_HOST_UK='lon.auth.api.rackspacecloud.com'
-AUTH_API_VERSION = 'v1.0'
+AUTH_HOST_US = 'auth.api.rackspacecloud.com'
+AUTH_HOST_UK = 'lon.auth.api.rackspacecloud.com'
__all__ = [
- "RackspaceBaseConnection",
"AUTH_HOST_US",
- "AUTH_HOST_UK"
+ "AUTH_HOST_UK",
]
-
-class RackspaceBaseConnection(ConnectionUserAndKey):
- def __init__(self, user_id, key, secure):
- self.cdn_management_url = None
- self.storage_url = None
- self.auth_token = None
- self.__host = None
- super(RackspaceBaseConnection, self).__init__(
- user_id, key, secure=secure)
-
- def add_default_headers(self, headers):
- headers['X-Auth-Token'] = self.auth_token
- headers['Accept'] = self.accept_format
- return headers
-
- @property
- def request_path(self):
- return self._get_request_path(url_key=self._url_key)
-
- @property
- def host(self):
- # Default to server_host
- return self._get_host(url_key=self._url_key)
-
- def _get_request_path(self, url_key):
- value_key = '__request_path_%s' % (url_key)
- value = getattr(self, value_key, None)
-
- if not value:
- self._populate_hosts_and_request_paths()
- value = getattr(self, value_key, None)
-
- return value
-
- def _get_host(self, url_key):
- value_key = '__%s' % (url_key)
- value = getattr(self, value_key, None)
-
- if not value:
- self._populate_hosts_and_request_paths()
- value = getattr(self, value_key, None)
-
- return value
-
- def _populate_hosts_and_request_paths(self):
- """
- Rackspace uses a separate host for API calls which is only provided
- after an initial authentication request. If we haven't made that
- request yet, do it here. Otherwise, just return the management host.
- """
- if not self.auth_token:
- # Initial connection used for authentication
- conn = self.conn_classes[self.secure](
- self.auth_host, self.port[self.secure])
- conn.request(
- method='GET',
- url='/%s' % (AUTH_API_VERSION),
- headers={
- 'X-Auth-User': self.user_id,
- 'X-Auth-Key': self.key
- }
- )
-
- resp = conn.getresponse()
-
- if resp.status == httplib.NO_CONTENT:
- # HTTP NO CONTENT (204): auth successful
- headers = dict(resp.getheaders())
-
- try:
- self.server_url = headers['x-server-management-url']
- self.storage_url = headers['x-storage-url']
- self.cdn_management_url = headers['x-cdn-management-url']
- self.lb_url = self.server_url.replace("servers", "ord.loadbalancers")
- self.auth_token = headers['x-auth-token']
- except KeyError, e:
- # Returned 204 but has missing information in the header, something is wrong
- raise MalformedResponseError('Malformed response',
- body='Missing header: %s' % (str(e)),
- driver=self.driver)
- elif resp.status == httplib.UNAUTHORIZED:
- # HTTP UNAUTHORIZED (401): auth failed
- raise InvalidCredsError()
- else:
- # Any response code != 401 or 204, something is wrong
- raise MalformedResponseError('Malformed response',
- body='code: %s body:%s' % (resp.status, ''.join(resp.body.readlines())),
- driver=self.driver)
-
- for key in ['server_url', 'storage_url', 'cdn_management_url',
- 'lb_url']:
- scheme, server, request_path, param, query, fragment = (
- urlparse.urlparse(getattr(self, key)))
- # Set host to where we want to make further requests to
- setattr(self, '__%s' % (key), server)
- setattr(self, '__request_path_%s' % (key), request_path)
-
- conn.close()
Added: libcloud/trunk/libcloud/compute/drivers/openstack.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/openstack.py?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/openstack.py (added)
+++ libcloud/trunk/libcloud/compute/drivers/openstack.py Wed Sep 7 11:58:16 2011
@@ -0,0 +1,648 @@
+# 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.
+"""
+OpenStack driver
+"""
+import os
+
+import base64
+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.common.base import Response
+from libcloud.common.types import MalformedResponseError
+from libcloud.compute.types import NodeState, Provider
+from libcloud.compute.base import NodeDriver, Node
+from libcloud.compute.base import NodeSize, NodeImage
+from libcloud.common.openstack import OpenStackBaseConnection
+
+__all__ = [
+ 'OpenStackResponse',
+ 'OpenStackConnection',
+ 'OpenStackNodeDriver',
+ 'OpenStackSharedIpGroup',
+ 'OpenStackNodeIpAddresses',
+ ]
+
+NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
+
+
+class OpenStackResponse(Response):
+
+ def success(self):
+ i = int(self.status)
+ return i >= 200 and i <= 299
+
+ def has_content_type(self, content_type):
+ content_type_value = self.headers.get('content-type') or ''
+ content_type_value = content_type_value.lower()
+ return content_type_value.find(content_type.lower()) > -1
+
+ def parse_body(self):
+ if self.has_content_type('application/xml'):
+ try:
+ return ET.XML(self.body)
+ except:
+ raise MalformedResponseError(
+ 'Failed to parse XML',
+ body=self.body,
+ driver=OpenStackNodeDriver)
+
+ 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=OpenStackNodeDriver)
+ 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)
+
+
+class OpenStackConnection(OpenStackBaseConnection):
+
+ responseCls = OpenStackResponse
+ _url_key = "server_url"
+
+ def __init__(self, user_id, key, secure, host=None, port=None):
+ super(OpenStackConnection, self).__init__(
+ user_id, key, secure=secure, host=host, port=port)
+ self.api_version = 'v1.0'
+ self.accept_format = 'application/xml'
+
+ def request(self, action, params=None, data='', headers=None,
+ method='GET'):
+ if not headers:
+ headers = {}
+ if not params:
+ params = {}
+ # Due to first-run authentication request, we may not have a path
+ if self.server_url:
+ action = self.server_url + action
+ if method in ("POST", "PUT"):
+ headers = {'Content-Type': 'application/xml; charset=UTF-8'}
+ if method == "GET":
+ params['cache-busting'] = os.urandom(8).encode('hex')
+ return super(OpenStackConnection, self).request(
+ action=action,
+ params=params, data=data,
+ method=method, headers=headers
+ )
+
+
+class OpenStackNodeDriver(NodeDriver):
+ """
+ OpenStack node driver.
+
+ Extra node attributes:
+ - password: root password, available after create.
+ - hostId: represents the host your cloud server runs on
+ - imageId: id of image
+ - flavorId: id of flavor
+ """
+ connectionCls = OpenStackConnection
+ type = Provider.OPENSTACK
+ api_name = 'openstack'
+ name = 'OpenStack'
+
+ 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}
+
+ 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)
+
+ if not name:
+ name = node.name
+
+ body = {'xmlns': NAMESPACE,
+ 'name': name}
+
+ if password != None:
+ body['adminPass'] = password
+
+ server_elm = ET.Element('server', body)
+
+ resp = self.connection.request(
+ uri, method='PUT', data=ET.tostring(server_elm))
+
+ if resp.status == 204 and password != None:
+ node.extra['password'] = password
+
+ return resp.status == 204
+
+ def ex_set_password(self, node, password):
+ """
+ Sets the Node's root password.
+
+ This will reboot the instance to complete the operation.
+
+ L{node.extra['password']} will be set to the new value if the
+ operation was successful.
+ """
+ return self._change_password_or_name(node, password=password)
+
+ def ex_set_server_name(self, node, name):
+ """
+ Sets the Node's name.
+
+ This will reboot the instance to complete the operation.
+ """
+ return self._change_password_or_name(node, name=name)
+
+ 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}
+ """
+ name = kwargs['name']
+ image = kwargs['image']
+ size = kwargs['size']
+
+ attributes = {'xmlns': NAMESPACE,
+ 'name': name,
+ 'imageId': str(image.id),
+ 'flavorId': str(size.id)
+ }
+
+ if 'ex_shared_ip_group' in kwargs:
+ # Deprecate this. Be explicit and call the variable
+ # ex_shared_ip_group_id since user needs to pass in the id, not the
+ # name.
+ warnings.warn('ex_shared_ip_group argument is deprecated. Please'
+ + ' use ex_shared_ip_group_id')
+
+ if 'ex_shared_ip_group_id' in kwargs:
+ shared_ip_group_id = kwargs['ex_shared_ip_group_id']
+ attributes['sharedIpGroupId'] = shared_ip_group_id
+
+ server_elm = ET.Element('server', attributes)
+
+ metadata_elm = self._metadata_to_xml(kwargs.get("ex_metadata", {}))
+ if metadata_elm:
+ server_elm.append(metadata_elm)
+
+ 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))
+ return self._to_node(resp.object)
+
+ def ex_resize(self, node, size):
+ """
+ Change an existing server flavor / scale the server up or down.
+
+ @keyword node: node to resize.
+ @param node: C{Node}
+
+ @keyword size: new size.
+ @param size: C{NodeSize}
+ """
+ elm = ET.Element(
+ 'resize',
+ {'xmlns': NAMESPACE,
+ 'flavorId': str(size.id),
+ }
+ )
+
+ resp = self.connection.request("/servers/%s/action" % (node.id),
+ method='POST',
+ data=ET.tostring(elm))
+ return resp.status == 202
+
+ def ex_confirm_resize(self, node):
+ """
+ Confirm a resize request which is currently in progress. If a resize
+ request is not explicitly confirmed or reverted it's automatically
+ confirmed after 24 hours.
+
+ For more info refer to the API documentation: http://goo.gl/zjFI1
+
+ @keyword node: node for which the resize request will be confirmed.
+ @param node: C{Node}
+ """
+ elm = ET.Element(
+ 'confirmResize',
+ {'xmlns': NAMESPACE}
+ )
+
+ resp = self.connection.request("/servers/%s/action" % (node.id),
+ method='POST',
+ data=ET.tostring(elm))
+ return resp.status == 204
+
+ def ex_revert_resize(self, node):
+ """
+ Revert a resize request which is currently in progress.
+ All resizes are automatically confirmed after 24 hours if they have
+ not already been confirmed explicitly or reverted.
+
+ For more info refer to the API documentation: http://goo.gl/AizBu
+
+ @keyword node: node for which the resize request will be reverted.
+ @param node: C{Node}
+ """
+ elm = ET.Element(
+ 'revertResize',
+ {'xmlns': NAMESPACE}
+ )
+
+ resp = self.connection.request("/servers/%s/action" % (node.id),
+ method='POST',
+ data=ET.tostring(elm))
+ return resp.status == 204
+
+ def ex_rebuild(self, node_id, image_id):
+ # @TODO: Remove those ifs in 0.6
+ if isinstance(node_id, Node):
+ node_id = node_id.id
+
+ if isinstance(image_id, NodeImage):
+ image_id = image_id.id
+
+ elm = ET.Element(
+ 'rebuild',
+ {'xmlns': NAMESPACE,
+ 'imageId': image_id,
+ }
+ )
+ resp = self.connection.request("/servers/%s/action" % node_id,
+ method='POST',
+ data=ET.tostring(elm))
+ return resp.status == 202
+
+ def ex_create_ip_group(self, group_name, node_id=None):
+ # @TODO: Remove this if in 0.6
+ if isinstance(node_id, Node):
+ node_id = node_id.id
+
+ group_elm = ET.Element(
+ 'sharedIpGroup',
+ {'xmlns': NAMESPACE,
+ 'name': group_name,
+ }
+ )
+
+ if node_id:
+ ET.SubElement(group_elm,
+ 'server',
+ {'id': node_id}
+ )
+
+ resp = self.connection.request('/shared_ip_groups',
+ method='POST',
+ data=ET.tostring(group_elm))
+ return self._to_shared_ip_group(resp.object)
+
+ def ex_list_ip_groups(self, details=False):
+ uri = '/shared_ip_groups/detail' if details else '/shared_ip_groups'
+ resp = self.connection.request(uri,
+ method='GET')
+ groups = self._findall(resp.object, 'sharedIpGroup')
+ return [self._to_shared_ip_group(el) for el in groups]
+
+ def ex_delete_ip_group(self, group_id):
+ uri = '/shared_ip_groups/%s' % group_id
+ resp = self.connection.request(uri, method='DELETE')
+ return resp.status == 204
+
+ def ex_share_ip(self, group_id, node_id, ip, configure_node=True):
+ # @TODO: Remove this if in 0.6
+ if isinstance(node_id, Node):
+ node_id = node_id.id
+
+ if configure_node:
+ str_configure = 'true'
+ else:
+ str_configure = 'false'
+
+ elm = ET.Element(
+ 'shareIp',
+ {'xmlns': NAMESPACE,
+ 'sharedIpGroupId': group_id,
+ 'configureServer': str_configure}
+ )
+
+ uri = '/servers/%s/ips/public/%s' % (node_id, ip)
+
+ resp = self.connection.request(uri,
+ method='PUT',
+ data=ET.tostring(elm))
+ return resp.status == 202
+
+ def ex_unshare_ip(self, node_id, ip):
+ # @TODO: Remove this if in 0.6
+ if isinstance(node_id, Node):
+ node_id = node_id.id
+
+ uri = '/servers/%s/ips/public/%s' % (node_id, ip)
+
+ resp = self.connection.request(uri,
+ method='DELETE')
+ return resp.status == 202
+
+ def ex_list_ip_addresses(self, node_id):
+ # @TODO: Remove this if in 0.6
+ if isinstance(node_id, Node):
+ node_id = node_id.id
+
+ uri = '/servers/%s/ips' % node_id
+ resp = self.connection.request(uri,
+ method='GET')
+ return self._to_ip_addresses(resp.object)
+
+ def _metadata_to_xml(self, metadata):
+ if len(metadata) == 0:
+ return None
+
+ metadata_elm = ET.Element('metadata')
+ for k, v in metadata.items():
+ meta_elm = ET.SubElement(metadata_elm, 'meta', {'key': str(k)})
+ meta_elm.text = str(v)
+
+ return metadata_elm
+
+ def _files_to_xml(self, files):
+ if len(files) == 0:
+ return None
+
+ personality_elm = ET.Element('personality')
+ for k, v in files.items():
+ file_elm = ET.SubElement(personality_elm,
+ 'file',
+ {'path': str(k)})
+ file_elm.text = base64.b64encode(v)
+
+ return personality_elm
+
+ def _reboot_node(self, node, reboot_type='SOFT'):
+ 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])
+ for item in body[1:]])
+ body = '<%s xmlns="%s" %s/>' % (body[0], NAMESPACE, attr)
+ uri = '/servers/%s/action' % (node.id)
+ resp = self.connection.request(uri, method='POST', data=body)
+ return resp
+
+ def _to_nodes(self, object):
+ node_elements = self._findall(object, 'server')
+ return [self._to_node(el) for el in node_elements]
+
+ def _fixxpath(self, xpath):
+ # ElementTree wants namespaces in its xpaths, so here we add them.
+ return "/".join(["{%s}%s" % (NAMESPACE, e) for e in xpath.split("/")])
+
+ def _findall(self, element, xpath):
+ return element.findall(self._fixxpath(xpath))
+
+ def _to_node(self, el):
+ def get_ips(el):
+ return [ip.get('addr') for ip in el]
+
+ def get_meta_dict(el):
+ d = {}
+ for meta in el:
+ d[meta.get('key')] = meta.text
+ return d
+
+ public_ip = get_ips(self._findall(el,
+ 'addresses/public/ip'))
+ private_ip = get_ips(self._findall(el,
+ 'addresses/private/ip'))
+ metadata = get_meta_dict(self._findall(el, 'metadata/meta'))
+
+ n = Node(id=el.get('id'),
+ name=el.get('name'),
+ state=self.NODE_STATE_MAP.get(
+ el.get('status'), NodeState.UNKNOWN),
+ public_ip=public_ip,
+ private_ip=private_ip,
+ driver=self.connection.driver,
+ extra={
+ 'password': el.get('adminPass'),
+ 'hostId': el.get('hostId'),
+ 'imageId': el.get('imageId'),
+ 'flavorId': el.get('flavorId'),
+ 'uri': "https://%s%s/servers/%s" % (
+ self.connection.host,
+ self.connection.request_path, el.get('id')),
+ 'metadata': metadata,
+ })
+ return n
+
+ def _to_sizes(self, object):
+ elements = self._findall(object, 'flavor')
+ return [self._to_size(el) for el in elements]
+
+ def _to_size(self, el):
+ s = NodeSize(id=el.get('id'),
+ 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,
+ driver=self.connection.driver)
+ return s
+
+ def _to_images(self, object):
+ elements = self._findall(object, "image")
+ return [self._to_image(el)
+ for el in elements
+ if el.get('status') == 'ACTIVE']
+
+ def _to_image(self, el):
+ i = NodeImage(id=el.get('id'),
+ name=el.get('name'),
+ driver=self.connection.driver,
+ extra={'updated': el.get('updated'),
+ 'created': el.get('created'),
+ 'status': el.get('status'),
+ 'serverId': el.get('serverId'),
+ 'progress': el.get('progress')})
+ return i
+
+ def ex_limits(self):
+ """
+ Extra call to get account's limits, such as
+ rates (for example amount of POST requests per day)
+ and absolute limits like total amount of available
+ RAM to be used by servers.
+
+ @return: C{dict} with keys 'rate' and 'absolute'
+ """
+
+ def _to_rate(el):
+ rate = {}
+ for item in el.items():
+ rate[item[0]] = item[1]
+
+ return rate
+
+ def _to_absolute(el):
+ return {el.get('name'): el.get('value')}
+
+ limits = self.connection.request("/limits").object
+ rate = [_to_rate(el) for el in self._findall(limits, 'rate/limit')]
+ absolute = {}
+ for item in self._findall(limits, 'absolute/limit'):
+ absolute.update(_to_absolute(item))
+
+ return {"rate": rate, "absolute": absolute}
+
+ def ex_save_image(self, node, name):
+ """Create an image for node.
+
+ @keyword node: node to use as a base for image
+ @param node: L{Node}
+ @keyword name: name for new image
+ @param name: C{string}
+ """
+
+ image_elm = ET.Element(
+ 'image',
+ {'xmlns': NAMESPACE,
+ 'name': name,
+ 'serverId': node.id}
+ )
+
+ return self._to_image(self.connection.request("/images",
+ method="POST",
+ data=ET.tostring(image_elm)).object)
+
+ def _to_shared_ip_group(self, el):
+ servers_el = self._findall(el, 'servers')
+ if servers_el:
+ servers = [s.get('id')
+ for s in self._findall(servers_el[0], 'server')]
+ else:
+ servers = None
+ return OpenStackSharedIpGroup(id=el.get('id'),
+ name=el.get('name'),
+ servers=servers)
+
+ def _to_ip_addresses(self, el):
+ return OpenStackNodeIpAddresses(
+ [ip.get('addr') for ip in
+ self._findall(self._findall(el, 'public')[0], 'ip')],
+ [ip.get('addr') for ip in
+ self._findall(self._findall(el, 'private')[0], 'ip')]
+ )
+
+ def _get_size_price(self, size_id):
+ if 'openstack' not in PRICING_DATA['compute']:
+ return 0.0
+
+ return get_size_price(driver_type='compute',
+ driver_name='openstack',
+ size_id=size_id)
+
+
+class OpenStackSharedIpGroup(object):
+ """
+ Shared IP group info.
+ """
+
+ def __init__(self, id, name, servers=None):
+ self.id = str(id)
+ self.name = name
+ self.servers = servers
+
+
+class OpenStackNodeIpAddresses(object):
+ """
+ List of public and private IP addresses of a Node.
+ """
+
+ def __init__(self, public_addresses, private_addresses):
+ self.public_addresses = public_addresses
+ self.private_addresses = private_addresses
Modified: libcloud/trunk/libcloud/compute/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/rackspace.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/rackspace.py Wed Sep 7 11:58:16 2011
@@ -15,164 +15,28 @@
"""
Rackspace driver
"""
-import os
-
-import base64
-import warnings
-
-from xml.etree import ElementTree as ET
-from xml.parsers.expat import ExpatError
-
-from libcloud.pricing import get_pricing, get_size_price, PRICING_DATA
-from libcloud.common.base import Response
-from libcloud.common.types import MalformedResponseError
-from libcloud.compute.types import NodeState, Provider
-from libcloud.compute.base import NodeDriver, Node
-from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
+from libcloud.compute.types import Provider
+from libcloud.compute.base import NodeLocation
+from libcloud.compute.drivers.openstack import OpenStackConnection, OpenStackNodeDriver, OpenStackResponse
from libcloud.common.rackspace import (
- AUTH_HOST_US, AUTH_HOST_UK, RackspaceBaseConnection)
-
-
-NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
-
+ AUTH_HOST_US, AUTH_HOST_UK)
-class RackspaceResponse(Response):
- def success(self):
- i = int(self.status)
- return i >= 200 and i <= 299
-
- def parse_body(self):
- if not self.body:
- return None
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError(
- "Failed to parse XML",
- body=self.body,
- driver=RackspaceNodeDriver)
- return body
-
- def parse_error(self):
- # TODO: fixup, Rackspace only uses response codes really!
- try:
- body = ET.XML(self.body)
- except:
- raise MalformedResponseError(
- "Failed to parse XML",
- body=self.body, driver=RackspaceNodeDriver)
- 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)
-
-
-class RackspaceConnection(RackspaceBaseConnection):
+class RackspaceConnection(OpenStackConnection):
"""
Connection class for the Rackspace driver
"""
- responseCls = RackspaceResponse
+ responseCls = OpenStackResponse
auth_host = AUTH_HOST_US
- _url_key = "server_url"
- def __init__(self, user_id, key, secure=True):
- super(RackspaceConnection, self).__init__(user_id, key, secure)
- self.api_version = 'v1.0'
- self.accept_format = 'application/xml'
-
- def request(self, action, params=None, data='', headers=None,
- method='GET'):
- if not headers:
- headers = {}
- if not params:
- params = {}
- # Due to first-run authentication request, we may not have a path
- if self.server_url:
- action = self.server_url + action
- if method in ("POST", "PUT"):
- headers = {'Content-Type': 'application/xml; charset=UTF-8'}
- if method == "GET":
- params['cache-busting'] = os.urandom(8).encode('hex')
- return super(RackspaceConnection, self).request(
- action=action,
- params=params, data=data,
- method=method, headers=headers
- )
-
-class RackspaceSharedIpGroup(object):
- """
- Shared IP group info.
- """
-
- def __init__(self, id, name, servers=None):
- self.id = str(id)
- self.name = name
- self.servers = servers
-
-
-class RackspaceNodeIpAddresses(object):
- """
- List of public and private IP addresses of a Node.
- """
-
- def __init__(self, public_addresses, private_addresses):
- self.public_addresses = public_addresses
- self.private_addresses = private_addresses
-
-
-class RackspaceNodeDriver(NodeDriver):
- """
- Rackspace node driver.
-
- Extra node attributes:
- - password: root password, available after create.
- - hostId: represents the host your cloud server runs on
- - imageId: id of image
- - flavorId: id of flavor
- """
+class RackspaceNodeDriver(OpenStackNodeDriver):
+ name = 'Rackspace'
connectionCls = RackspaceConnection
type = Provider.RACKSPACE
api_name = 'rackspace'
- name = 'Rackspace'
-
- 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}
-
- 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)
def list_locations(self):
"""Lists available locations
@@ -182,462 +46,6 @@ class RackspaceNodeDriver(NodeDriver):
"""
return [NodeLocation(0, "Rackspace DFW1/ORD1", 'US', self)]
- def _change_password_or_name(self, node, name=None, password=None):
- uri = '/servers/%s' % (node.id)
-
- if not name:
- name = node.name
-
- body = {'xmlns': NAMESPACE,
- 'name': name}
-
- if password != None:
- body['adminPass'] = password
-
- server_elm = ET.Element('server', body)
-
- resp = self.connection.request(
- uri, method='PUT', data=ET.tostring(server_elm))
-
- if resp.status == 204 and password != None:
- node.extra['password'] = password
-
- return resp.status == 204
-
- def ex_set_password(self, node, password):
- """
- Sets the Node's root password.
-
- This will reboot the instance to complete the operation.
-
- L{node.extra['password']} will be set to the new value if the
- operation was successful.
- """
- return self._change_password_or_name(node, password=password)
-
- def ex_set_server_name(self, node, name):
- """
- Sets the Node's name.
-
- This will reboot the instance to complete the operation.
- """
- return self._change_password_or_name(node, name=name)
-
- def create_node(self, **kwargs):
- """Create a new rackspace 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}
- """
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- attributes = {'xmlns': NAMESPACE,
- 'name': name,
- 'imageId': str(image.id),
- 'flavorId': str(size.id)
- }
-
- if 'ex_shared_ip_group' in kwargs:
- # Deprecate this. Be explicit and call the variable
- # ex_shared_ip_group_id since user needs to pass in the id, not the
- # name.
- warnings.warn('ex_shared_ip_group argument is deprecated. Please'
- + ' use ex_shared_ip_group_id')
-
- if 'ex_shared_ip_group_id' in kwargs:
- shared_ip_group_id = kwargs['ex_shared_ip_group_id']
- attributes['sharedIpGroupId'] = shared_ip_group_id
-
- server_elm = ET.Element('server', attributes)
-
- metadata_elm = self._metadata_to_xml(kwargs.get("ex_metadata", {}))
- if metadata_elm:
- server_elm.append(metadata_elm)
-
- 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))
- return self._to_node(resp.object)
-
- def ex_resize(self, node, size):
- """
- Change an existing server flavor / scale the server up or down.
-
- @keyword node: node to resize.
- @param node: C{Node}
-
- @keyword size: new size.
- @param size: C{NodeSize}
- """
- elm = ET.Element(
- 'resize',
- {'xmlns': NAMESPACE,
- 'flavorId': str(size.id),
- }
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == 202
-
- def ex_confirm_resize(self, node):
- """
- Confirm a resize request which is currently in progress. If a resize
- request is not explicitly confirmed or reverted it's automatically
- confirmed after 24 hours.
-
- For more info refer to the API documentation: http://goo.gl/zjFI1
-
- @keyword node: node for which the resize request will be confirmed.
- @param node: C{Node}
- """
- elm = ET.Element(
- 'confirmResize',
- {'xmlns': NAMESPACE}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == 204
-
- def ex_revert_resize(self, node):
- """
- Revert a resize request which is currently in progress.
- All resizes are automatically confirmed after 24 hours if they have
- not already been confirmed explicitly or reverted.
-
- For more info refer to the API documentation: http://goo.gl/AizBu
-
- @keyword node: node for which the resize request will be reverted.
- @param node: C{Node}
- """
- elm = ET.Element(
- 'revertResize',
- {'xmlns': NAMESPACE}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == 204
-
- def ex_rebuild(self, node_id, image_id):
- # @TODO: Remove those ifs in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- if isinstance(image_id, NodeImage):
- image_id = image_id.id
-
- elm = ET.Element(
- 'rebuild',
- {'xmlns': NAMESPACE,
- 'imageId': image_id,
- }
- )
- resp = self.connection.request("/servers/%s/action" % node_id,
- method='POST',
- data=ET.tostring(elm))
- return resp.status == 202
-
- def ex_create_ip_group(self, group_name, node_id=None):
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- group_elm = ET.Element(
- 'sharedIpGroup',
- {'xmlns': NAMESPACE,
- 'name': group_name,
- }
- )
-
- if node_id:
- ET.SubElement(group_elm,
- 'server',
- {'id': node_id}
- )
-
- resp = self.connection.request('/shared_ip_groups',
- method='POST',
- data=ET.tostring(group_elm))
- return self._to_shared_ip_group(resp.object)
-
- def ex_list_ip_groups(self, details=False):
- uri = '/shared_ip_groups/detail' if details else '/shared_ip_groups'
- resp = self.connection.request(uri,
- method='GET')
- groups = self._findall(resp.object, 'sharedIpGroup')
- return [self._to_shared_ip_group(el) for el in groups]
-
- def ex_delete_ip_group(self, group_id):
- uri = '/shared_ip_groups/%s' % group_id
- resp = self.connection.request(uri, method='DELETE')
- return resp.status == 204
-
- def ex_share_ip(self, group_id, node_id, ip, configure_node=True):
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- if configure_node:
- str_configure = 'true'
- else:
- str_configure = 'false'
-
- elm = ET.Element(
- 'shareIp',
- {'xmlns': NAMESPACE,
- 'sharedIpGroupId': group_id,
- 'configureServer': str_configure}
- )
-
- uri = '/servers/%s/ips/public/%s' % (node_id, ip)
-
- resp = self.connection.request(uri,
- method='PUT',
- data=ET.tostring(elm))
- return resp.status == 202
-
- def ex_unshare_ip(self, node_id, ip):
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s/ips/public/%s' % (node_id, ip)
-
- resp = self.connection.request(uri,
- method='DELETE')
- return resp.status == 202
-
- def ex_list_ip_addresses(self, node_id):
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s/ips' % node_id
- resp = self.connection.request(uri,
- method='GET')
- return self._to_ip_addresses(resp.object)
-
- def _metadata_to_xml(self, metadata):
- if len(metadata) == 0:
- return None
-
- metadata_elm = ET.Element('metadata')
- for k, v in metadata.items():
- meta_elm = ET.SubElement(metadata_elm, 'meta', {'key': str(k)})
- meta_elm.text = str(v)
-
- return metadata_elm
-
- def _files_to_xml(self, files):
- if len(files) == 0:
- return None
-
- personality_elm = ET.Element('personality')
- for k, v in files.items():
- file_elm = ET.SubElement(personality_elm,
- 'file',
- {'path': str(k)})
- file_elm.text = base64.b64encode(v)
-
- return personality_elm
-
- def _reboot_node(self, node, reboot_type='SOFT'):
- 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])
- for item in body[1:]])
- body = '<%s xmlns="%s" %s/>' % (body[0], NAMESPACE, attr)
- uri = '/servers/%s/action' % (node.id)
- resp = self.connection.request(uri, method='POST', data=body)
- return resp
-
- def _to_nodes(self, object):
- node_elements = self._findall(object, 'server')
- return [self._to_node(el) for el in node_elements]
-
- def _fixxpath(self, xpath):
- # ElementTree wants namespaces in its xpaths, so here we add them.
- return "/".join(["{%s}%s" % (NAMESPACE, e) for e in xpath.split("/")])
-
- def _findall(self, element, xpath):
- return element.findall(self._fixxpath(xpath))
-
- def _to_node(self, el):
- def get_ips(el):
- return [ip.get('addr') for ip in el]
-
- def get_meta_dict(el):
- d = {}
- for meta in el:
- d[meta.get('key')] = meta.text
- return d
-
- public_ip = get_ips(self._findall(el,
- 'addresses/public/ip'))
- private_ip = get_ips(self._findall(el,
- 'addresses/private/ip'))
- metadata = get_meta_dict(self._findall(el, 'metadata/meta'))
-
- n = Node(id=el.get('id'),
- name=el.get('name'),
- state=self.NODE_STATE_MAP.get(
- el.get('status'), NodeState.UNKNOWN),
- public_ip=public_ip,
- private_ip=private_ip,
- driver=self.connection.driver,
- extra={
- 'password': el.get('adminPass'),
- 'hostId': el.get('hostId'),
- 'imageId': el.get('imageId'),
- 'flavorId': el.get('flavorId'),
- 'uri': "https://%s%s/servers/%s" % (
- self.connection.host,
- self.connection.request_path, el.get('id')),
- 'metadata': metadata,
- })
- return n
-
- def _to_sizes(self, object):
- elements = self._findall(object, 'flavor')
- return [self._to_size(el) for el in elements]
-
- def _to_size(self, el):
- s = NodeSize(id=el.get('id'),
- 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,
- driver=self.connection.driver)
- return s
-
- def _to_images(self, object):
- elements = self._findall(object, "image")
- return [self._to_image(el)
- for el in elements
- if el.get('status') == 'ACTIVE']
-
- def _to_image(self, el):
- i = NodeImage(id=el.get('id'),
- name=el.get('name'),
- driver=self.connection.driver,
- extra={'updated': el.get('updated'),
- 'created': el.get('created'),
- 'status': el.get('status'),
- 'serverId': el.get('serverId'),
- 'progress': el.get('progress')})
- return i
-
- def ex_limits(self):
- """
- Extra call to get account's limits, such as
- rates (for example amount of POST requests per day)
- and absolute limits like total amount of available
- RAM to be used by servers.
-
- @return: C{dict} with keys 'rate' and 'absolute'
- """
-
- def _to_rate(el):
- rate = {}
- for item in el.items():
- rate[item[0]] = item[1]
-
- return rate
-
- def _to_absolute(el):
- return {el.get('name'): el.get('value')}
-
- limits = self.connection.request("/limits").object
- rate = [_to_rate(el) for el in self._findall(limits, 'rate/limit')]
- absolute = {}
- for item in self._findall(limits, 'absolute/limit'):
- absolute.update(_to_absolute(item))
-
- return {"rate": rate, "absolute": absolute}
-
- def ex_save_image(self, node, name):
- """Create an image for node.
-
- @keyword node: node to use as a base for image
- @param node: L{Node}
- @keyword name: name for new image
- @param name: C{string}
- """
-
- image_elm = ET.Element(
- 'image',
- {'xmlns': NAMESPACE,
- 'name': name,
- 'serverId': node.id}
- )
-
- return self._to_image(self.connection.request("/images",
- method="POST",
- data=ET.tostring(image_elm)).object)
-
- def _to_shared_ip_group(self, el):
- servers_el = self._findall(el, 'servers')
- if servers_el:
- servers = [s.get('id')
- for s in self._findall(servers_el[0], 'server')]
- else:
- servers = None
- return RackspaceSharedIpGroup(id=el.get('id'),
- name=el.get('name'),
- servers=servers)
-
- def _to_ip_addresses(self, el):
- return RackspaceNodeIpAddresses(
- [ip.get('addr') for ip in
- self._findall(self._findall(el, 'public')[0], 'ip')],
- [ip.get('addr') for ip in
- self._findall(self._findall(el, 'private')[0], 'ip')]
- )
-
class RackspaceUKConnection(RackspaceConnection):
"""
@@ -655,52 +63,3 @@ class RackspaceUKNodeDriver(RackspaceNod
def list_locations(self):
return [NodeLocation(0, 'Rackspace UK London', 'UK', self)]
-
-
-class OpenStackResponse(RackspaceResponse):
-
- def has_content_type(self, content_type):
- content_type_header = dict([(key, value) for key, value in
- self.headers.items()
- if key.lower() == 'content-type'])
- if not content_type_header:
- return False
-
- content_type_value = content_type_header['content-type'].lower()
-
- return content_type_value.find(content_type.lower()) > -1
-
- def parse_body(self):
- if not self.has_content_type('application/xml') or not self.body:
- return self.body
-
- try:
- return ET.XML(self.body)
- except:
- raise MalformedResponseError(
- 'Failed to parse XML',
- body=self.body,
- driver=RackspaceNodeDriver)
-
-
-class OpenStackConnection(RackspaceConnection):
-
- responseCls = OpenStackResponse
-
- def __init__(self, user_id, key, secure, host, port):
- super(OpenStackConnection, self).__init__(user_id, key, secure=secure)
- self.auth_host = host
- self.port = (port, port)
-
-
-class OpenStackNodeDriver(RackspaceNodeDriver):
- name = 'OpenStack'
- connectionCls = OpenStackConnection
-
- def _get_size_price(self, size_id):
- if 'openstack' not in PRICING_DATA['compute']:
- return 0.0
-
- return get_size_price(driver_type='compute',
- driver_name='openstack',
- size_id=size_id)
Modified: libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py Wed Sep 7 11:58:16 2011
@@ -25,9 +25,9 @@ from libcloud.common.base import Respons
from libcloud.loadbalancer.base import LoadBalancer, Member, Driver, Algorithm
from libcloud.loadbalancer.base import DEFAULT_ALGORITHM
from libcloud.loadbalancer.types import State
-from libcloud.common.rackspace import (AUTH_HOST_US, AUTH_HOST_UK,
- RackspaceBaseConnection)
-
+from libcloud.common.openstack import OpenStackBaseConnection
+from libcloud.common.rackspace import (
+ AUTH_HOST_US, AUTH_HOST_UK)
class RackspaceResponse(Response):
@@ -41,7 +41,7 @@ class RackspaceResponse(Response):
return json.loads(self.body)
-class RackspaceConnection(RackspaceBaseConnection):
+class RackspaceConnection(OpenStackBaseConnection):
responseCls = RackspaceResponse
auth_host = AUTH_HOST_US
_url_key = "lb_url"
Modified: libcloud/trunk/libcloud/pricing.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/pricing.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/libcloud/pricing.py (original)
+++ libcloud/trunk/libcloud/pricing.py Wed Sep 7 11:58:16 2011
@@ -27,13 +27,18 @@ from os.path import join as pjoin
PRICING_FILE_PATH = 'data/pricing.json'
-PRICING_DATA = {
- 'compute': {},
- 'storage': {}
-}
+PRICING_DATA = {}
VALID_PRICING_DRIVER_TYPES = [ 'compute', 'storage' ]
+def clear_pricing_data():
+ PRICING_DATA.clear()
+ PRICING_DATA.update({
+ 'compute': {},
+ 'storage': {},
+ })
+clear_pricing_data()
+
def get_pricing_file_path(file_path=None):
pricing_directory = os.path.dirname(os.path.abspath(__file__))
pricing_file_path = pjoin(pricing_directory, PRICING_FILE_PATH)
Modified: libcloud/trunk/libcloud/storage/drivers/cloudfiles.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/storage/drivers/cloudfiles.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/libcloud/storage/drivers/cloudfiles.py (original)
+++ libcloud/trunk/libcloud/storage/drivers/cloudfiles.py Wed Sep 7 11:58:16 2011
@@ -34,9 +34,10 @@ from libcloud.storage.types import Objec
from libcloud.storage.types import ObjectHashMismatchError
from libcloud.storage.types import InvalidContainerNameError
from libcloud.common.types import LazyList
+from libcloud.common.openstack import OpenStackBaseConnection
from libcloud.common.rackspace import (
- AUTH_HOST_US, AUTH_HOST_UK, RackspaceBaseConnection)
+ AUTH_HOST_US, AUTH_HOST_UK)
CDN_HOST = 'cdn.clouddrive.com'
API_VERSION = 'v1.0'
@@ -82,7 +83,7 @@ class CloudFilesResponse(Response):
class CloudFilesRawResponse(CloudFilesResponse, RawResponse):
pass
-class CloudFilesConnection(RackspaceBaseConnection):
+class CloudFilesConnection(OpenStackBaseConnection):
"""
Base connection class for the Cloudfiles driver.
"""
Modified: libcloud/trunk/test/__init__.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/__init__.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/test/__init__.py (original)
+++ libcloud/trunk/test/__init__.py Wed Sep 7 11:58:16 2011
@@ -21,6 +21,9 @@ from cStringIO import StringIO
from urllib2 import urlparse
from cgi import parse_qs
+XML_HEADERS = {'content-type': 'application/xml'}
+
+
class LibcloudTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
self._visited_urls = []
@@ -90,11 +93,11 @@ class MockResponse(object):
class BaseMockHttpObject(object):
def _get_method_name(self, type, use_param, qs, path):
- meth_name = path.replace('/','_').replace('.', '_').replace('-','_')
+ meth_name = path.replace('/', '_').replace('.', '_').replace('-', '_')
if type:
meth_name = '%s_%s' % (meth_name, self.type)
if use_param:
- param = qs[self.use_param][0].replace('.', '_').replace('-','_')
+ param = qs[self.use_param][0].replace('.', '_').replace('-', '_')
meth_name = '%s_%s' % (meth_name, param)
return meth_name
Modified: libcloud/trunk/test/compute/__init__.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/__init__.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/test/compute/__init__.py (original)
+++ libcloud/trunk/test/compute/__init__.py Wed Sep 7 11:58:16 2011
@@ -14,8 +14,11 @@
# limitations under the License.
from libcloud.compute.base import Node, NodeImage, NodeLocation
+from libcloud.pricing import get_pricing
class TestCaseMixin(object):
+ should_list_locations = True
+ should_have_pricing = False
def test_list_nodes_response(self):
nodes = self.driver.list_nodes()
@@ -39,8 +42,10 @@ class TestCaseMixin(object):
for image in images:
self.assertTrue(isinstance(image, NodeImage))
-
def test_list_locations_response(self):
+ if not self.should_list_locations:
+ return None
+
locations = self.driver.list_locations()
self.assertTrue(isinstance(locations, list))
for dc in locations:
@@ -67,6 +72,19 @@ class TestCaseMixin(object):
ret = self.driver.reboot_node(node)
self.assertTrue(isinstance(ret, bool))
+ def test_get_pricing_success(self):
+ if not self.should_have_pricing:
+ return None
+
+ driver_type = 'compute'
+ try:
+ get_pricing(driver_type=driver_type, driver_name=self.driver.api_name)
+ except KeyError:
+ self.fail("No {driver_type!r} pricing info for {driver}.".format(
+ driver=self.driver.__class__.__name__,
+ driver_type=driver_type,
+ ))
+
if __name__ == "__main__":
import doctest
doctest.testmod()
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_flavors_detail.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_flavors_detail.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_flavors_detail.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_flavors_detail.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<flavors xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <flavor disk="10" ram="256" name="256 slice" id="1"/>
+ <flavor disk="20" ram="512" name="512 slice" id="2"/>
+ <flavor disk="40" ram="1024" name="1GB slice" id="3"/>
+ <flavor disk="80" ram="2048" name="2GB slice" id="4"/>
+ <flavor disk="160" ram="4096" name="4GB slice" id="5"/>
+ <flavor disk="320" ram="8192" name="8GB slice" id="6"/>
+ <flavor disk="620" ram="15872" name="15.5GB slice" id="7"/>
+</flavors>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_detail.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_detail.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_detail.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_detail.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<images xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="CentOS 5.2" id="2"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Gentoo 2008.0" id="3"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Debian 5.0 (lenny)" id="4"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Fedora 10 (Cambridge)" id="5"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="CentOS 5.3" id="7"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Ubuntu 9.04 (jaunty)" id="8"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Arch 2009.02" id="9"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Ubuntu 8.04.2 LTS (hardy)" id="10"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Ubuntu 8.10 (intrepid)" id="11"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Red Hat EL 5.3" id="12"/>
+ <image status="ACTIVE" created="2009-07-20T09:14:37-05:00" updated="2009-07-20T09:14:37-05:00" name="Fedora 11 (Leonidas)" id="13"/>
+ <image status="ACTIVE" progress="100" created="2009-11-29T20:22:09-06:00" updated="2009-11-29T20:24:08-06:00" serverId="91221" name="daily" id="191234"/>
+</images>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_post.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_post.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_post.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_images_post.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<image xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" status="QUEUED" updated="2010-11-19T23:36:58-06:00" serverId="444222" name="imgtest" id="12345"/>
+
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_limits.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_limits.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_limits.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_limits.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,15 @@
+<?xml version="1.0" ?>
+<limits xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <rate>
+ <limit URI="*changes-since*" regex="changes-since" remaining="3" resetTime="1288636970" unit="MINUTE" value="3" verb="GET"/>
+ <limit URI="*" regex=".*" remaining="10" resetTime="1288636970" unit="MINUTE" value="10" verb="PUT"/>
+ <limit URI="*" regex=".*" remaining="10" resetTime="1288636970" unit="MINUTE" value="10" verb="POST"/>
+ <limit URI="*" regex=".*" remaining="600" resetTime="1288636970" unit="MINUTE" value="600" verb="DELETE"/>
+ <limit URI="/servers*" regex="^/servers" remaining="500" resetTime="1288636970" unit="DAY" value="500" verb="POST"/>
+ </rate>
+ <absolute>
+ <limit name="maxIPGroupMembers" value="25"/>
+ <limit name="maxIPGroups" value="25"/>
+ <limit name="maxTotalRAMSize" value="921600"/>
+ </absolute>
+</limits>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" status="BUILD" progress="0" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" adminPass="racktestvJq7d3" id="72258" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+</server>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="72258" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_missing.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_missing.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_missing.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_missing.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="11111" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_pending.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_pending.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_pending.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_pending.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="BUILD" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="12345" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_same_uuid.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_same_uuid.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_same_uuid.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_same_uuid.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="12345" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="12345" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_success.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_success.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_success.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_success.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="12345" name="racktest">
+ <metadata/>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_empty.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_empty.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_empty.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_empty.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"/>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_metadata.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_metadata.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_metadata.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_detail_metadata.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<servers xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <server status="ACTIVE" progress="100" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" id="72258" name="racktest">
+ <metadata>
+ <meta key="somekey">somevalue</meta>
+ </metadata>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+ </server>
+</servers>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_ips.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_ips.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_ips.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_ips.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<addresses xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <public>
+ <ip addr="67.23.10.132"/>
+ <ip addr="67.23.10.131"/>
+ </public>
+ <private>
+ <ip addr="10.176.42.16"/>
+ </private>
+</addresses>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_metadata.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_metadata.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_metadata.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_servers_metadata.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" status="BUILD" progress="0" hostId="9dd380940fcbe39cb30255ed4664f1f3" flavorId="1" imageId="11" adminPass="racktestvJq7d3" id="72258" name="racktest">
+ <metadata>
+ <meta key="a">b</meta>
+ <meta key="c">d</meta>
+ </metadata>
+ <addresses>
+ <public>
+ <ip addr="67.23.21.33"/>
+ </public>
+ <private>
+ <ip addr="10.176.168.218"/>
+ </private>
+ </addresses>
+</server>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_group.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_group.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_group.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_group.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<sharedIpGroup xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" id="1234" name="Shared IP Group 1">
+ <servers>
+ <server id="422"/>
+ </servers>
+</sharedIpGroup>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<sharedIpGroups xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <sharedIpGroup id="1234" name="Shared IP Group 1"/>
+ <sharedIpGroup id="5678" name="Shared IP Group 2"/>
+</sharedIpGroups>
Added: libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups_detail.xml
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups_detail.xml?rev=1166136&view=auto
==============================================================================
--- libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups_detail.xml (added)
+++ libcloud/trunk/test/compute/fixtures/openstack/v1_slug_shared_ip_groups_detail.xml Wed Sep 7 11:58:16 2011
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<sharedIpGroups xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
+ <sharedIpGroup id="1234" name="Shared IP Group 1">
+ <servers>
+ <server id="422" />
+ <server id="3445" />
+ </servers>
+ </sharedIpGroup>
+ <sharedIpGroup id="5678" name="Shared IP Group 2">
+ <servers>
+ <server id="23203"/>
+ <server id="2456" />
+ <server id="9891" />
+ </servers>
+ </sharedIpGroup>
+</sharedIpGroups>
Modified: libcloud/trunk/test/compute/test_deployment.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_deployment.py?rev=1166136&r1=1166135&r2=1166136&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_deployment.py (original)
+++ libcloud/trunk/test/compute/test_deployment.py Wed Sep 7 11:58:16 2011
@@ -24,10 +24,9 @@ from libcloud.compute.deployment import
from libcloud.compute.base import Node
from libcloud.compute.types import NodeState, DeploymentError, LibcloudError
from libcloud.compute.ssh import BaseSSHClient
-from libcloud.compute.drivers.ec2 import EC2NodeDriver
from libcloud.compute.drivers.rackspace import RackspaceNodeDriver as Rackspace
-from test import MockHttp
+from test import MockHttp, XML_HEADERS
from test.file_fixtures import ComputeFileFixtures
from mock import Mock, patch
@@ -43,7 +42,7 @@ class MockClient(BaseSSHClient):
self.stderr = ''
self.exit_status = 0
- def put(self, path, contents, chmod=755):
+ def put(self, path, contents, chmod=755):
return contents
def run(self, name):
@@ -321,7 +320,7 @@ class DeploymentTests(unittest.TestCase)
class RackspaceMockHttp(MockHttp):
- fixtures = ComputeFileFixtures('rackspace')
+ fixtures = ComputeFileFixtures('openstack')
# fake auth token response
def _v1_0(self, method, url, body, headers):
@@ -334,24 +333,24 @@ class RackspaceMockHttp(MockHttp):
def _v1_0_slug_servers_detail(self, method, url, body, headers):
body = self.fixtures.load('v1_slug_servers_detail_deployment_success.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK])
def _v1_0_slug_servers_detail_1_SECOND_DELAY(self, method, url, body, headers):
time.sleep(1)
body = self.fixtures.load('v1_slug_servers_detail_deployment_success.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK])
def _v1_0_slug_servers_detail_TIMEOUT(self, method, url, body, headers):
body = self.fixtures.load('v1_slug_servers_detail_deployment_pending.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK])
def _v1_0_slug_servers_detail_MISSING(self, method, url, body, headers):
body = self.fixtures.load('v1_slug_servers_detail_deployment_missing.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK])
def _v1_0_slug_servers_detail_SAME_UUID(self, method, url, body, headers):
body = self.fixtures.load('v1_slug_servers_detail_deployment_same_uuid.xml')
- return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+ return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK])
if __name__ == '__main__':