http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py deleted file mode 100644 index 2560145..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/packet.py +++ /dev/null @@ -1,258 +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. -""" -Packet Driver -""" - -from libcloud.utils.py3 import httplib - -from libcloud.common.base import ConnectionKey, JsonResponse -from libcloud.compute.types import Provider, NodeState, InvalidCredsError -from libcloud.compute.base import NodeDriver, Node -from libcloud.compute.base import NodeImage, NodeSize, NodeLocation -from libcloud.compute.base import KeyPair - -PACKET_ENDPOINT = "api.packet.net" - - -class PacketResponse(JsonResponse): - valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, - httplib.NO_CONTENT] - - def parse_error(self): - if self.status == httplib.UNAUTHORIZED: - body = self.parse_body() - raise InvalidCredsError(body['message']) - else: - body = self.parse_body() - if 'message' in body: - error = '%s (code: %s)' % (body['message'], self.status) - else: - error = body - return error - - def success(self): - return self.status in self.valid_response_codes - - -class PacketConnection(ConnectionKey): - """ - Connection class for the Packet driver. - """ - - host = PACKET_ENDPOINT - responseCls = PacketResponse - - def add_default_headers(self, headers): - """ - Add headers that are necessary for every request - """ - headers['Content-Type'] = 'application/json' - headers['X-Auth-Token'] = self.key - headers['X-Consumer-Token'] = \ - 'kcrhMn7hwG8Ceo2hAhGFa2qpxLBvVHxEjS9ue8iqmsNkeeB2iQgMq4dNc1893pYu' - return headers - - -class PacketNodeDriver(NodeDriver): - """ - Packet NodeDriver - """ - - connectionCls = PacketConnection - type = Provider.PACKET - name = 'Packet' - website = 'http://www.packet.net/' - - NODE_STATE_MAP = {'queued': NodeState.PENDING, - 'provisioning': NodeState.PENDING, - 'rebuilding': NodeState.PENDING, - 'powering_on': NodeState.REBOOTING, - 'powering_off': NodeState.REBOOTING, - 'rebooting': NodeState.REBOOTING, - 'inactive': NodeState.STOPPED, - 'deleted': NodeState.TERMINATED, - 'deprovisioning': NodeState.TERMINATED, - 'failed': NodeState.ERROR, - 'active': NodeState.RUNNING} - - def list_nodes(self, ex_project_id): - data = self.connection.request('/projects/%s/devices' % - (ex_project_id), - params={'include': 'plan'} - ).object['devices'] - return list(map(self._to_node, data)) - - def list_locations(self): - data = self.connection.request('/facilities')\ - .object['facilities'] - return list(map(self._to_location, data)) - - def list_images(self): - data = self.connection.request('/operating-systems')\ - .object['operating_systems'] - return list(map(self._to_image, data)) - - def list_sizes(self): - data = self.connection.request('/plans').object['plans'] - return list(map(self._to_size, data)) - - def create_node(self, name, size, image, location, ex_project_id): - """ - Create a node. - - :return: The newly created node. - :rtype: :class:`Node` - """ - - params = {'hostname': name, 'plan': size.id, - 'operating_system': image.id, 'facility': location.id, - 'include': 'plan', 'billing_cycle': 'hourly'} - - data = self.connection.request('/projects/%s/devices' % - (ex_project_id), - params=params, method='POST') - - status = data.object.get('status', 'OK') - if status == 'ERROR': - message = data.object.get('message', None) - error_message = data.object.get('error_message', message) - raise ValueError('Failed to create node: %s' % (error_message)) - return self._to_node(data=data.object) - - def reboot_node(self, node): - params = {'type': 'reboot'} - res = self.connection.request('/devices/%s/actions' % (node.id), - params=params, method='POST') - return res.status == httplib.OK - - def destroy_node(self, node): - res = self.connection.request('/devices/%s' % (node.id), - method='DELETE') - return res.status == httplib.OK - - def list_key_pairs(self): - """ - List all the available SSH keys. - - :return: Available SSH keys. - :rtype: ``list`` of :class:`.KeyPair` objects - """ - data = self.connection.request('/ssh-keys').object['ssh_keys'] - return list(map(self._to_key_pairs, data)) - - def create_key_pair(self, name, public_key): - """ - Create a new SSH key. - - :param name: Key name (required) - :type name: ``str`` - - :param public_key: Valid public key string (required) - :type public_key: ``str`` - """ - params = {'label': name, 'key': public_key} - data = self.connection.request('/ssh-keys', method='POST', - params=params).object - return self._to_key_pairs(data) - - def delete_key_pair(self, key): - """ - Delete an existing SSH key. - - :param key: SSH key (required) - :type key: :class:`KeyPair` - """ - key_id = key.name - res = self.connection.request('/ssh-keys/%s' % (key_id), - method='DELETE') - return res.status == httplib.NO_CONTENT - - def _to_node(self, data): - extra_keys = ['created_at', 'updated_at', - 'userdata', 'billing_cycle', 'locked'] - if 'state' in data: - state = self.NODE_STATE_MAP.get(data['state'], NodeState.UNKNOWN) - else: - state = NodeState.UNKNOWN - - if 'ip_addresses' in data and data['ip_addresses'] is not None: - ips = self._parse_ips(data['ip_addresses']) - - if 'operating_system' in data and data['operating_system'] is not None: - image = self._to_image(data['operating_system']) - - if 'plan' in data and data['plan'] is not None: - size = self._to_size(data['plan']) - - extra = {} - for key in extra_keys: - if key in data: - extra[key] = data[key] - - node = Node(id=data['id'], name=data['hostname'], state=state, - image=image, size=size, - public_ips=ips['public'], private_ips=ips['private'], - extra=extra, driver=self) - return node - - def _to_image(self, data): - extra = {'distro': data['distro'], 'version': data['version']} - return NodeImage(id=data['slug'], name=data['name'], extra=extra, - driver=self) - - def _to_location(self, data): - return NodeLocation(id=data['code'], name=data['name'], country=None, - driver=self) - - def _to_size(self, data): - extra = {'description': data['description'], 'line': data['line']} - - ram = data['specs']['memory']['total'].lower() - if 'mb' in ram: - ram = int(ram.replace('mb', '')) - elif 'gb' in ram: - ram = int(ram.replace('gb', '')) * 1024 - - disk = 0 - for disks in data['specs']['drives']: - disk += disks['count'] * int(disks['size'].replace('GB', '')) - - price = data['pricing']['hourly'] - - return NodeSize(id=data['slug'], name=data['name'], ram=ram, disk=disk, - bandwidth=0, price=price, extra=extra, driver=self) - - def _to_key_pairs(self, data): - extra = {'label': data['label'], - 'created_at': data['created_at'], - 'updated_at': data['updated_at']} - return KeyPair(name=data['id'], - fingerprint=data['fingerprint'], - public_key=data['key'], - private_key=None, - driver=self, - extra=extra) - - def _parse_ips(self, data): - public_ips = [] - private_ips = [] - for address in data: - if 'address' in address and address['address'] is not None: - if 'public' in address and address['public'] is True: - public_ips.append(address['address']) - else: - private_ips.append(address['address']) - return {'public': public_ips, 'private': private_ips}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py deleted file mode 100644 index 697b082..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/profitbricks.py +++ /dev/null @@ -1,1496 +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. -"""ProfitBricks Compute driver -""" -import base64 - -import copy -import time - -try: - from lxml import etree as ET -except ImportError: - from xml.etree import ElementTree as ET - -from libcloud.utils.networking import is_private_subnet -from libcloud.utils.py3 import b -from libcloud.compute.providers import Provider -from libcloud.common.base import ConnectionUserAndKey, XmlResponse -from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize -from libcloud.compute.base import NodeImage, StorageVolume -from libcloud.compute.base import UuidMixin -from libcloud.compute.types import NodeState -from libcloud.common.types import LibcloudError, MalformedResponseError - -__all__ = [ - 'API_VERSION', - 'API_HOST', - 'ProfitBricksNodeDriver', - 'Datacenter', - 'ProfitBricksNetworkInterface', - 'ProfitBricksAvailabilityZone' -] - -API_HOST = 'api.profitbricks.com' -API_VERSION = '/1.3/' - - -class ProfitBricksResponse(XmlResponse): - """ - ProfitBricks response parsing. - """ - def parse_error(self): - try: - body = ET.XML(self.body) - except: - raise MalformedResponseError('Failed to parse XML', - body=self.body, - driver=ProfitBricksNodeDriver) - - for e in body.findall('.//detail'): - if ET.iselement(e[0].find('httpCode')): - http_code = e[0].find('httpCode').text - else: - http_code = None - if ET.iselement(e[0].find('faultCode')): - fault_code = e[0].find('faultCode').text - else: - fault_code = None - if ET.iselement(e[0].find('message')): - message = e[0].find('message').text - else: - message = None - - return LibcloudError('HTTP Code: %s, Fault Code: %s, Message: %s' % - (http_code, fault_code, message), driver=self) - - -class ProfitBricksConnection(ConnectionUserAndKey): - """ - Represents a single connection to the ProfitBricks endpoint. - """ - host = API_HOST - api_prefix = API_VERSION - responseCls = ProfitBricksResponse - - def add_default_headers(self, headers): - headers['Content-Type'] = 'text/xml' - headers['Authorization'] = 'Basic %s' % (base64.b64encode( - b('%s:%s' % (self.user_id, self.key))).decode('utf-8')) - - return headers - - def encode_data(self, data): - soap_env = ET.Element('soapenv:Envelope', { - 'xmlns:soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', - 'xmlns:ws': 'http://ws.api.profitbricks.com/' - }) - ET.SubElement(soap_env, 'soapenv:Header') - soap_body = ET.SubElement(soap_env, 'soapenv:Body') - soap_req_body = ET.SubElement(soap_body, 'ws:%s' % (data['action'])) - - if 'request' in data.keys(): - soap_req_body = ET.SubElement(soap_req_body, 'request') - for key, value in data.items(): - if key not in ['action', 'request']: - child = ET.SubElement(soap_req_body, key) - child.text = value - else: - for key, value in data.items(): - if key != 'action': - child = ET.SubElement(soap_req_body, key) - child.text = value - - soap_post = ET.tostring(soap_env) - - return soap_post - - def request(self, action, params=None, data=None, headers=None, - method='POST', raw=False): - action = self.api_prefix + action - - return super(ProfitBricksConnection, self).request(action=action, - params=params, - data=data, - headers=headers, - method=method, - raw=raw) - - -class Datacenter(UuidMixin): - """ - Class which stores information about ProfitBricks datacenter - instances. - - :param id: The datacenter ID. - :type id: ``str`` - - :param name: The datacenter name. - :type name: ``str`` - - :param version: Datacenter version. - :type version: ``str`` - - - Note: This class is ProfitBricks specific. - """ - def __init__(self, id, name, version, driver, extra=None): - self.id = str(id) - self.name = name - self.version = version - self.driver = driver - self.extra = extra or {} - UuidMixin.__init__(self) - - def __repr__(self): - return (( - '<Datacenter: id=%s, name=%s, version=%s, driver=%s> ...>') - % (self.id, self.name, self.version, - self.driver.name)) - - -class ProfitBricksNetworkInterface(object): - """ - Class which stores information about ProfitBricks network - interfaces. - - :param id: The network interface ID. - :type id: ``str`` - - :param name: The network interface name. - :type name: ``str`` - - :param state: The network interface name. - :type state: ``int`` - - Note: This class is ProfitBricks specific. - """ - def __init__(self, id, name, state, extra=None): - self.id = id - self.name = name - self.state = state - self.extra = extra or {} - - def __repr__(self): - return (('<ProfitBricksNetworkInterface: id=%s, name=%s>') - % (self.id, self.name)) - - -class ProfitBricksAvailabilityZone(object): - """ - Extension class which stores information about a ProfitBricks - availability zone. - - Note: This class is ProfitBricks specific. - """ - - def __init__(self, name): - self.name = name - - def __repr__(self): - return (('<ProfitBricksAvailabilityZone: name=%s>') - % (self.name)) - - -class ProfitBricksNodeDriver(NodeDriver): - """ - Base ProfitBricks node driver. - """ - connectionCls = ProfitBricksConnection - name = 'ProfitBricks' - website = 'http://www.profitbricks.com' - type = Provider.PROFIT_BRICKS - - PROVISIONING_STATE = { - 'INACTIVE': NodeState.PENDING, - 'INPROCESS': NodeState.PENDING, - 'AVAILABLE': NodeState.RUNNING, - 'DELETED': NodeState.TERMINATED, - } - - NODE_STATE_MAP = { - 'NOSTATE': NodeState.UNKNOWN, - 'RUNNING': NodeState.RUNNING, - 'BLOCKED': NodeState.STOPPED, - 'PAUSE': NodeState.STOPPED, - 'SHUTDOWN': NodeState.PENDING, - 'SHUTOFF': NodeState.STOPPED, - 'CRASHED': NodeState.STOPPED, - } - - REGIONS = { - '1': {'region': 'us/las', 'country': 'USA'}, - '2': {'region': 'de/fra', 'country': 'DEU'}, - '3': {'region': 'de/fkb', 'country': 'DEU'}, - } - - AVAILABILITY_ZONE = { - '1': {'name': 'AUTO'}, - '2': {'name': 'ZONE_1'}, - '3': {'name': 'ZONE_2'}, - } - - """ - ProfitBricks is unique in that they allow the user to define all aspects - of the instance size, i.e. disk size, core size, and memory size. - - These are instance types that match up with what other providers support. - - You can configure disk size, core size, and memory size using the ``ex_`` - parameters on the create_node method. - """ - - PROFIT_BRICKS_GENERIC_SIZES = { - '1': { - 'id': '1', - 'name': 'Micro', - 'ram': 1024, - 'disk': 50, - 'cores': 1 - }, - '2': { - 'id': '2', - 'name': 'Small Instance', - 'ram': 2048, - 'disk': 50, - 'cores': 1 - }, - '3': { - 'id': '3', - 'name': 'Medium Instance', - 'ram': 4096, - 'disk': 50, - 'cores': 2 - }, - '4': { - 'id': '4', - 'name': 'Large Instance', - 'ram': 7168, - 'disk': 50, - 'cores': 4 - }, - '5': { - 'id': '5', - 'name': 'ExtraLarge Instance', - 'ram': 14336, - 'disk': 50, - 'cores': 8 - }, - '6': { - 'id': '6', - 'name': 'Memory Intensive Instance Medium', - 'ram': 28672, - 'disk': 50, - 'cores': 4 - }, - '7': { - 'id': '7', - 'name': 'Memory Intensive Instance Large', - 'ram': 57344, - 'disk': 50, - 'cores': 8 - } - } - - """ - Core Functions - """ - - def list_sizes(self): - """ - Lists all sizes - - :rtype: ``list`` of :class:`NodeSize` - """ - sizes = [] - - for key, values in self.PROFIT_BRICKS_GENERIC_SIZES.items(): - node_size = self._to_node_size(values) - sizes.append(node_size) - - return sizes - - def list_images(self): - """ - List all images. - - :rtype: ``list`` of :class:`NodeImage` - """ - - action = 'getAllImages' - body = {'action': action} - - return self._to_images(self.connection.request(action=action, - data=body, method='POST').object) - - def list_locations(self): - """ - List all locations. - """ - locations = [] - - for key, values in self.REGIONS.items(): - location = self._to_location(values) - locations.append(location) - - return locations - - def list_nodes(self): - """ - List all nodes. - - :rtype: ``list`` of :class:`Node` - """ - action = 'getAllServers' - body = {'action': action} - - return self._to_nodes(self.connection.request(action=action, - data=body, method='POST').object) - - def reboot_node(self, node): - """ - Reboots the node. - - :rtype: ``bool`` - """ - action = 'resetServer' - body = {'action': action, - 'serverId': node.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def create_node(self, name, image, size=None, location=None, - volume=None, ex_datacenter=None, ex_internet_access=True, - ex_availability_zone=None, ex_ram=None, ex_cores=None, - ex_disk=None, **kwargs): - """ - Creates a node. - - image is optional as long as you pass ram, cores, and disk - to the method. ProfitBricks allows you to adjust compute - resources at a much more granular level. - - :param volume: If the volume already exists then pass this in. - :type volume: :class:`StorageVolume` - - :param location: The location of the new data center - if one is not supplied. - :type location: : :class:`NodeLocation` - - :param ex_datacenter: If you've already created the DC then pass - it in. - :type ex_datacenter: :class:`Datacenter` - - :param ex_internet_access: Configure public Internet access. - :type ex_internet_access: : ``bool`` - - :param ex_availability_zone: The availability zone. - :type ex_availability_zone: class: `ProfitBricksAvailabilityZone` - - :param ex_ram: The amount of ram required. - :type ex_ram: : ``int`` - - :param ex_cores: The number of cores required. - :type ex_cores: : ``int`` - - :param ex_disk: The amount of disk required. - :type ex_disk: : ``int`` - - :return: Instance of class ``Node`` - :rtype: :class:`Node` - """ - if not ex_datacenter: - ''' - We generate a name from the server name passed into the function. - ''' - - 'Creating a Datacenter for the node since one was not provided.' - new_datacenter = self._create_new_datacenter_for_node( - name=name, - location=location - ) - datacenter_id = new_datacenter.id - - 'Waiting for the Datacenter create operation to finish.' - self._wait_for_datacenter_state(datacenter=new_datacenter) - else: - datacenter_id = ex_datacenter.id - new_datacenter = None - - if not size: - if not ex_ram: - raise ValueError('You need to either pass a ' - 'NodeSize or specify ex_ram as ' - 'an extra parameter.') - if not ex_cores: - raise ValueError('You need to either pass a ' - 'NodeSize or specify ex_cores as ' - 'an extra parameter.') - - if not volume: - if not size: - if not ex_disk: - raise ValueError('You need to either pass a ' - 'StorageVolume, a NodeSize, or specify ' - 'ex_disk as an extra parameter.') - - ''' - You can override the suggested sizes by passing in unique - values for ram, cores, and disk allowing you to size it - for your specific use. - ''' - - if not ex_disk: - ex_disk = size.disk - - if not ex_ram: - ex_ram = size.ram - - if not ex_cores: - ex_cores = size.extra['cores'] - - ''' - A pasword is automatically generated if it is - not provided. This is then sent via email to - the admin contact on record. - ''' - - if 'auth' in kwargs: - auth = self._get_and_check_auth(kwargs["auth"]) - password = auth.password - else: - password = None - - ''' - Create a StorageVolume that can be attached to the - server when it is created. - ''' - if not volume: - volume = self._create_node_volume(ex_disk=ex_disk, - image=image, - password=password, - name=name, - ex_datacenter=ex_datacenter, - new_datacenter=new_datacenter) - - storage_id = volume.id - - 'Waiting on the storage volume to be created before provisioning ' - 'the instance.' - self._wait_for_storage_volume_state(volume) - else: - if ex_datacenter: - datacenter_id = ex_datacenter.id - else: - datacenter_id = volume.extra['datacenter_id'] - - storage_id = volume.id - - action = 'createServer' - body = {'action': action, - 'request': 'true', - 'serverName': name, - 'cores': str(ex_cores), - 'ram': str(ex_ram), - 'bootFromStorageId': storage_id, - 'internetAccess': str(ex_internet_access).lower(), - 'dataCenterId': datacenter_id - } - - if ex_availability_zone: - body['availabilityZone'] = ex_availability_zone.name - - data = self.connection.request(action=action, - data=body, - method='POST').object - nodes = self._to_nodes(data) - return nodes[0] - - def destroy_node(self, node, ex_remove_attached_disks=False): - """ - Destroys a node. - - :param node: The node you wish to destroy. - :type volume: :class:`Node` - - :param ex_remove_attached_disks: True to destroy all attached volumes. - :type ex_remove_attached_disks: : ``bool`` - - :rtype: : ``bool`` - """ - action = 'deleteServer' - body = {'action': action, - 'serverId': node.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - """ - Volume Functions - """ - - def list_volumes(self): - """ - Lists all voumes. - """ - action = 'getAllStorages' - body = {'action': action} - - return self._to_volumes(self.connection.request(action=action, - data=body, - method='POST').object) - - def attach_volume(self, node, volume, device=None, ex_bus_type=None): - """ - Attaches a volume. - - :param volume: The volume you're attaching. - :type volume: :class:`StorageVolume` - - :param node: The node to which you're attaching the volume. - :type node: :class:`Node` - - :param device: The device number order. - :type device: : ``int`` - - :param ex_bus_type: Bus type. Either IDE or VIRTIO (default). - :type ex_bus_type: ``str`` - - :return: Instance of class ``StorageVolume`` - :rtype: :class:`StorageVolume` - """ - action = 'connectStorageToServer' - body = {'action': action, - 'request': 'true', - 'storageId': volume.id, - 'serverId': node.id, - 'busType': ex_bus_type, - 'deviceNumber': str(device) - } - - self.connection.request(action=action, - data=body, method='POST').object - return volume - - def create_volume(self, size, name=None, - ex_datacenter=None, ex_image=None, ex_password=None): - """ - Creates a volume. - - :param ex_datacenter: The datacenter you're placing - the storage in. (req) - :type ex_datacenter: :class:`Datacenter` - - :param ex_image: The OS image for the volume. - :type ex_image: :class:`NodeImage` - - :param ex_password: Optional password for root. - :type ex_password: : ``str`` - - :return: Instance of class ``StorageVolume`` - :rtype: :class:`StorageVolume` - """ - action = 'createStorage' - body = {'action': action, - 'request': 'true', - 'size': str(size), - 'storageName': name, - 'mountImageId': ex_image.id - } - - if ex_datacenter: - body['dataCenterId'] = ex_datacenter.id - - if ex_password: - body['profitBricksImagePassword'] = ex_password - - data = self.connection.request(action=action, - data=body, - method='POST').object - volumes = self._to_volumes(data) - return volumes[0] - - def detach_volume(self, volume): - """ - Detaches a volume. - - :param volume: The volume you're detaching. - :type volume: :class:`StorageVolume` - - :rtype: :``bool`` - """ - node_id = volume.extra['server_id'] - - action = 'disconnectStorageFromServer' - body = {'action': action, - 'storageId': volume.id, - 'serverId': node_id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def destroy_volume(self, volume): - """ - Destroys a volume. - - :param volume: The volume you're attaching. - :type volume: :class:`StorageVolume` - - :rtype: : ``bool`` - """ - action = 'deleteStorage' - body = {'action': action, - 'storageId': volume.id} - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_update_volume(self, volume, storage_name=None, size=None): - """ - Updates a volume. - - :param volume: The volume you're attaching.. - :type volume: :class:`StorageVolume` - - :param storage_name: The name of the volume. - :type storage_name: : ``str`` - - :param size: The desired size. - :type size: ``int`` - - :rtype: : ``bool`` - """ - action = 'updateStorage' - body = {'action': action, - 'request': 'true', - 'storageId': volume.id - } - - if storage_name: - body['storageName'] = storage_name - if size: - body['size'] = str(size) - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_describe_volume(self, volume_id): - """ - Describes a volume. - - :param volume_id: The ID of the volume you're describing. - :type volume_id: :class:`StorageVolume` - - :return: Instance of class ``StorageVolume`` - :rtype: :class:`StorageVolume` - """ - action = 'getStorage' - body = {'action': action, - 'storageId': volume_id - } - - data = self.connection.request(action=action, - data=body, - method='POST').object - volumes = self._to_volumes(data) - return volumes[0] - - """ - Extension Functions - """ - - ''' Server Extension Functions - ''' - def ex_stop_node(self, node): - """ - Stops a node. - - This also deallocates the public IP space. - - :param node: The node you wish to halt. - :type node: :class:`Node` - - :rtype: : ``bool`` - """ - action = 'stopServer' - body = {'action': action, - 'serverId': node.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_start_node(self, node): - """ - Starts a volume. - - :param node: The node you wish to start. - :type node: :class:`Node` - - :rtype: : ``bool`` - """ - action = 'startServer' - body = {'action': action, - 'serverId': node.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_list_availability_zones(self): - """ - Returns a list of availability zones. - """ - - availability_zones = [] - - for key, values in self.AVAILABILITY_ZONE.items(): - name = copy.deepcopy(values)["name"] - - availability_zone = ProfitBricksAvailabilityZone( - name=name - ) - availability_zones.append(availability_zone) - - return availability_zones - - def ex_describe_node(self, node): - """ - Describes a node. - - :param node: The node you wish to describe. - :type node: :class:`Node` - - :return: Instance of class ``Node`` - :rtype: :class:`Node` - """ - action = 'getServer' - body = {'action': action, - 'serverId': node.id - } - - data = self.connection.request(action=action, - data=body, - method='POST').object - nodes = self._to_nodes(data) - return nodes[0] - - def ex_update_node(self, node, name=None, cores=None, - ram=None, availability_zone=None): - """ - Updates a node. - - :param cores: The number of CPUs the node should have. - :type device: : ``int`` - - :param ram: The amount of ram the machine should have. - :type ram: : ``int`` - - :param ex_availability_zone: Update the availability zone. - :type ex_availability_zone: :class:`ProfitBricksAvailabilityZone` - - :rtype: : ``bool`` - """ - action = 'updateServer' - - body = {'action': action, - 'request': 'true', - 'serverId': node.id - } - - if name: - body['serverName'] = name - - if cores: - body['cores'] = str(cores) - - if ram: - body['ram'] = str(ram) - - if availability_zone: - body['availabilityZone'] = availability_zone.name - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - ''' - Datacenter Extension Functions - ''' - - def ex_create_datacenter(self, name, location): - """ - Creates a datacenter. - - ProfitBricks has a concept of datacenters. - These represent buckets into which you - can place various compute resources. - - :param name: The DC name. - :type name: : ``str`` - - :param location: The DC region. - :type location: : ``str`` - - :return: Instance of class ``Datacenter`` - :rtype: :class:`Datacenter` - """ - action = 'createDataCenter' - - body = {'action': action, - 'request': 'true', - 'dataCenterName': name, - 'location': location.lower() - } - data = self.connection.request(action=action, - data=body, - method='POST').object - datacenters = self._to_datacenters(data) - return datacenters[0] - - def ex_destroy_datacenter(self, datacenter): - """ - Destroys a datacenter. - - :param datacenter: The DC you're destroying. - :type datacenter: :class:`Datacenter` - - :rtype: : ``bool`` - """ - action = 'deleteDataCenter' - body = {'action': action, - 'dataCenterId': datacenter.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_describe_datacenter(self, datacenter_id): - """ - Describes a datacenter. - - :param datacenter_id: The DC you are describing. - :type datacenter_id: ``str`` - - :return: Instance of class ``Datacenter`` - :rtype: :class:`Datacenter` - """ - - action = 'getDataCenter' - body = {'action': action, - 'dataCenterId': datacenter_id - } - - data = self.connection.request(action=action, - data=body, - method='POST').object - datacenters = self._to_datacenters(data) - return datacenters[0] - - def ex_list_datacenters(self): - """ - Lists all datacenters. - - :return: ``list`` of class ``Datacenter`` - :rtype: :class:`Datacenter` - """ - action = 'getAllDataCenters' - body = {'action': action} - - return self._to_datacenters(self.connection.request( - action=action, - data=body, - method='POST').object) - - def ex_rename_datacenter(self, datacenter, name): - """ - Update a datacenter. - - :param datacenter: The DC you are renaming. - :type datacenter: :class:`Datacenter` - - :param name: The DC name. - :type name: : ``str`` - - :rtype: : ``bool`` - """ - action = 'updateDataCenter' - body = {'action': action, - 'request': 'true', - 'dataCenterId': datacenter.id, - 'dataCenterName': name - } - - self.connection.request(action=action, - data=body, - method='POST').object - - return True - - def ex_clear_datacenter(self, datacenter): - """ - Clear a datacenter. - - This removes all objects in a DC. - - :param datacenter: The DC you're clearing. - :type datacenter: :class:`Datacenter` - - :rtype: : ``bool`` - """ - action = 'clearDataCenter' - body = {'action': action, - 'dataCenterId': datacenter.id - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - ''' - Network Interface Extension Functions - ''' - - def ex_list_network_interfaces(self): - """ - Lists all network interfaces. - - :return: ``list`` of class ``ProfitBricksNetworkInterface`` - :rtype: :class:`ProfitBricksNetworkInterface` - """ - action = 'getAllNic' - body = {'action': action} - - return self._to_interfaces( - self.connection.request(action=action, - data=body, - method='POST').object) - - def ex_describe_network_interface(self, network_interface): - """ - Describes a network interface. - - :param network_interface: The NIC you wish to describe. - :type network_interface: :class:`ProfitBricksNetworkInterface` - - :return: Instance of class ``ProfitBricksNetworkInterface`` - :rtype: :class:`ProfitBricksNetworkInterface` - """ - action = 'getNic' - body = {'action': action, - 'nicId': network_interface.id - } - - return self._to_interface( - self.connection.request( - action=action, - data=body, - method='POST').object.findall('.//return')[0]) - - def ex_create_network_interface(self, node, - lan_id=None, ip=None, nic_name=None, - dhcp_active=True): - """ - Creates a network interface. - - :param lan_id: The ID for the LAN. - :type lan_id: : ``int`` - - :param ip: The IP address for the NIC. - :type ip: ``str`` - - :param nic_name: The name of the NIC, e.g. PUBLIC. - :type nic_name: ``str`` - - :param dhcp_active: Set to false to disable. - :type dhcp_active: ``bool`` - - :return: Instance of class ``ProfitBricksNetworkInterface`` - :rtype: :class:`ProfitBricksNetworkInterface` - """ - action = 'createNic' - body = {'action': action, - 'request': 'true', - 'serverId': node.id, - 'dhcpActive': str(dhcp_active) - } - - if lan_id: - body['lanId'] = str(lan_id) - else: - body['lanId'] = str(1) - - if ip: - body['ip'] = ip - - if nic_name: - body['nicName'] = nic_name - - data = self.connection.request(action=action, - data=body, - method='POST').object - interfaces = self._to_interfaces(data) - return interfaces[0] - - def ex_update_network_interface(self, network_interface, name=None, - lan_id=None, ip=None, - dhcp_active=None): - """ - Updates a network interface. - - :param lan_id: The ID for the LAN. - :type lan_id: : ``int`` - - :param ip: The IP address for the NIC. - :type ip: ``str`` - - :param name: The name of the NIC, e.g. PUBLIC. - :type name: ``str`` - - :param dhcp_active: Set to false to disable. - :type dhcp_active: ``bool`` - - :rtype: : ``bool`` - """ - action = 'updateNic' - body = {'action': action, - 'request': 'true', - 'nicId': network_interface.id - } - - if name: - body['nicName'] = name - - if lan_id: - body['lanId'] = str(lan_id) - - if ip: - body['ip'] = ip - - if dhcp_active is not None: - body['dhcpActive'] = str(dhcp_active).lower() - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_destroy_network_interface(self, network_interface): - """ - Destroy a network interface. - - :param network_interface: The NIC you wish to describe. - :type network_interface: :class:`ProfitBricksNetworkInterface` - - :rtype: : ``bool`` - """ - - action = 'deleteNic' - body = {'action': action, - 'nicId': network_interface.id} - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - def ex_set_inet_access(self, datacenter, - network_interface, internet_access=True): - - action = 'setInternetAccess' - - body = {'action': action, - 'dataCenterId': datacenter.id, - 'lanId': network_interface.extra['lan_id'], - 'internetAccess': str(internet_access).lower() - } - - self.connection.request(action=action, - data=body, method='POST').object - - return True - - """ - Private Functions - """ - - def _to_datacenters(self, object): - return [self._to_datacenter( - datacenter) for datacenter in object.findall('.//return')] - - def _to_datacenter(self, datacenter): - datacenter_id = datacenter.find('dataCenterId').text - if ET.iselement(datacenter.find('dataCenterName')): - datacenter_name = datacenter.find('dataCenterName').text - else: - datacenter_name = None - version = datacenter.find('dataCenterVersion').text - if ET.iselement(datacenter.find('provisioningState')): - provisioning_state = datacenter.find('provisioningState').text - else: - provisioning_state = None - if ET.iselement(datacenter.find('location')): - location = datacenter.find('location').text - else: - location = None - - provisioning_state = self.PROVISIONING_STATE.get(provisioning_state, - NodeState.UNKNOWN) - - return Datacenter(id=datacenter_id, - name=datacenter_name, - version=version, - driver=self.connection.driver, - extra={'provisioning_state': provisioning_state, - 'location': location}) - - def _to_images(self, object): - return [self._to_image(image) for image in object.findall('.//return')] - - def _to_image(self, image): - image_id = image.find('imageId').text - image_name = image.find('imageName').text - image_size = image.find('imageSize').text - image_type = image.find('imageType').text - os_type = image.find('osType').text - public = image.find('public').text - writeable = image.find('writeable').text - - if ET.iselement(image.find('cpuHotpluggable')): - cpu_hotpluggable = image.find('cpuHotpluggable').text - else: - cpu_hotpluggable = None - - if ET.iselement(image.find('memoryHotpluggable')): - memory_hotpluggable = image.find('memoryHotpluggable').text - else: - memory_hotpluggable = None - - if ET.iselement(image.find('location')): - if image.find('region'): - image_region = image.find('region').text - else: - image_region = None - else: - image_region = None - - return NodeImage(id=image_id, - name=image_name, - driver=self.connection.driver, - extra={'image_size': image_size, - 'image_type': image_type, - 'cpu_hotpluggable': cpu_hotpluggable, - 'memory_hotpluggable': memory_hotpluggable, - 'os_type': os_type, - 'public': public, - 'location': image_region, - 'writeable': writeable}) - - def _to_nodes(self, object): - return [self._to_node(n) for n in object.findall('.//return')] - - def _to_node(self, node): - """ - Convert the request into a node Node - """ - ATTRIBUTE_NAME_MAP = { - 'dataCenterId': 'datacenter_id', - 'dataCenterVersion': 'datacenter_version', - 'serverId': 'node_id', - 'serverName': 'node_name', - 'cores': 'cores', - 'ram': 'ram', - 'internetAccess': 'internet_access', - 'provisioningState': 'provisioning_state', - 'virtualMachineState': 'virtual_machine_state', - 'creationTime': 'creation_time', - 'lastModificationTime': 'last_modification_time', - 'osType': 'os_type', - 'availabilityZone': 'availability_zone', - 'cpuHotPlug': 'cpu_hotpluggable', - 'ramHotPlug': 'memory_hotpluggable', - 'nicHotPlug': 'nic_hotpluggable', - 'discVirtioHotPlug': 'disc_virtio_hotplug', - 'discVirtioHotUnPlug': 'disc_virtio_hotunplug' - } - - extra = {} - for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items(): - elem = node.find(attribute_name) - - if ET.iselement(elem): - value = elem.text - else: - value = None - - extra[extra_name] = value - - public_ips = [] - private_ips = [] - - if ET.iselement(node.find('nics')): - for nic in node.findall('.//nics'): - n_elements = list(nic.findall('.//ips')) - if len(n_elements) > 0: - ip = n_elements[0].text - if is_private_subnet(ip): - private_ips.append(ip) - else: - public_ips.append(ip) - - extra['provisioning_state'] = self.PROVISIONING_STATE.get( - extra['provisioning_state'], NodeState.UNKNOWN) - - node_id = extra['node_id'] - node_name = extra['node_name'] - state = self.NODE_STATE_MAP.get(extra['virtual_machine_state'], - NodeState.UNKNOWN) - - return Node( - id=node_id, - name=node_name, - state=state, - public_ips=public_ips, - private_ips=private_ips, - driver=self.connection.driver, - extra=extra) - - def _to_volumes(self, object): - return [self._to_volume( - volume) for volume in object.findall('.//return')] - - def _to_volume(self, volume, node=None): - ATTRIBUTE_NAME_MAP = { - 'dataCenterId': 'datacenter_id', - 'storageId': 'storage_id', - 'storageName': 'storage_name', - 'serverIds': 'server_id', - 'creationTime': 'creation_time', - 'lastModificationTime': 'last_modification_time', - 'provisioningState': 'provisioning_state', - 'size': 'size', - } - - extra = {} - for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items(): - elem = volume.find(attribute_name) - - if ET.iselement(elem): - value = elem.text - else: - value = None - - extra[extra_name] = value - - if ET.iselement(volume.find('mountImage')): - image_id = volume.find('mountImage')[0].text - image_name = volume.find('mountImage')[1].text - else: - image_id = None - image_name = None - - extra['image_id'] = image_id - extra['image_name'] = image_name - extra['size'] = int(extra['size']) if extra['size'] else 0 - extra['provisioning_state'] = \ - self.PROVISIONING_STATE.get(extra['provisioning_state'], - NodeState.UNKNOWN) - - storage_id = extra['storage_id'] - storage_name = extra['storage_name'] - size = extra['size'] - - return StorageVolume( - id=storage_id, - name=storage_name, - size=size, - driver=self.connection.driver, - extra=extra) - - def _to_interfaces(self, object): - return [self._to_interface( - interface) for interface in object.findall('.//return')] - - def _to_interface(self, interface): - ATTRIBUTE_NAME_MAP = { - 'nicId': 'nic_id', - 'nicName': 'nic_name', - 'serverId': 'server_id', - 'lanId': 'lan_id', - 'internetAccess': 'internet_access', - 'macAddress': 'mac_address', - 'dhcpActive': 'dhcp_active', - 'gatewayIp': 'gateway_ip', - 'provisioningState': 'provisioning_state', - 'dataCenterId': 'datacenter_id', - 'dataCenterVersion': 'datacenter_version' - } - - extra = {} - for attribute_name, extra_name in ATTRIBUTE_NAME_MAP.items(): - elem = interface.find(attribute_name) - - if ET.iselement(elem): - value = elem.text - else: - value = None - - extra[extra_name] = value - - ips = [] - - if ET.iselement(interface.find('ips')): - for ip in interface.findall('.//ips'): - ips.append(ip.text) - - extra['ips'] = ips - - nic_id = extra['nic_id'] - nic_name = extra['nic_name'] - state = self.PROVISIONING_STATE.get(extra['provisioning_state'], - NodeState.UNKNOWN) - - return ProfitBricksNetworkInterface( - id=nic_id, - name=nic_name, - state=state, - extra=extra) - - def _to_location(self, data): - - return NodeLocation(id=data["region"], - name=data["region"], - country=data["country"], - driver=self.connection.driver) - - def _to_node_size(self, data): - """ - Convert the PROFIT_BRICKS_GENERIC_SIZES into NodeSize - """ - return NodeSize(id=data["id"], - name=data["name"], - ram=data["ram"], - disk=data["disk"], - bandwidth=None, - price=None, - driver=self.connection.driver, - extra={ - 'cores': data["cores"]}) - - def _wait_for_datacenter_state(self, datacenter, state=NodeState.RUNNING, - timeout=300, interval=5): - """ - Private function that waits the datacenter to transition into the - specified state. - - :return: Datacenter object on success. - :rtype: :class:`.Datacenter` - """ - wait_time = 0 - datacenter = self.ex_describe_datacenter(datacenter_id=datacenter.id) - - while (datacenter.extra['provisioning_state'] != state): - datacenter = \ - self.ex_describe_datacenter(datacenter_id=datacenter.id) - if datacenter.extra['provisioning_state'] == state: - break - - if wait_time >= timeout: - raise Exception('Datacenter didn\'t transition to %s state ' - 'in %s seconds' % (state, timeout)) - - wait_time += interval - time.sleep(interval) - - return datacenter - - def _create_new_datacenter_for_node(self, name, location): - """ - Creates a Datacenter for a node. - """ - dc_name = name + '-DC' - - if not location: - loc = 'us/las' - else: - loc = location.id - return self.ex_create_datacenter(name=dc_name, location=loc) - - def _wait_for_storage_volume_state(self, volume, state=NodeState.RUNNING, - timeout=300, interval=5): - """ - Wait for volume to transition into the specified state. - - :return: Volume object on success. - :rtype: :class:`Volume` - """ - wait_time = 0 - volume = self.ex_describe_volume(volume_id=volume.id) - - while (volume.extra['provisioning_state'] != state): - volume = self.ex_describe_volume(volume_id=volume.id) - if volume.extra['provisioning_state'] == state: - break - - if wait_time >= timeout: - raise Exception('Volume didn\'t transition to %s state ' - 'in %s seconds' % (state, timeout)) - - wait_time += interval - time.sleep(interval) - - return volume - - def _create_node_volume(self, ex_disk, image, password, - name, ex_datacenter=None, new_datacenter=None): - - volume_name = name + '-volume' - - if ex_datacenter: - volume = self.create_volume(size=ex_disk, - ex_datacenter=ex_datacenter, - ex_image=image, - ex_password=password, - name=volume_name) - else: - volume = self.create_volume(size=ex_disk, - ex_datacenter=new_datacenter, - ex_image=image, - ex_password=password, - name=volume_name) - - return volume http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py deleted file mode 100644 index 4aab926..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rackspace.py +++ /dev/null @@ -1,253 +0,0 @@ -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Rackspace driver -""" -from libcloud.compute.types import Provider, LibcloudError, VolumeSnapshotState -from libcloud.compute.base import NodeLocation, VolumeSnapshot -from libcloud.compute.drivers.openstack import OpenStack_1_0_Connection,\ - OpenStack_1_0_NodeDriver, OpenStack_1_0_Response -from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection,\ - OpenStack_1_1_NodeDriver - -from libcloud.common.rackspace import AUTH_URL -from libcloud.utils.iso8601 import parse_date - -SERVICE_TYPE = 'compute' -SERVICE_NAME_GEN1 = 'cloudServers' -SERVICE_NAME_GEN2 = 'cloudServersOpenStack' -ENDPOINT_ARGS_MAP = { - 'dfw': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'DFW'}, - 'ord': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'ORD'}, - 'iad': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'IAD'}, - 'lon': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'LON'}, - 'syd': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'SYD'}, - 'hkg': {'service_type': SERVICE_TYPE, - 'name': SERVICE_NAME_GEN2, - 'region': 'HKG'}, - -} - - -class RackspaceFirstGenConnection(OpenStack_1_0_Connection): - """ - Connection class for the Rackspace first-gen driver. - """ - responseCls = OpenStack_1_0_Response - XML_NAMESPACE = 'http://docs.rackspacecloud.com/servers/api/v1.0' - auth_url = AUTH_URL - _auth_version = '2.0' - cache_busting = True - - def __init__(self, *args, **kwargs): - self.region = kwargs.pop('region', None) - super(RackspaceFirstGenConnection, self).__init__(*args, **kwargs) - - def get_endpoint(self): - if '2.0' in self._auth_version: - ep = self.service_catalog.get_endpoint(service_type=SERVICE_TYPE, - name=SERVICE_NAME_GEN1) - else: - raise LibcloudError( - 'Auth version "%s" not supported' % (self._auth_version)) - - public_url = ep.url - - if not public_url: - raise LibcloudError('Could not find specified endpoint') - - # This is a nasty hack, but it's required because of how the - # auth system works. - # Old US accounts can access UK API endpoint, but they don't - # have this endpoint in the service catalog. Same goes for the - # old UK accounts and US endpoint. - if self.region == 'us': - # Old UK account, which only have uk endpoint in the catalog - public_url = public_url.replace('https://lon.servers.api', - 'https://servers.api') - elif self.region == 'uk': - # Old US account, which only has us endpoints in the catalog - public_url = public_url.replace('https://servers.api', - 'https://lon.servers.api') - - return public_url - - def get_service_name(self): - return SERVICE_NAME_GEN1 - - -class RackspaceFirstGenNodeDriver(OpenStack_1_0_NodeDriver): - name = 'Rackspace Cloud (First Gen)' - website = 'http://www.rackspace.com' - connectionCls = RackspaceFirstGenConnection - type = Provider.RACKSPACE_FIRST_GEN - api_name = 'rackspace' - - def __init__(self, key, secret=None, secure=True, host=None, port=None, - region='us', **kwargs): - """ - @inherits: :class:`NodeDriver.__init__` - - :param region: Region ID which should be used - :type region: ``str`` - """ - if region not in ['us', 'uk']: - raise ValueError('Invalid region: %s' % (region)) - - super(RackspaceFirstGenNodeDriver, self).__init__(key=key, - secret=secret, - secure=secure, - host=host, - port=port, - region=region, - **kwargs) - - def list_locations(self): - """ - Lists available locations - - Locations cannot be set or retrieved via the API, but currently - there are two locations, DFW and ORD. - - @inherits: :class:`OpenStack_1_0_NodeDriver.list_locations` - """ - if self.region == 'us': - locations = [NodeLocation(0, "Rackspace DFW1/ORD1", 'US', self)] - elif self.region == 'uk': - locations = [NodeLocation(0, 'Rackspace UK London', 'UK', self)] - - return locations - - def _ex_connection_class_kwargs(self): - kwargs = self.openstack_connection_kwargs() - kwargs['region'] = self.region - return kwargs - - -class RackspaceConnection(OpenStack_1_1_Connection): - """ - Connection class for the Rackspace next-gen OpenStack base driver. - """ - - auth_url = AUTH_URL - _auth_version = '2.0' - - def __init__(self, *args, **kwargs): - self.region = kwargs.pop('region', None) - self.get_endpoint_args = kwargs.pop('get_endpoint_args', None) - super(RackspaceConnection, self).__init__(*args, **kwargs) - - def get_service_name(self): - if not self.get_endpoint_args: - # if they used ex_force_base_url, assume the Rackspace default - return SERVICE_NAME_GEN2 - - return self.get_endpoint_args.get('name', SERVICE_NAME_GEN2) - - def get_endpoint(self): - if not self.get_endpoint_args: - raise LibcloudError( - 'RackspaceConnection must have get_endpoint_args set') - - if '2.0' in self._auth_version: - ep = self.service_catalog.get_endpoint(**self.get_endpoint_args) - else: - raise LibcloudError( - 'Auth version "%s" not supported' % (self._auth_version)) - - public_url = ep.url - - if not public_url: - raise LibcloudError('Could not find specified endpoint') - - return public_url - - -class RackspaceNodeDriver(OpenStack_1_1_NodeDriver): - name = 'Rackspace Cloud (Next Gen)' - website = 'http://www.rackspace.com' - connectionCls = RackspaceConnection - type = Provider.RACKSPACE - - _networks_url_prefix = '/os-networksv2' - - def __init__(self, key, secret=None, secure=True, host=None, port=None, - region='dfw', **kwargs): - """ - @inherits: :class:`NodeDriver.__init__` - - :param region: ID of the region which should be used. - :type region: ``str`` - """ - valid_regions = ENDPOINT_ARGS_MAP.keys() - - if region not in valid_regions: - raise ValueError('Invalid region: %s' % (region)) - - if region == 'lon': - self.api_name = 'rackspacenovalon' - elif region == 'syd': - self.api_name = 'rackspacenovasyd' - else: - self.api_name = 'rackspacenovaus' - - super(RackspaceNodeDriver, self).__init__(key=key, secret=secret, - secure=secure, host=host, - port=port, - region=region, - **kwargs) - - def _to_snapshot(self, api_node): - if 'snapshot' in api_node: - api_node = api_node['snapshot'] - - extra = {'volume_id': api_node['volumeId'], - 'name': api_node['displayName'], - 'created': api_node['createdAt'], - 'description': api_node['displayDescription'], - 'status': api_node['status']} - - state = self.SNAPSHOT_STATE_MAP.get( - api_node['status'], - VolumeSnapshotState.UNKNOWN - ) - - try: - created_td = parse_date(api_node['createdAt']) - except ValueError: - created_td = None - - snapshot = VolumeSnapshot(id=api_node['id'], driver=self, - size=api_node['size'], - extra=extra, - created=created_td, - state=state) - return snapshot - - def _ex_connection_class_kwargs(self): - endpoint_args = ENDPOINT_ARGS_MAP[self.region] - kwargs = self.openstack_connection_kwargs() - kwargs['region'] = self.region - kwargs['get_endpoint_args'] = endpoint_args - return kwargs http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py deleted file mode 100644 index 4cf8457..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/rimuhosting.py +++ /dev/null @@ -1,339 +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. -""" -RimuHosting Driver -""" -try: - import simplejson as json -except ImportError: - import json - -from libcloud.common.base import ConnectionKey, JsonResponse -from libcloud.common.types import InvalidCredsError -from libcloud.compute.types import Provider, NodeState -from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation -from libcloud.compute.base import NodeImage - -API_CONTEXT = '/r' -API_HOST = 'rimuhosting.com' - - -class RimuHostingException(Exception): - """ - Exception class for RimuHosting driver - """ - - def __str__(self): - return self.args[0] - - def __repr__(self): - return "<RimuHostingException '%s'>" % (self.args[0]) - - -class RimuHostingResponse(JsonResponse): - """ - Response Class for RimuHosting driver - """ - def success(self): - if self.status == 403: - raise InvalidCredsError() - return True - - def parse_body(self): - try: - js = super(RimuHostingResponse, self).parse_body() - keys = list(js.keys()) - if js[keys[0]]['response_type'] == "ERROR": - raise RimuHostingException( - js[keys[0]]['human_readable_message'] - ) - return js[keys[0]] - except KeyError: - raise RimuHostingException('Could not parse body: %s' - % (self.body)) - - -class RimuHostingConnection(ConnectionKey): - """ - Connection class for the RimuHosting driver - """ - - api_context = API_CONTEXT - host = API_HOST - port = 443 - responseCls = RimuHostingResponse - - def __init__(self, key, secure=True, retry_delay=None, - backoff=None, timeout=None): - # override __init__ so that we can set secure of False for testing - ConnectionKey.__init__(self, key, secure, timeout=timeout, - retry_delay=retry_delay, backoff=backoff) - - def add_default_headers(self, headers): - # We want JSON back from the server. Could be application/xml - # (but JSON is better). - headers['Accept'] = 'application/json' - # Must encode all data as json, or override this header. - headers['Content-Type'] = 'application/json' - - headers['Authorization'] = 'rimuhosting apikey=%s' % (self.key) - return headers - - def request(self, action, params=None, data='', headers=None, - method='GET'): - if not headers: - headers = {} - if not params: - params = {} - # Override this method to prepend the api_context - return ConnectionKey.request(self, self.api_context + action, - params, data, headers, method) - - -class RimuHostingNodeDriver(NodeDriver): - """ - RimuHosting node driver - """ - - type = Provider.RIMUHOSTING - name = 'RimuHosting' - website = 'http://rimuhosting.com/' - connectionCls = RimuHostingConnection - features = {'create_node': ['password']} - - def __init__(self, key, host=API_HOST, port=443, - api_context=API_CONTEXT, secure=True): - """ - :param key: API key (required) - :type key: ``str`` - - :param host: hostname for connection - :type host: ``str`` - - :param port: Override port used for connections. - :type port: ``int`` - - :param api_context: Optional API context. - :type api_context: ``str`` - - :param secure: Weither to use HTTPS or HTTP. - :type secure: ``bool`` - - :rtype: ``None`` - """ - # Pass in some extra vars so that - self.key = key - self.secure = secure - self.connection = self.connectionCls(key, secure) - self.connection.host = host - self.connection.api_context = api_context - self.connection.port = port - self.connection.driver = self - self.connection.connect() - - def _order_uri(self, node, resource): - # Returns the order uri with its resourse appended. - return "/orders/%s/%s" % (node.id, resource) - - # TODO: Get the node state. - def _to_node(self, order): - n = Node(id=order['slug'], - name=order['domain_name'], - state=NodeState.RUNNING, - public_ips=( - [order['allocated_ips']['primary_ip']] + - order['allocated_ips']['secondary_ips']), - private_ips=[], - driver=self.connection.driver, - extra={ - 'order_oid': order['order_oid'], - 'monthly_recurring_fee': order.get( - 'billing_info').get('monthly_recurring_fee')}) - return n - - def _to_size(self, plan): - return NodeSize( - id=plan['pricing_plan_code'], - name=plan['pricing_plan_description'], - ram=plan['minimum_memory_mb'], - disk=plan['minimum_disk_gb'], - bandwidth=plan['minimum_data_transfer_allowance_gb'], - price=plan['monthly_recurring_amt']['amt_usd'], - driver=self.connection.driver - ) - - def _to_image(self, image): - return NodeImage(id=image['distro_code'], - name=image['distro_description'], - driver=self.connection.driver) - - def list_sizes(self, location=None): - # Returns a list of sizes (aka plans) - # Get plans. Note this is really just for libcloud. - # We are happy with any size. - if location is None: - location = '' - else: - location = ";dc_location=%s" % (location.id) - - res = self.connection.request( - '/pricing-plans;server-type=VPS%s' % (location)).object - return list(map(lambda x: self._to_size(x), res['pricing_plan_infos'])) - - def list_nodes(self): - # Returns a list of Nodes - # Will only include active ones. - res = self.connection.request('/orders;include_inactive=N').object - return list(map(lambda x: self._to_node(x), res['about_orders'])) - - def list_images(self, location=None): - # Get all base images. - # TODO: add other image sources. (Such as a backup of a VPS) - # All Images are available for use at all locations - res = self.connection.request('/distributions').object - return list(map(lambda x: self._to_image(x), res['distro_infos'])) - - def reboot_node(self, node): - # Reboot - # PUT the state of RESTARTING to restart a VPS. - # All data is encoded as JSON - data = {'reboot_request': {'running_state': 'RESTARTING'}} - uri = self._order_uri(node, 'vps/running-state') - self.connection.request(uri, data=json.dumps(data), method='PUT') - # XXX check that the response was actually successful - return True - - def destroy_node(self, node): - # Shutdown a VPS. - uri = self._order_uri(node, 'vps') - self.connection.request(uri, method='DELETE') - # XXX check that the response was actually successful - return True - - def create_node(self, **kwargs): - """Creates a RimuHosting instance - - @inherits: :class:`NodeDriver.create_node` - - :keyword name: Must be a FQDN. e.g example.com. - :type name: ``str`` - - :keyword ex_billing_oid: If not set, - a billing method is automatically picked. - :type ex_billing_oid: ``str`` - - :keyword ex_host_server_oid: The host server to set the VPS up on. - :type ex_host_server_oid: ``str`` - - :keyword ex_vps_order_oid_to_clone: Clone another VPS to use as - the image for the new VPS. - :type ex_vps_order_oid_to_clone: ``str`` - - :keyword ex_num_ips: Number of IPs to allocate. Defaults to 1. - :type ex_num_ips: ``int`` - - :keyword ex_extra_ip_reason: Reason for needing the extra IPs. - :type ex_extra_ip_reason: ``str`` - - :keyword ex_memory_mb: Memory to allocate to the VPS. - :type ex_memory_mb: ``int`` - - :keyword ex_disk_space_mb: Diskspace to allocate to the VPS. - Defaults to 4096 (4GB). - :type ex_disk_space_mb: ``int`` - - :keyword ex_disk_space_2_mb: Secondary disk size allocation. - Disabled by default. - :type ex_disk_space_2_mb: ``int`` - - :keyword ex_control_panel: Control panel to install on the VPS. - :type ex_control_panel: ``str`` - """ - # Note we don't do much error checking in this because we - # expect the API to error out if there is a problem. - name = kwargs['name'] - image = kwargs['image'] - size = kwargs['size'] - - data = { - 'instantiation_options': { - 'domain_name': name, - 'distro': image.id - }, - 'pricing_plan_code': size.id, - 'vps_parameters': {} - } - - if 'ex_control_panel' in kwargs: - data['instantiation_options']['control_panel'] = \ - kwargs['ex_control_panel'] - - auth = self._get_and_check_auth(kwargs.get('auth')) - data['instantiation_options']['password'] = auth.password - - if 'ex_billing_oid' in kwargs: - # TODO check for valid oid. - data['billing_oid'] = kwargs['ex_billing_oid'] - - if 'ex_host_server_oid' in kwargs: - data['host_server_oid'] = kwargs['ex_host_server_oid'] - - if 'ex_vps_order_oid_to_clone' in kwargs: - data['vps_order_oid_to_clone'] = \ - kwargs['ex_vps_order_oid_to_clone'] - - if 'ex_num_ips' in kwargs and int(kwargs['ex_num_ips']) > 1: - if 'ex_extra_ip_reason' not in kwargs: - raise RimuHostingException( - 'Need an reason for having an extra IP') - else: - if 'ip_request' not in data: - data['ip_request'] = {} - data['ip_request']['num_ips'] = int(kwargs['ex_num_ips']) - data['ip_request']['extra_ip_reason'] = \ - kwargs['ex_extra_ip_reason'] - - if 'ex_memory_mb' in kwargs: - data['vps_parameters']['memory_mb'] = kwargs['ex_memory_mb'] - - if 'ex_disk_space_mb' in kwargs: - data['vps_parameters']['disk_space_mb'] = \ - kwargs['ex_disk_space_mb'] - - if 'ex_disk_space_2_mb' in kwargs: - data['vps_parameters']['disk_space_2_mb'] =\ - kwargs['ex_disk_space_2_mb'] - - # Don't send empty 'vps_parameters' attribute - if not data['vps_parameters']: - del data['vps_parameters'] - - res = self.connection.request( - '/orders/new-vps', - method='POST', - data=json.dumps({"new-vps": data}) - ).object - node = self._to_node(res['about_order']) - node.extra['password'] = \ - res['new_order_request']['instantiation_options']['password'] - return node - - def list_locations(self): - return [ - NodeLocation('DCAUCKLAND', "RimuHosting Auckland", 'NZ', self), - NodeLocation('DCDALLAS', "RimuHosting Dallas", 'US', self), - NodeLocation('DCLONDON', "RimuHosting London", 'GB', self), - NodeLocation('DCSYDNEY', "RimuHosting Sydney", 'AU', self), - ]
