You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/11/14 23:51:08 UTC
[21/56] [abbrv] libcloud git commit: Removed sdist
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
----------------------------------------------------------------------
diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
deleted file mode 100644
index 1fa3479..0000000
--- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/openstack.py
+++ /dev/null
@@ -1,2528 +0,0 @@
-# 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
-"""
-from libcloud.utils.iso8601 import parse_date
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-try:
- from lxml import etree as ET
-except ImportError:
- from xml.etree import ElementTree as ET
-
-import warnings
-import base64
-
-from libcloud.utils.py3 import httplib
-from libcloud.utils.py3 import b
-from libcloud.utils.py3 import next
-from libcloud.utils.py3 import urlparse
-
-
-from libcloud.common.openstack import OpenStackBaseConnection
-from libcloud.common.openstack import OpenStackDriverMixin
-from libcloud.common.openstack import OpenStackException
-from libcloud.common.openstack import OpenStackResponse
-from libcloud.utils.networking import is_public_subnet
-from libcloud.compute.base import NodeSize, NodeImage
-from libcloud.compute.base import (NodeDriver, Node, NodeLocation,
- StorageVolume, VolumeSnapshot)
-from libcloud.compute.base import KeyPair
-from libcloud.compute.types import NodeState, StorageVolumeState, Provider, \
- VolumeSnapshotState
-from libcloud.pricing import get_size_price
-from libcloud.utils.xml import findall
-
-__all__ = [
- 'OpenStack_1_0_Response',
- 'OpenStack_1_0_Connection',
- 'OpenStack_1_0_NodeDriver',
- 'OpenStack_1_0_SharedIpGroup',
- 'OpenStack_1_0_NodeIpAddresses',
- 'OpenStack_1_1_Response',
- 'OpenStack_1_1_Connection',
- 'OpenStack_1_1_NodeDriver',
- 'OpenStack_1_1_FloatingIpPool',
- 'OpenStack_1_1_FloatingIpAddress',
- 'OpenStackNodeDriver'
-]
-
-ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
-
-DEFAULT_API_VERSION = '1.1'
-
-
-class OpenStackComputeConnection(OpenStackBaseConnection):
- # default config for http://devstack.org/
- service_type = 'compute'
- service_name = 'nova'
- service_region = 'RegionOne'
-
-
-class OpenStackNodeDriver(NodeDriver, OpenStackDriverMixin):
- """
- Base OpenStack node driver. Should not be used directly.
- """
- api_name = 'openstack'
- name = 'OpenStack'
- website = 'http://openstack.org/'
-
- NODE_STATE_MAP = {
- 'BUILD': NodeState.PENDING,
- 'REBUILD': NodeState.PENDING,
- 'ACTIVE': NodeState.RUNNING,
- 'SUSPENDED': NodeState.STOPPED,
- 'SHUTOFF': NodeState.STOPPED,
- 'DELETED': NodeState.TERMINATED,
- 'QUEUE_RESIZE': NodeState.PENDING,
- 'PREP_RESIZE': NodeState.PENDING,
- 'VERIFY_RESIZE': NodeState.RUNNING,
- 'PASSWORD': NodeState.PENDING,
- 'RESCUE': NodeState.PENDING,
- 'REBOOT': NodeState.REBOOTING,
- 'HARD_REBOOT': NodeState.REBOOTING,
- 'SHARE_IP': NodeState.PENDING,
- 'SHARE_IP_NO_CONFIG': NodeState.PENDING,
- 'DELETE_IP': NodeState.PENDING,
- 'ERROR': NodeState.ERROR,
- 'UNKNOWN': NodeState.UNKNOWN
- }
-
- # http://developer.openstack.org/api-ref-blockstorage-v2.html#volumes-v2
- VOLUME_STATE_MAP = {
- 'creating': StorageVolumeState.CREATING,
- 'available': StorageVolumeState.AVAILABLE,
- 'attaching': StorageVolumeState.ATTACHING,
- 'in-use': StorageVolumeState.INUSE,
- 'deleting': StorageVolumeState.DELETING,
- 'error': StorageVolumeState.ERROR,
- 'error_deleting': StorageVolumeState.ERROR,
- 'backing-up': StorageVolumeState.BACKUP,
- 'restoring-backup': StorageVolumeState.BACKUP,
- 'error_restoring': StorageVolumeState.ERROR,
- 'error_extending': StorageVolumeState.ERROR,
- }
-
- # http://developer.openstack.org/api-ref-blockstorage-v2.html#ext-backups-v2
- SNAPSHOT_STATE_MAP = {
- 'creating': VolumeSnapshotState.CREATING,
- 'available': VolumeSnapshotState.AVAILABLE,
- 'deleting': VolumeSnapshotState.DELETING,
- 'error': VolumeSnapshotState.ERROR,
- 'restoring': VolumeSnapshotState.RESTORING,
- 'error_restoring': VolumeSnapshotState.ERROR
- }
-
- def __new__(cls, key, secret=None, secure=True, host=None, port=None,
- api_version=DEFAULT_API_VERSION, **kwargs):
- if cls is OpenStackNodeDriver:
- if api_version == '1.0':
- cls = OpenStack_1_0_NodeDriver
- elif api_version == '1.1':
- cls = OpenStack_1_1_NodeDriver
- else:
- raise NotImplementedError(
- "No OpenStackNodeDriver found for API version %s" %
- (api_version))
- return super(OpenStackNodeDriver, cls).__new__(cls)
-
- def __init__(self, *args, **kwargs):
- OpenStackDriverMixin.__init__(self, **kwargs)
- super(OpenStackNodeDriver, self).__init__(*args, **kwargs)
-
- def destroy_node(self, node):
- uri = '/servers/%s' % (node.id)
- resp = self.connection.request(uri, method='DELETE')
- # The OpenStack and Rackspace documentation both say this API will
- # return a 204, but in-fact, everyone everywhere agrees it actually
- # returns a 202, so we are going to accept either, and someday,
- # someone will fix either the implementation or the documentation to
- # agree.
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def reboot_node(self, node):
- return self._reboot_node(node, reboot_type='HARD')
-
- def list_nodes(self, ex_all_tenants=False):
- """
- List the nodes in a tenant
-
- :param ex_all_tenants: List nodes for all the tenants. Note: Your user
- must have admin privileges for this
- functionality to work.
- :type ex_all_tenants: ``bool``
- """
- params = {}
- if ex_all_tenants:
- params = {'all_tenants': 1}
- return self._to_nodes(
- self.connection.request('/servers/detail', params=params).object)
-
- def create_volume(self, size, name, location=None, snapshot=None,
- ex_volume_type=None):
- """
- Create a new volume.
-
- :param size: Size of volume in gigabytes (required)
- :type size: ``int``
-
- :param name: Name of the volume to be created
- :type name: ``str``
-
- :param location: Which data center to create a volume in. If
- empty, undefined behavior will be selected.
- (optional)
- :type location: :class:`.NodeLocation`
-
- :param snapshot: Snapshot from which to create the new
- volume. (optional)
- :type snapshot: :class:`.VolumeSnapshot`
-
- :param ex_volume_type: What kind of volume to create.
- (optional)
- :type ex_volume_type: ``str``
-
- :return: The newly created volume.
- :rtype: :class:`StorageVolume`
- """
- volume = {
- 'display_name': name,
- 'display_description': name,
- 'size': size,
- 'volume_type': ex_volume_type,
- 'metadata': {
- 'contents': name,
- },
- 'availability_zone': location
- }
-
- if snapshot:
- volume['snapshot_id'] = snapshot.id
-
- resp = self.connection.request('/os-volumes',
- method='POST',
- data={'volume': volume})
- return self._to_volume(resp.object)
-
- def destroy_volume(self, volume):
- return self.connection.request('/os-volumes/%s' % volume.id,
- method='DELETE').success()
-
- def attach_volume(self, node, volume, device="auto"):
- # when "auto" or None is provided for device, openstack will let
- # the guest OS pick the next available device (fi. /dev/vdb)
- return self.connection.request(
- '/servers/%s/os-volume_attachments' % node.id,
- method='POST',
- data={
- 'volumeAttachment': {
- 'volumeId': volume.id,
- 'device': device,
- }
- }).success()
-
- def detach_volume(self, volume, ex_node=None):
- # when ex_node is not provided, volume is detached from all nodes
- failed_nodes = []
- for attachment in volume.extra['attachments']:
- if not ex_node or ex_node.id == attachment['serverId']:
- response = self.connection.request(
- '/servers/%s/os-volume_attachments/%s' %
- (attachment['serverId'], attachment['id']),
- method='DELETE')
-
- if not response.success():
- failed_nodes.append(attachment['serverId'])
- if failed_nodes:
- raise OpenStackException(
- 'detach_volume failed for nodes with id: %s' %
- ', '.join(failed_nodes), 500, self
- )
- return True
-
- def list_volumes(self):
- return self._to_volumes(
- self.connection.request('/os-volumes').object)
-
- def ex_get_volume(self, volumeId):
- return self._to_volume(
- self.connection.request('/os-volumes/%s' % volumeId).object)
-
- def list_images(self, location=None, ex_only_active=True):
- """
- Lists all active images
-
- @inherits: :class:`NodeDriver.list_images`
-
- :param ex_only_active: True if list only active
- :type ex_only_active: ``bool``
-
- """
- return self._to_images(
- self.connection.request('/images/detail').object, ex_only_active)
-
- def get_image(self, image_id):
- """
- Get an image based on an image_id
-
- @inherits: :class:`NodeDriver.get_image`
-
- :param image_id: Image identifier
- :type image_id: ``str``
-
- :return: A NodeImage object
- :rtype: :class:`NodeImage`
-
- """
- return self._to_image(self.connection.request(
- '/images/%s' % (image_id,)).object['image'])
-
- def list_sizes(self, location=None):
- return self._to_sizes(
- self.connection.request('/flavors/detail').object)
-
- def list_locations(self):
- return [NodeLocation(0, '', '', self)]
-
- def _ex_connection_class_kwargs(self):
- return self.openstack_connection_kwargs()
-
- def ex_get_node_details(self, node_id):
- """
- Lists details of the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: :class:`Node`
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- uri = '/servers/%s' % (node_id)
- resp = self.connection.request(uri, method='GET')
- if resp.status == httplib.NOT_FOUND:
- return None
-
- return self._to_node_from_obj(resp.object)
-
- def ex_soft_reboot_node(self, node):
- """
- Soft reboots the specified server
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- return self._reboot_node(node, reboot_type='SOFT')
-
- def ex_hard_reboot_node(self, node):
- """
- Hard reboots the specified server
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- return self._reboot_node(node, reboot_type='HARD')
-
-
-class OpenStackNodeSize(NodeSize):
- """
- NodeSize class for the OpenStack.org driver.
-
- Following the example of OpenNebula.org driver
- and following guidelines:
- https://issues.apache.org/jira/browse/LIBCLOUD-119
- """
-
- def __init__(self, id, name, ram, disk, bandwidth, price, driver,
- vcpus=None, ephemeral_disk=None, swap=None, extra=None):
- super(OpenStackNodeSize, self).__init__(id=id, name=name, ram=ram,
- disk=disk,
- bandwidth=bandwidth,
- price=price, driver=driver)
- self.vcpus = vcpus
- self.ephemeral_disk = ephemeral_disk
- self.swap = swap
- self.extra = extra
-
- def __repr__(self):
- return (('<OpenStackNodeSize: id=%s, name=%s, ram=%s, disk=%s, '
- 'bandwidth=%s, price=%s, driver=%s, vcpus=%s, ...>')
- % (self.id, self.name, self.ram, self.disk, self.bandwidth,
- self.price, self.driver.name, self.vcpus))
-
-
-class OpenStack_1_0_Response(OpenStackResponse):
- def __init__(self, *args, **kwargs):
- # done because of a circular reference from
- # NodeDriver -> Connection -> Response
- self.node_driver = OpenStack_1_0_NodeDriver
- super(OpenStack_1_0_Response, self).__init__(*args, **kwargs)
-
-
-class OpenStack_1_0_Connection(OpenStackComputeConnection):
- responseCls = OpenStack_1_0_Response
- default_content_type = 'application/xml; charset=UTF-8'
- accept_format = 'application/xml'
- XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0'
-
-
-class OpenStack_1_0_NodeDriver(OpenStackNodeDriver):
- """
- OpenStack node driver.
-
- 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 = OpenStack_1_0_Connection
- type = Provider.OPENSTACK
-
- features = {'create_node': ['generates_password']}
-
- def __init__(self, *args, **kwargs):
- self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
- None))
- self.XML_NAMESPACE = self.connectionCls.XML_NAMESPACE
- super(OpenStack_1_0_NodeDriver, self).__init__(*args, **kwargs)
-
- def _to_images(self, object, ex_only_active):
- images = []
- for image in findall(object, 'image', self.XML_NAMESPACE):
- if ex_only_active and image.get('status') != 'ACTIVE':
- continue
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, element):
- return NodeImage(id=element.get('id'),
- name=element.get('name'),
- driver=self.connection.driver,
- extra={'updated': element.get('updated'),
- 'created': element.get('created'),
- 'status': element.get('status'),
- 'serverId': element.get('serverId'),
- 'progress': element.get('progress'),
- 'minDisk': element.get('minDisk'),
- 'minRam': element.get('minRam')
- }
- )
-
- def _change_password_or_name(self, node, name=None, password=None):
- uri = '/servers/%s' % (node.id)
-
- if not name:
- name = node.name
-
- body = {'xmlns': self.XML_NAMESPACE,
- 'name': name}
-
- if password is not 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 == httplib.NO_CONTENT and password is not None:
- node.extra['password'] = password
-
- return resp.status == httplib.NO_CONTENT
-
- def create_node(self, **kwargs):
- """
- Create a new node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the node
- :type ex_files: ``dict``
-
- :keyword ex_shared_ip_group_id: The server is launched into
- that shared IP group
- :type ex_shared_ip_group_id: ``str``
- """
- name = kwargs['name']
- image = kwargs['image']
- size = kwargs['size']
-
- attributes = {'xmlns': self.XML_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_set_password(self, node, password):
- """
- Sets the Node's root password.
-
- This will reboot the instance to complete the operation.
-
- :class:`Node.extra['password']` will be set to the new value if the
- operation was successful.
-
- :param node: node to set password
- :type node: :class:`Node`
-
- :param password: new password.
- :type password: ``str``
-
- :rtype: ``bool``
- """
- 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.
-
- :param node: node to set name
- :type node: :class:`Node`
-
- :param name: new name
- :type name: ``str``
-
- :rtype: ``bool``
- """
- return self._change_password_or_name(node, name=name)
-
- def ex_resize(self, node, size):
- """
- Change an existing server flavor / scale the server up or down.
-
- :param node: node to resize.
- :type node: :class:`Node`
-
- :param size: new size.
- :type size: :class:`NodeSize`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'resize',
- {'xmlns': self.XML_NAMESPACE,
- 'flavorId': str(size.id)}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.ACCEPTED
-
- 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
-
- :param node: node for which the resize request will be confirmed.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'confirmResize',
- {'xmlns': self.XML_NAMESPACE},
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.NO_CONTENT
-
- 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
-
- :param node: node for which the resize request will be reverted.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- elm = ET.Element(
- 'revertResize',
- {'xmlns': self.XML_NAMESPACE}
- )
-
- resp = self.connection.request("/servers/%s/action" % (node.id),
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.NO_CONTENT
-
- def ex_rebuild(self, node_id, image_id):
- """
- Rebuilds the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param image_id: ID of the image which should be used
- :type image_id: ``str``
-
- :rtype: ``bool``
- """
- # @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': self.XML_NAMESPACE,
- 'imageId': image_id}
- )
-
- resp = self.connection.request("/servers/%s/action" % node_id,
- method='POST',
- data=ET.tostring(elm))
- return resp.status == httplib.ACCEPTED
-
- def ex_create_ip_group(self, group_name, node_id=None):
- """
- Creates a shared IP group.
-
- :param group_name: group name which should be used
- :type group_name: ``str``
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: ``bool``
- """
- # @TODO: Remove this if in 0.6
- if isinstance(node_id, Node):
- node_id = node_id.id
-
- group_elm = ET.Element(
- 'sharedIpGroup',
- {'xmlns': self.XML_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):
- """
- Lists IDs and names for shared IP groups.
- If details lists all details for shared IP groups.
-
- :param details: True if details is required
- :type details: ``bool``
-
- :rtype: ``list`` of :class:`OpenStack_1_0_SharedIpGroup`
- """
- uri = '/shared_ip_groups/detail' if details else '/shared_ip_groups'
- resp = self.connection.request(uri,
- method='GET')
- groups = findall(resp.object, 'sharedIpGroup',
- self.XML_NAMESPACE)
- return [self._to_shared_ip_group(el) for el in groups]
-
- def ex_delete_ip_group(self, group_id):
- """
- Deletes the specified shared IP group.
-
- :param group_id: group id which should be used
- :type group_id: ``str``
-
- :rtype: ``bool``
- """
- uri = '/shared_ip_groups/%s' % group_id
- resp = self.connection.request(uri, method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def ex_share_ip(self, group_id, node_id, ip, configure_node=True):
- """
- Shares an IP address to the specified server.
-
- :param group_id: group id which should be used
- :type group_id: ``str``
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param ip: ip which should be used
- :type ip: ``str``
-
- :param configure_node: configure node
- :type configure_node: ``bool``
-
- :rtype: ``bool``
- """
- # @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': self.XML_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 == httplib.ACCEPTED
-
- def ex_unshare_ip(self, node_id, ip):
- """
- Removes a shared IP address from the specified server.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :param ip: ip which should be used
- :type ip: ``str``
-
- :rtype: ``bool``
- """
- # @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 == httplib.ACCEPTED
-
- def ex_list_ip_addresses(self, node_id):
- """
- List all server addresses.
-
- :param node_id: ID of the node which should be used
- :type node_id: ``str``
-
- :rtype: :class:`OpenStack_1_0_NodeIpAddresses`
- """
- # @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 list(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 list(files.items()):
- file_elm = ET.SubElement(personality_elm,
- 'file',
- {'path': str(k)})
- file_elm.text = base64.b64encode(b(v))
-
- return personality_elm
-
- def _reboot_node(self, node, reboot_type='SOFT'):
- resp = self._node_action(node, ['reboot', ('type', reboot_type)])
- return resp.status == httplib.ACCEPTED
-
- 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], self.XML_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 = findall(object, 'server', self.XML_NAMESPACE)
- return [self._to_node(el) for el in node_elements]
-
- def _to_node_from_obj(self, obj):
- return self._to_node(findall(obj, 'server', self.XML_NAMESPACE)[0])
-
- 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(findall(el, 'addresses/public/ip',
- self.XML_NAMESPACE))
- private_ip = get_ips(findall(el, 'addresses/private/ip',
- self.XML_NAMESPACE))
- metadata = get_meta_dict(findall(el, 'metadata/meta',
- self.XML_NAMESPACE))
-
- n = Node(id=el.get('id'),
- name=el.get('name'),
- state=self.NODE_STATE_MAP.get(
- el.get('status'), NodeState.UNKNOWN),
- public_ips=public_ip,
- private_ips=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')),
- 'service_name': self.connection.get_service_name(),
- 'metadata': metadata})
- return n
-
- def _to_sizes(self, object):
- elements = findall(object, 'flavor', self.XML_NAMESPACE)
- return [self._to_size(el) for el in elements]
-
- def _to_size(self, el):
- vcpus = int(el.get('vcpus')) if el.get('vcpus', None) else None
- return OpenStackNodeSize(id=el.get('id'),
- name=el.get('name'),
- ram=int(el.get('ram')),
- disk=int(el.get('disk')),
- # XXX: needs hardcode
- vcpus=vcpus,
- bandwidth=None,
- # Hardcoded
- price=self._get_size_price(el.get('id')),
- driver=self.connection.driver)
-
- 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: dict with keys 'rate' and 'absolute'
- :rtype: ``dict``
- """
-
- def _to_rate(el):
- rate = {}
- for item in list(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 findall(limits, 'rate/limit',
- self.XML_NAMESPACE)]
- absolute = {}
- for item in findall(limits, 'absolute/limit',
- self.XML_NAMESPACE):
- absolute.update(_to_absolute(item))
-
- return {"rate": rate, "absolute": absolute}
-
- def create_image(self, node, name, description=None, reboot=True):
- """Create an image for node.
-
- @inherits: :class:`NodeDriver.create_image`
-
- :param node: node to use as a base for image
- :type node: :class:`Node`
-
- :param name: name for new image
- :type name: ``str``
-
- :rtype: :class:`NodeImage`
- """
-
- image_elm = ET.Element(
- 'image',
- {'xmlns': self.XML_NAMESPACE,
- 'name': name,
- 'serverId': node.id}
- )
-
- return self._to_image(
- self.connection.request("/images", method="POST",
- data=ET.tostring(image_elm)).object)
-
- def delete_image(self, image):
- """Delete an image for node.
-
- @inherits: :class:`NodeDriver.delete_image`
-
- :param image: the image to be deleted
- :type image: :class:`NodeImage`
-
- :rtype: ``bool``
- """
- uri = '/images/%s' % image.id
- resp = self.connection.request(uri, method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _to_shared_ip_group(self, el):
- servers_el = findall(el, 'servers', self.XML_NAMESPACE)
- if servers_el:
- servers = [s.get('id')
- for s in findall(servers_el[0], 'server',
- self.XML_NAMESPACE)]
- else:
- servers = None
- return OpenStack_1_0_SharedIpGroup(id=el.get('id'),
- name=el.get('name'),
- servers=servers)
-
- def _to_ip_addresses(self, el):
- public_ips = [ip.get('addr') for ip in findall(
- findall(el, 'public', self.XML_NAMESPACE)[0],
- 'ip', self.XML_NAMESPACE)]
- private_ips = [ip.get('addr') for ip in findall(
- findall(el, 'private', self.XML_NAMESPACE)[0],
- 'ip', self.XML_NAMESPACE)]
-
- return OpenStack_1_0_NodeIpAddresses(public_ips, private_ips)
-
- def _get_size_price(self, size_id):
- try:
- return get_size_price(driver_type='compute',
- driver_name=self.api_name,
- size_id=size_id)
- except KeyError:
- return 0.0
-
-
-class OpenStack_1_0_SharedIpGroup(object):
- """
- Shared IP group info.
- """
-
- def __init__(self, id, name, servers=None):
- self.id = str(id)
- self.name = name
- self.servers = servers
-
-
-class OpenStack_1_0_NodeIpAddresses(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 OpenStack_1_1_Response(OpenStackResponse):
- def __init__(self, *args, **kwargs):
- # done because of a circular reference from
- # NodeDriver -> Connection -> Response
- self.node_driver = OpenStack_1_1_NodeDriver
- super(OpenStack_1_1_Response, self).__init__(*args, **kwargs)
-
-
-class OpenStackNetwork(object):
- """
- A Virtual Network.
- """
-
- def __init__(self, id, name, cidr, driver, extra=None):
- self.id = str(id)
- self.name = name
- self.cidr = cidr
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return '<OpenStackNetwork id="%s" name="%s" cidr="%s">' % (self.id,
- self.name,
- self.cidr,)
-
-
-class OpenStackSecurityGroup(object):
- """
- A Security Group.
- """
-
- def __init__(self, id, tenant_id, name, description, driver, rules=None,
- extra=None):
- """
- Constructor.
-
- :keyword id: Group id.
- :type id: ``str``
-
- :keyword tenant_id: Owner of the security group.
- :type tenant_id: ``str``
-
- :keyword name: Human-readable name for the security group. Might
- not be unique.
- :type name: ``str``
-
- :keyword description: Human-readable description of a security
- group.
- :type description: ``str``
-
- :keyword rules: Rules associated with this group.
- :type rules: ``list`` of
- :class:`OpenStackSecurityGroupRule`
-
- :keyword extra: Extra attributes associated with this group.
- :type extra: ``dict``
- """
- self.id = id
- self.tenant_id = tenant_id
- self.name = name
- self.description = description
- self.driver = driver
- self.rules = rules or []
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackSecurityGroup id=%s tenant_id=%s name=%s \
- description=%s>' % (self.id, self.tenant_id, self.name,
- self.description))
-
-
-class OpenStackSecurityGroupRule(object):
- """
- A Rule of a Security Group.
- """
-
- def __init__(self, id, parent_group_id, ip_protocol, from_port, to_port,
- driver, ip_range=None, group=None, tenant_id=None,
- extra=None):
- """
- Constructor.
-
- :keyword id: Rule id.
- :type id: ``str``
-
- :keyword parent_group_id: ID of the parent security group.
- :type parent_group_id: ``str``
-
- :keyword ip_protocol: IP Protocol (icmp, tcp, udp, etc).
- :type ip_protocol: ``str``
-
- :keyword from_port: Port at start of range.
- :type from_port: ``int``
-
- :keyword to_port: Port at end of range.
- :type to_port: ``int``
-
- :keyword ip_range: CIDR for address range.
- :type ip_range: ``str``
-
- :keyword group: Name of a source security group to apply to rule.
- :type group: ``str``
-
- :keyword tenant_id: Owner of the security group.
- :type tenant_id: ``str``
-
- :keyword extra: Extra attributes associated with this rule.
- :type extra: ``dict``
- """
- self.id = id
- self.parent_group_id = parent_group_id
- self.ip_protocol = ip_protocol
- self.from_port = from_port
- self.to_port = to_port
- self.driver = driver
- self.ip_range = ''
- self.group = {}
-
- if group is None:
- self.ip_range = ip_range
- else:
- self.group = {'name': group, 'tenant_id': tenant_id}
-
- self.tenant_id = tenant_id
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackSecurityGroupRule id=%s parent_group_id=%s \
- ip_protocol=%s from_port=%s to_port=%s>' % (self.id,
- self.parent_group_id, self.ip_protocol, self.from_port,
- self.to_port))
-
-
-class OpenStackKeyPair(object):
- """
- A KeyPair.
- """
-
- def __init__(self, name, fingerprint, public_key, driver, private_key=None,
- extra=None):
- """
- Constructor.
-
- :keyword name: Name of the KeyPair.
- :type name: ``str``
-
- :keyword fingerprint: Fingerprint of the KeyPair
- :type fingerprint: ``str``
-
- :keyword public_key: Public key in OpenSSH format.
- :type public_key: ``str``
-
- :keyword private_key: Private key in PEM format.
- :type private_key: ``str``
-
- :keyword extra: Extra attributes associated with this KeyPair.
- :type extra: ``dict``
- """
- self.name = name
- self.fingerprint = fingerprint
- self.public_key = public_key
- self.private_key = private_key
- self.driver = driver
- self.extra = extra or {}
-
- def __repr__(self):
- return ('<OpenStackKeyPair name=%s fingerprint=%s public_key=%s ...>'
- % (self.name, self.fingerprint, self.public_key))
-
-
-class OpenStack_1_1_Connection(OpenStackComputeConnection):
- responseCls = OpenStack_1_1_Response
- accept_format = 'application/json'
- default_content_type = 'application/json; charset=UTF-8'
-
- def encode_data(self, data):
- return json.dumps(data)
-
-
-class OpenStack_1_1_NodeDriver(OpenStackNodeDriver):
- """
- OpenStack node driver.
- """
- connectionCls = OpenStack_1_1_Connection
- type = Provider.OPENSTACK
-
- features = {"create_node": ["generates_password"]}
- _networks_url_prefix = '/os-networks'
-
- def __init__(self, *args, **kwargs):
- self._ex_force_api_version = str(kwargs.pop('ex_force_api_version',
- None))
- super(OpenStack_1_1_NodeDriver, self).__init__(*args, **kwargs)
-
- def create_node(self, **kwargs):
- """Create a new node
-
- @inherits: :class:`NodeDriver.create_node`
-
- :keyword ex_keyname: The name of the key pair
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: String containing user data
- see
- https://help.ubuntu.com/community/CloudInit
- :type ex_userdata: ``str``
-
- :keyword ex_config_drive: Enable config drive
- see
- http://docs.openstack.org/grizzly/openstack-compute/admin/content/config-drive.html
- :type ex_config_drive: ``bool``
-
- :keyword ex_security_groups: List of security groups to assign to
- the node
- :type ex_security_groups: ``list`` of
- :class:`OpenStackSecurityGroup`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the no de
- :type ex_files: ``dict``
-
-
- :keyword networks: The server is launched into a set of Networks.
- :type networks: ``list`` of :class:`OpenStackNetwork`
-
- :keyword ex_disk_config: Name of the disk configuration.
- Can be either ``AUTO`` or ``MANUAL``.
- :type ex_disk_config: ``str``
-
- :keyword ex_config_drive: If True enables metadata injection in a
- server through a configuration drive.
- :type ex_config_drive: ``bool``
-
- :keyword ex_admin_pass: The root password for the node
- :type ex_admin_pass: ``str``
-
- :keyword ex_availability_zone: Nova availability zone for the node
- :type ex_availability_zone: ``str``
- """
-
- server_params = self._create_args_to_params(None, **kwargs)
-
- resp = self.connection.request("/servers",
- method='POST',
- data={'server': server_params})
-
- create_response = resp.object['server']
- server_resp = self.connection.request(
- '/servers/%s' % create_response['id'])
- server_object = server_resp.object['server']
-
- # adminPass is not always present
- # http://docs.openstack.org/essex/openstack-compute/admin/
- # content/configuring-compute-API.html#d6e1833
- server_object['adminPass'] = create_response.get('adminPass', None)
-
- return self._to_node(server_object)
-
- def _to_images(self, obj, ex_only_active):
- images = []
- for image in obj['images']:
- if ex_only_active and image.get('status') != 'ACTIVE':
- continue
- images.append(self._to_image(image))
-
- return images
-
- def _to_image(self, api_image):
- server = api_image.get('server', {})
- return NodeImage(
- id=api_image['id'],
- name=api_image['name'],
- driver=self,
- extra=dict(
- updated=api_image['updated'],
- created=api_image['created'],
- status=api_image['status'],
- progress=api_image.get('progress'),
- metadata=api_image.get('metadata'),
- serverId=server.get('id'),
- minDisk=api_image.get('minDisk'),
- minRam=api_image.get('minRam'),
- )
- )
-
- def _to_nodes(self, obj):
- servers = obj['servers']
- return [self._to_node(server) for server in servers]
-
- def _to_volumes(self, obj):
- volumes = obj['volumes']
- return [self._to_volume(volume) for volume in volumes]
-
- def _to_snapshots(self, obj):
- snapshots = obj['snapshots']
- return [self._to_snapshot(snapshot) for snapshot in snapshots]
-
- def _to_sizes(self, obj):
- flavors = obj['flavors']
- return [self._to_size(flavor) for flavor in flavors]
-
- def _create_args_to_params(self, node, **kwargs):
- server_params = {
- 'name': kwargs.get('name'),
- 'metadata': kwargs.get('ex_metadata', {}),
- 'personality': self._files_to_personality(kwargs.get("ex_files",
- {}))
- }
-
- if 'ex_availability_zone' in kwargs:
- server_params['availability_zone'] = kwargs['ex_availability_zone']
-
- if 'ex_keyname' in kwargs:
- server_params['key_name'] = kwargs['ex_keyname']
-
- if 'ex_userdata' in kwargs:
- server_params['user_data'] = base64.b64encode(
- b(kwargs['ex_userdata'])).decode('ascii')
-
- if 'ex_config_drive' in kwargs:
- server_params['config_drive'] = kwargs['ex_config_drive']
-
- if 'ex_disk_config' in kwargs:
- server_params['OS-DCF:diskConfig'] = kwargs['ex_disk_config']
-
- if 'ex_config_drive' in kwargs:
- server_params['config_drive'] = str(kwargs['ex_config_drive'])
-
- if 'ex_admin_pass' in kwargs:
- server_params['adminPass'] = kwargs['ex_admin_pass']
-
- if 'networks' in kwargs:
- networks = kwargs['networks']
- networks = [{'uuid': network.id} for network in networks]
- server_params['networks'] = networks
-
- if 'ex_security_groups' in kwargs:
- server_params['security_groups'] = []
- for security_group in kwargs['ex_security_groups']:
- name = security_group.name
- server_params['security_groups'].append({'name': name})
-
- if 'ex_blockdevicemappings' in kwargs:
- server_params['block_device_mapping_v2'] = \
- kwargs['ex_blockdevicemappings']
-
- if 'name' in kwargs:
- server_params['name'] = kwargs.get('name')
- else:
- server_params['name'] = node.name
-
- if 'image' in kwargs:
- server_params['imageRef'] = kwargs.get('image').id
- else:
- server_params['imageRef'] = node.extra.get('imageId')
-
- if 'size' in kwargs:
- server_params['flavorRef'] = kwargs.get('size').id
- else:
- server_params['flavorRef'] = node.extra.get('flavorId')
-
- return server_params
-
- def _files_to_personality(self, files):
- rv = []
-
- for k, v in list(files.items()):
- rv.append({'path': k, 'contents': base64.b64encode(b(v))})
-
- return rv
-
- def _reboot_node(self, node, reboot_type='SOFT'):
- resp = self._node_action(node, 'reboot', type=reboot_type)
- return resp.status == httplib.ACCEPTED
-
- def ex_set_password(self, node, password):
- """
- Changes the administrator password for a specified server.
-
- :param node: Node to rebuild.
- :type node: :class:`Node`
-
- :param password: The administrator password.
- :type password: ``str``
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'changePassword', adminPass=password)
- node.extra['password'] = password
- return resp.status == httplib.ACCEPTED
-
- def ex_rebuild(self, node, image, **kwargs):
- """
- Rebuild a Node.
-
- :param node: Node to rebuild.
- :type node: :class:`Node`
-
- :param image: New image to use.
- :type image: :class:`NodeImage`
-
- :keyword ex_metadata: Key/Value metadata to associate with a node
- :type ex_metadata: ``dict``
-
- :keyword ex_files: File Path => File contents to create on
- the no de
- :type ex_files: ``dict``
-
- :keyword ex_keyname: Name of existing public key to inject into
- instance
- :type ex_keyname: ``str``
-
- :keyword ex_userdata: String containing user data
- see
- https://help.ubuntu.com/community/CloudInit
- :type ex_userdata: ``str``
-
- :keyword ex_security_groups: List of security groups to assign to
- the node
- :type ex_security_groups: ``list`` of
- :class:`OpenStackSecurityGroup`
-
- :keyword ex_disk_config: Name of the disk configuration.
- Can be either ``AUTO`` or ``MANUAL``.
- :type ex_disk_config: ``str``
-
- :keyword ex_config_drive: If True enables metadata injection in a
- server through a configuration drive.
- :type ex_config_drive: ``bool``
-
- :rtype: ``bool``
- """
- server_params = self._create_args_to_params(node, image=image,
- **kwargs)
- resp = self._node_action(node, 'rebuild', **server_params)
- return resp.status == httplib.ACCEPTED
-
- def ex_resize(self, node, size):
- """
- Change a node size.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :type size: :class:`NodeSize`
- :param size: New size to use.
-
- :rtype: ``bool``
- """
- server_params = self._create_args_to_params(node, size=size)
- resp = self._node_action(node, 'resize', **server_params)
- return resp.status == httplib.ACCEPTED
-
- def ex_confirm_resize(self, node):
- """
- Confirms a pending resize action.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'confirmResize')
- return resp.status == httplib.NO_CONTENT
-
- def ex_revert_resize(self, node):
- """
- Cancels and reverts a pending resize action.
-
- :param node: Node to resize.
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'revertResize')
- return resp.status == httplib.ACCEPTED
-
- def create_image(self, node, name, metadata=None):
- """
- Creates a new image.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param name: The name for the new image.
- :type name: ``str``
-
- :param metadata: Key and value pairs for metadata.
- :type metadata: ``dict``
-
- :rtype: :class:`NodeImage`
- """
- optional_params = {}
- if metadata:
- optional_params['metadata'] = metadata
- resp = self._node_action(node, 'createImage', name=name,
- **optional_params)
- image_id = self._extract_image_id_from_url(resp.headers['location'])
- return self.get_image(image_id=image_id)
-
- def ex_set_server_name(self, node, name):
- """
- Sets the Node's name.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param name: The name of the server.
- :type name: ``str``
-
- :rtype: :class:`Node`
- """
- return self._update_node(node, name=name)
-
- def ex_get_metadata(self, node):
- """
- Get a Node's metadata.
-
- :param node: Node
- :type node: :class:`Node`
-
- :return: Key/Value metadata associated with node.
- :rtype: ``dict``
- """
- return self.connection.request(
- '/servers/%s/metadata' % (node.id,),
- method='GET',).object['metadata']
-
- def ex_set_metadata(self, node, metadata):
- """
- Sets the Node's metadata.
-
- :param node: Node
- :type node: :class:`Node`
-
- :param metadata: Key/Value metadata to associate with a node
- :type metadata: ``dict``
-
- :rtype: ``dict``
- """
- return self.connection.request(
- '/servers/%s/metadata' % (node.id,), method='PUT',
- data={'metadata': metadata}
- ).object['metadata']
-
- def ex_update_node(self, node, **node_updates):
- """
- Update the Node's editable attributes. The OpenStack API currently
- supports editing name and IPv4/IPv6 access addresses.
-
- The driver currently only supports updating the node name.
-
- :param node: Node
- :type node: :class:`Node`
-
- :keyword name: New name for the server
- :type name: ``str``
-
- :rtype: :class:`Node`
- """
- potential_data = self._create_args_to_params(node, **node_updates)
- updates = {'name': potential_data['name']}
- return self._update_node(node, **updates)
-
- def _to_networks(self, obj):
- networks = obj['networks']
- return [self._to_network(network) for network in networks]
-
- def _to_network(self, obj):
- return OpenStackNetwork(id=obj['id'],
- name=obj['label'],
- cidr=obj.get('cidr', None),
- driver=self)
-
- def ex_list_networks(self):
- """
- Get a list of Networks that are available.
-
- :rtype: ``list`` of :class:`OpenStackNetwork`
- """
- response = self.connection.request(self._networks_url_prefix).object
- return self._to_networks(response)
-
- def ex_create_network(self, name, cidr):
- """
- Create a new Network
-
- :param name: Name of network which should be used
- :type name: ``str``
-
- :param cidr: cidr of network which should be used
- :type cidr: ``str``
-
- :rtype: :class:`OpenStackNetwork`
- """
- data = {'network': {'cidr': cidr, 'label': name}}
- response = self.connection.request(self._networks_url_prefix,
- method='POST', data=data).object
- return self._to_network(response['network'])
-
- def ex_delete_network(self, network):
- """
- Get a list of NodeNetorks that are available.
-
- :param network: Network which should be used
- :type network: :class:`OpenStackNetwork`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('%s/%s' % (self._networks_url_prefix,
- network.id),
- method='DELETE')
- return resp.status == httplib.ACCEPTED
-
- def ex_get_console_output(self, node, length=None):
- """
- Get console output
-
- :param node: node
- :type node: :class:`Node`
-
- :param length: Optional number of lines to fetch from the
- console log
- :type length: ``int``
-
- :return: Dictionary with the output
- :rtype: ``dict``
- """
-
- data = {
- "os-getConsoleOutput": {
- "length": length
- }
- }
-
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data).object
- return resp
-
- def ex_list_snapshots(self):
- return self._to_snapshots(
- self.connection.request('/os-snapshots').object)
-
- def list_volume_snapshots(self, volume):
- return [snapshot for snapshot in self.ex_list_snapshots()
- if snapshot.extra['volume_id'] == volume.id]
-
- def create_volume_snapshot(self, volume, name=None, ex_description=None,
- ex_force=True):
- """
- Create snapshot from volume
-
- :param volume: Instance of `StorageVolume`
- :type volume: `StorageVolume`
-
- :param name: Name of snapshot (optional)
- :type name: `str`
-
- :param ex_description: Description of the snapshot (optional)
- :type ex_description: `str`
-
- :param ex_force: Specifies if we create a snapshot that is not in
- state `available`. For example `in-use`. Defaults
- to True. (optional)
- :type ex_force: `bool`
-
- :rtype: :class:`VolumeSnapshot`
- """
- data = {'snapshot': {'display_name': name,
- 'display_description': ex_description,
- 'volume_id': volume.id,
- 'force': ex_force}}
-
- return self._to_snapshot(self.connection.request('/os-snapshots',
- method='POST',
- data=data).object)
-
- def destroy_volume_snapshot(self, snapshot):
- resp = self.connection.request('/os-snapshots/%s' % snapshot.id,
- method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def ex_create_snapshot(self, volume, name, description=None, force=False):
- """
- Create a snapshot based off of a volume.
-
- :param volume: volume
- :type volume: :class:`StorageVolume`
-
- :keyword name: New name for the volume snapshot
- :type name: ``str``
-
- :keyword description: Description of the snapshot (optional)
- :type description: ``str``
-
- :keyword force: Whether to force creation (optional)
- :type force: ``bool``
-
- :rtype: :class:`VolumeSnapshot`
- """
- warnings.warn('This method has been deprecated in favor of the '
- 'create_volume_snapshot method')
- return self.create_volume_snapshot(volume, name,
- ex_description=description,
- ex_force=force)
-
- def ex_delete_snapshot(self, snapshot):
- """
- Delete a VolumeSnapshot
-
- :param snapshot: snapshot
- :type snapshot: :class:`VolumeSnapshot`
-
- :rtype: ``bool``
- """
- warnings.warn('This method has been deprecated in favor of the '
- 'destroy_volume_snapshot method')
- return self.destroy_volume_snapshot(snapshot)
-
- def _to_security_group_rules(self, obj):
- return [self._to_security_group_rule(security_group_rule) for
- security_group_rule in obj]
-
- def _to_security_group_rule(self, obj):
- ip_range = group = tenant_id = None
- if obj['group'] == {}:
- ip_range = obj['ip_range'].get('cidr', None)
- else:
- group = obj['group'].get('name', None)
- tenant_id = obj['group'].get('tenant_id', None)
-
- return OpenStackSecurityGroupRule(
- id=obj['id'], parent_group_id=obj['parent_group_id'],
- ip_protocol=obj['ip_protocol'], from_port=obj['from_port'],
- to_port=obj['to_port'], driver=self, ip_range=ip_range,
- group=group, tenant_id=tenant_id)
-
- def _to_security_groups(self, obj):
- security_groups = obj['security_groups']
- return [self._to_security_group(security_group) for security_group in
- security_groups]
-
- def _to_security_group(self, obj):
- rules = self._to_security_group_rules(obj.get('rules', []))
- return OpenStackSecurityGroup(id=obj['id'],
- tenant_id=obj['tenant_id'],
- name=obj['name'],
- description=obj.get('description', ''),
- rules=rules,
- driver=self)
-
- def ex_list_security_groups(self):
- """
- Get a list of Security Groups that are available.
-
- :rtype: ``list`` of :class:`OpenStackSecurityGroup`
- """
- return self._to_security_groups(
- self.connection.request('/os-security-groups').object)
-
- def ex_get_node_security_groups(self, node):
- """
- Get Security Groups of the specified server.
-
- :rtype: ``list`` of :class:`OpenStackSecurityGroup`
- """
- return self._to_security_groups(
- self.connection.request('/servers/%s/os-security-groups' %
- (node.id)).object)
-
- def ex_create_security_group(self, name, description):
- """
- Create a new Security Group
-
- :param name: Name of the new Security Group
- :type name: ``str``
-
- :param description: Description of the new Security Group
- :type description: ``str``
-
- :rtype: :class:`OpenStackSecurityGroup`
- """
- return self._to_security_group(self.connection.request(
- '/os-security-groups', method='POST',
- data={'security_group': {'name': name, 'description': description}}
- ).object['security_group'])
-
- def ex_delete_security_group(self, security_group):
- """
- Delete a Security Group.
-
- :param security_group: Security Group should be deleted
- :type security_group: :class:`OpenStackSecurityGroup`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-security-groups/%s' %
- (security_group.id),
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def ex_create_security_group_rule(self, security_group, ip_protocol,
- from_port, to_port, cidr=None,
- source_security_group=None):
- """
- Create a new Rule in a Security Group
-
- :param security_group: Security Group in which to add the rule
- :type security_group: :class:`OpenStackSecurityGroup`
-
- :param ip_protocol: Protocol to which this rule applies
- Examples: tcp, udp, ...
- :type ip_protocol: ``str``
-
- :param from_port: First port of the port range
- :type from_port: ``int``
-
- :param to_port: Last port of the port range
- :type to_port: ``int``
-
- :param cidr: CIDR notation of the source IP range for this rule
- :type cidr: ``str``
-
- :param source_security_group: Existing Security Group to use as the
- source (instead of CIDR)
- :type source_security_group: L{OpenStackSecurityGroup
-
- :rtype: :class:`OpenStackSecurityGroupRule`
- """
- source_security_group_id = None
- if type(source_security_group) == OpenStackSecurityGroup:
- source_security_group_id = source_security_group.id
-
- return self._to_security_group_rule(self.connection.request(
- '/os-security-group-rules', method='POST',
- data={'security_group_rule': {
- 'ip_protocol': ip_protocol,
- 'from_port': from_port,
- 'to_port': to_port,
- 'cidr': cidr,
- 'group_id': source_security_group_id,
- 'parent_group_id': security_group.id}}
- ).object['security_group_rule'])
-
- def ex_delete_security_group_rule(self, rule):
- """
- Delete a Rule from a Security Group.
-
- :param rule: Rule should be deleted
- :type rule: :class:`OpenStackSecurityGroupRule`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-security-group-rules/%s' %
- (rule.id), method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _to_key_pairs(self, obj):
- key_pairs = obj['keypairs']
- key_pairs = [self._to_key_pair(key_pair['keypair']) for key_pair in
- key_pairs]
- return key_pairs
-
- def _to_key_pair(self, obj):
- key_pair = KeyPair(name=obj['name'],
- fingerprint=obj['fingerprint'],
- public_key=obj['public_key'],
- private_key=obj.get('private_key', None),
- driver=self)
- return key_pair
-
- def list_key_pairs(self):
- response = self.connection.request('/os-keypairs')
- key_pairs = self._to_key_pairs(response.object)
- return key_pairs
-
- def get_key_pair(self, name):
- self.connection.set_context({'key_pair_name': name})
-
- response = self.connection.request('/os-keypairs/%s' % (name))
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def create_key_pair(self, name):
- data = {'keypair': {'name': name}}
- response = self.connection.request('/os-keypairs', method='POST',
- data=data)
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def import_key_pair_from_string(self, name, key_material):
- data = {'keypair': {'name': name, 'public_key': key_material}}
- response = self.connection.request('/os-keypairs', method='POST',
- data=data)
- key_pair = self._to_key_pair(response.object['keypair'])
- return key_pair
-
- def delete_key_pair(self, key_pair):
- """
- Delete a KeyPair.
-
- :param keypair: KeyPair to delete
- :type keypair: :class:`OpenStackKeyPair`
-
- :rtype: ``bool``
- """
- response = self.connection.request('/os-keypairs/%s' % (key_pair.name),
- method='DELETE')
- return response.status == httplib.ACCEPTED
-
- def ex_list_keypairs(self):
- """
- Get a list of KeyPairs that are available.
-
- :rtype: ``list`` of :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'list_key_pairs method')
-
- return self.list_key_pairs()
-
- def ex_create_keypair(self, name):
- """
- Create a new KeyPair
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'create_key_pair method')
-
- return self.create_key_pair(name=name)
-
- def ex_import_keypair(self, name, keyfile):
- """
- Import a KeyPair from a file
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :param keyfile: Path to the public key file (in OpenSSH format)
- :type keyfile: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'import_key_pair_from_file method')
-
- return self.import_key_pair_from_file(name=name, key_file_path=keyfile)
-
- def ex_import_keypair_from_string(self, name, key_material):
- """
- Import a KeyPair from a string
-
- :param name: Name of the new KeyPair
- :type name: ``str``
-
- :param key_material: Public key (in OpenSSH format)
- :type key_material: ``str``
-
- :rtype: :class:`OpenStackKeyPair`
- """
- warnings.warn('This method has been deprecated in favor of '
- 'import_key_pair_from_string method')
-
- return self.import_key_pair_from_string(name=name,
- key_material=key_material)
-
- def ex_delete_keypair(self, keypair):
- """
- Delete a KeyPair.
-
- :param keypair: KeyPair to delete
- :type keypair: :class:`OpenStackKeyPair`
-
- :rtype: ``bool``
- """
- warnings.warn('This method has been deprecated in favor of '
- 'delete_key_pair method')
-
- return self.delete_key_pair(key_pair=keypair)
-
- def ex_get_size(self, size_id):
- """
- Get a NodeSize
-
- :param size_id: ID of the size which should be used
- :type size_id: ``str``
-
- :rtype: :class:`NodeSize`
- """
- return self._to_size(self.connection.request(
- '/flavors/%s' % (size_id,)) .object['flavor'])
-
- def get_image(self, image_id):
- """
- Get a NodeImage
-
- @inherits: :class:`NodeDriver.get_image`
-
- :param image_id: ID of the image which should be used
- :type image_id: ``str``
-
- :rtype: :class:`NodeImage`
- """
- return self._to_image(self.connection.request(
- '/images/%s' % (image_id,)).object['image'])
-
- def delete_image(self, image):
- """
- Delete a NodeImage
-
- @inherits: :class:`NodeDriver.delete_image`
-
- :param image: image witch should be used
- :type image: :class:`NodeImage`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/images/%s' % (image.id,),
- method='DELETE')
- return resp.status == httplib.NO_CONTENT
-
- def _node_action(self, node, action, **params):
- params = params or None
- return self.connection.request('/servers/%s/action' % (node.id,),
- method='POST', data={action: params})
-
- def _update_node(self, node, **node_updates):
- """
- Updates the editable attributes of a server, which currently include
- its name and IPv4/IPv6 access addresses.
- """
- return self._to_node(
- self.connection.request(
- '/servers/%s' % (node.id,), method='PUT',
- data={'server': node_updates}
- ).object['server']
- )
-
- def _to_node_from_obj(self, obj):
- return self._to_node(obj['server'])
-
- def _to_node(self, api_node):
- public_networks_labels = ['public', 'internet']
-
- public_ips, private_ips = [], []
-
- for label, values in api_node['addresses'].items():
- for value in values:
- ip = value['addr']
- is_public_ip = False
-
- try:
- is_public_ip = is_public_subnet(ip)
- except:
- # IPv6
-
- # Openstack Icehouse sets 'OS-EXT-IPS:type' to 'floating'
- # for public and 'fixed' for private
- explicit_ip_type = value.get('OS-EXT-IPS:type', None)
-
- if label in public_networks_labels:
- is_public_ip = True
- elif explicit_ip_type == 'floating':
- is_public_ip = True
- elif explicit_ip_type == 'fixed':
- is_public_ip = False
-
- if is_public_ip:
- public_ips.append(ip)
- else:
- private_ips.append(ip)
-
- # Sometimes 'image' attribute is not present if the node is in an error
- # state
- image = api_node.get('image', None)
- image_id = image.get('id', None) if image else None
- config_drive = api_node.get("config_drive", False)
- volumes_attached = api_node.get('os-extended-volumes:volumes_attached')
- created = parse_date(api_node["created"])
-
- return Node(
- id=api_node['id'],
- name=api_node['name'],
- state=self.NODE_STATE_MAP.get(api_node['status'],
- NodeState.UNKNOWN),
- public_ips=public_ips,
- private_ips=private_ips,
- created_at=created,
- driver=self,
- extra=dict(
- addresses=api_node['addresses'],
- hostId=api_node['hostId'],
- access_ip=api_node.get('accessIPv4'),
- access_ipv6=api_node.get('accessIPv6', None),
- # Docs says "tenantId", but actual is "tenant_id". *sigh*
- # Best handle both.
- tenantId=api_node.get('tenant_id') or api_node['tenantId'],
- userId=api_node.get('user_id', None),
- imageId=image_id,
- flavorId=api_node['flavor']['id'],
- uri=next(link['href'] for link in api_node['links'] if
- link['rel'] == 'self'),
- service_name=self.connection.get_service_name(),
- metadata=api_node['metadata'],
- password=api_node.get('adminPass', None),
- created=api_node['created'],
- updated=api_node['updated'],
- key_name=api_node.get('key_name', None),
- disk_config=api_node.get('OS-DCF:diskConfig', None),
- config_drive=config_drive,
- availability_zone=api_node.get('OS-EXT-AZ:availability_zone'),
- volumes_attached=volumes_attached,
- task_state=api_node.get("OS-EXT-STS:task_state", None),
- vm_state=api_node.get("OS-EXT-STS:vm_state", None),
- power_state=api_node.get("OS-EXT-STS:power_state", None),
- progress=api_node.get("progress", None),
- fault=api_node.get('fault')
- ),
- )
-
- def _to_volume(self, api_node):
- if 'volume' in api_node:
- api_node = api_node['volume']
-
- state = self.VOLUME_STATE_MAP.get(api_node['status'],
- StorageVolumeState.UNKNOWN)
-
- return StorageVolume(
- id=api_node['id'],
- name=api_node['displayName'],
- size=api_node['size'],
- state=state,
- driver=self,
- extra={
- 'description': api_node['displayDescription'],
- 'attachments': [att for att in api_node['attachments'] if att],
- # TODO: remove in 1.18.0
- 'state': api_node.get('status', None),
- 'snapshot_id': api_node.get('snapshotId', None),
- 'location': api_node.get('availabilityZone', None),
- 'volume_type': api_node.get('volumeType', None),
- 'metadata': api_node.get('metadata', None),
- 'created_at': api_node.get('createdAt', None)
- }
- )
-
- def _to_snapshot(self, data):
- if 'snapshot' in data:
- data = data['snapshot']
-
- volume_id = data.get('volume_id', data.get('volumeId', None))
- display_name = data.get('display_name', data.get('displayName', None))
- created_at = data.get('created_at', data.get('createdAt', None))
- description = data.get('display_description',
- data.get('displayDescription', None))
- status = data.get('status', None)
-
- extra = {'volume_id': volume_id,
- 'name': display_name,
- 'created': created_at,
- 'description': description,
- 'status': status}
-
- state = self.SNAPSHOT_STATE_MAP.get(
- status,
- VolumeSnapshotState.UNKNOWN
- )
-
- try:
- created_dt = parse_date(created_at)
- except ValueError:
- created_dt = None
-
- snapshot = VolumeSnapshot(id=data['id'], driver=self,
- size=data['size'], extra=extra,
- created=created_dt, state=state)
- return snapshot
-
- def _to_size(self, api_flavor, price=None, bandwidth=None):
- # if provider-specific subclasses can get better values for
- # price/bandwidth, then can pass them in when they super().
- if not price:
- price = self._get_size_price(str(api_flavor['id']))
-
- extra = api_flavor.get('OS-FLV-WITH-EXT-SPECS:extra_specs', {})
- return OpenStackNodeSize(
- id=api_flavor['id'],
- name=api_flavor['name'],
- ram=api_flavor['ram'],
- disk=api_flavor['disk'],
- vcpus=api_flavor['vcpus'],
- ephemeral_disk=api_flavor.get('OS-FLV-EXT-DATA:ephemeral', None),
- swap=api_flavor['swap'],
- extra=extra,
- bandwidth=bandwidth,
- price=price,
- driver=self,
- )
-
- def _get_size_price(self, size_id):
- try:
- return get_size_price(
- driver_type='compute',
- driver_name=self.api_name,
- size_id=size_id,
- )
- except KeyError:
- return(0.0)
-
- def _extract_image_id_from_url(self, location_header):
- path = urlparse.urlparse(location_header).path
- image_id = path.split('/')[-1]
- return image_id
-
- def ex_rescue(self, node, password=None):
- # Requires Rescue Mode extension
- """
- Rescue a node
-
- :param node: node
- :type node: :class:`Node`
-
- :param password: password
- :type password: ``str``
-
- :rtype: :class:`Node`
- """
- if password:
- resp = self._node_action(node, 'rescue', adminPass=password)
- else:
- resp = self._node_action(node, 'rescue')
- password = json.loads(resp.body)['adminPass']
- node.extra['password'] = password
- return node
-
- def ex_unrescue(self, node):
- """
- Unrescue a node
-
- :param node: node
- :type node: :class:`Node`
-
- :rtype: ``bool``
- """
- resp = self._node_action(node, 'unrescue')
- return resp.status == httplib.ACCEPTED
-
- def _to_floating_ip_pools(self, obj):
- pool_elements = obj['floating_ip_pools']
- return [self._to_floating_ip_pool(pool) for pool in pool_elements]
-
- def _to_floating_ip_pool(self, obj):
- return OpenStack_1_1_FloatingIpPool(obj['name'], self.connection)
-
- def ex_list_floating_ip_pools(self):
- """
- List available floating IP pools
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpPool`
- """
- return self._to_floating_ip_pools(
- self.connection.request('/os-floating-ip-pools').object)
-
- def _to_floating_ips(self, obj):
- ip_elements = obj['floating_ips']
- return [self._to_floating_ip(ip) for ip in ip_elements]
-
- def _to_floating_ip(self, obj):
- return OpenStack_1_1_FloatingIpAddress(id=obj['id'],
- ip_address=obj['ip'],
- pool=None,
- node_id=obj['instance_id'],
- driver=self)
-
- def ex_list_floating_ips(self):
- """
- List floating IPs
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress`
- """
- return self._to_floating_ips(
- self.connection.request('/os-floating-ips').object)
-
- def ex_get_floating_ip(self, ip):
- """
- Get specified floating IP
-
- :param ip: floating IP to get
- :type ip: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- floating_ips = self.ex_list_floating_ips()
- ip_obj, = [x for x in floating_ips if x.ip_address == ip]
- return ip_obj
-
- def ex_create_floating_ip(self, ip_pool=None):
- """
- Create new floating IP. The ip_pool attribute is optional only if your
- infrastructure has only one IP pool available.
-
- :param ip_pool: name of the floating IP pool
- :type ip_pool: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- data = {'pool': ip_pool} if ip_pool is not None else {}
- resp = self.connection.request('/os-floating-ips',
- method='POST',
- data=data)
-
- data = resp.object['floating_ip']
- id = data['id']
- ip_address = data['ip']
- return OpenStack_1_1_FloatingIpAddress(id=id,
- ip_address=ip_address,
- pool=None,
- node_id=None,
- driver=self)
-
- def ex_delete_floating_ip(self, ip):
- """
- Delete specified floating IP
-
- :param ip: floating IP to remove
- :type ip: :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-floating-ips/%s' % ip.id,
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def ex_attach_floating_ip_to_node(self, node, ip):
- """
- Attach the floating IP to the node
-
- :param node: node
- :type node: :class:`Node`
-
- :param ip: floating IP to attach
- :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- address = ip.ip_address if hasattr(ip, 'ip_address') else ip
- data = {
- 'addFloatingIp': {'address': address}
- }
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_detach_floating_ip_from_node(self, node, ip):
- """
- Detach the floating IP from the node
-
- :param node: node
- :type node: :class:`Node`
-
- :param ip: floating IP to remove
- :type ip: ``str`` or :class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- address = ip.ip_address if hasattr(ip, 'ip_address') else ip
- data = {
- 'removeFloatingIp': {'address': address}
- }
- resp = self.connection.request('/servers/%s/action' % node.id,
- method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_get_metadata_for_node(self, node):
- """
- Return the metadata associated with the node.
-
- :param node: Node instance
- :type node: :class:`Node`
-
- :return: A dictionary or other mapping of strings to strings,
- associating tag names with tag values.
- :type tags: ``dict``
- """
- return node.extra['metadata']
-
- def ex_pause_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'pause': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_unpause_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'unpause': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_suspend_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'suspend': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
- def ex_resume_node(self, node):
- uri = '/servers/%s/action' % (node.id)
- data = {'resume': None}
- resp = self.connection.request(uri, method='POST', data=data)
- return resp.status == httplib.ACCEPTED
-
-
-class OpenStack_1_1_FloatingIpPool(object):
- """
- Floating IP Pool info.
- """
-
- def __init__(self, name, connection):
- self.name = name
- self.connection = connection
-
- def list_floating_ips(self):
- """
- List floating IPs in the pool
-
- :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress`
- """
- return self._to_floating_ips(
- self.connection.request('/os-floating-ips').object)
-
- def _to_floating_ips(self, obj):
- ip_elements = obj['floating_ips']
- return [self._to_floating_ip(ip) for ip in ip_elements]
-
- def _to_floating_ip(self, obj):
- return OpenStack_1_1_FloatingIpAddress(id=obj['id'],
- ip_address=obj['ip'],
- pool=self,
- node_id=obj['instance_id'],
- driver=self.connection.driver)
-
- def get_floating_ip(self, ip):
- """
- Get specified floating IP from the pool
-
- :param ip: floating IP to get
- :type ip: ``str``
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- ip_obj, = [x for x in self.list_floating_ips() if x.ip_address == ip]
- return ip_obj
-
- def create_floating_ip(self):
- """
- Create new floating IP in the pool
-
- :rtype: :class:`OpenStack_1_1_FloatingIpAddress`
- """
- resp = self.connection.request('/os-floating-ips',
- method='POST',
- data={'pool': self.name})
- data = resp.object['floating_ip']
- id = data['id']
- ip_address = data['ip']
- return OpenStack_1_1_FloatingIpAddress(id=id,
- ip_address=ip_address,
- pool=self,
- node_id=None,
- driver=self.connection.driver)
-
- def delete_floating_ip(self, ip):
- """
- Delete specified floating IP from the pool
-
- :param ip: floating IP to remove
- :type ip::class:`OpenStack_1_1_FloatingIpAddress`
-
- :rtype: ``bool``
- """
- resp = self.connection.request('/os-floating-ips/%s' % ip.id,
- method='DELETE')
- return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
-
- def __repr__(self):
- return ('<OpenStack_1_1_FloatingIpPool: name=%s>' % self.name)
-
-
-cl
<TRUNCATED>