http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py deleted file mode 100644 index 6c73e46..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gogrid.py +++ /dev/null @@ -1,464 +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. -""" -GoGrid driver -""" -import time -import hashlib -import copy - -from libcloud.utils.py3 import b - -from libcloud.common.types import InvalidCredsError, LibcloudError -from libcloud.common.gogrid import GoGridConnection, BaseGoGridDriver -from libcloud.compute.providers import Provider -from libcloud.compute.types import NodeState -from libcloud.compute.base import Node, NodeDriver -from libcloud.compute.base import NodeSize, NodeImage, NodeLocation - -STATE = { - "Starting": NodeState.PENDING, - "On": NodeState.RUNNING, - "On/Saving": NodeState.RUNNING, - "Off": NodeState.PENDING, - "Restarting": NodeState.REBOOTING, - "Saving": NodeState.PENDING, - "Restoring": NodeState.PENDING, -} - -GOGRID_INSTANCE_TYPES = { - '512MB': {'id': '512MB', - 'name': '512MB', - 'ram': 512, - 'disk': 30, - 'bandwidth': None}, - '1GB': {'id': '1GB', - 'name': '1GB', - 'ram': 1024, - 'disk': 60, - 'bandwidth': None}, - '2GB': {'id': '2GB', - 'name': '2GB', - 'ram': 2048, - 'disk': 120, - 'bandwidth': None}, - '4GB': {'id': '4GB', - 'name': '4GB', - 'ram': 4096, - 'disk': 240, - 'bandwidth': None}, - '8GB': {'id': '8GB', - 'name': '8GB', - 'ram': 8192, - 'disk': 480, - 'bandwidth': None}, - '16GB': {'id': '16GB', - 'name': '16GB', - 'ram': 16384, - 'disk': 960, - 'bandwidth': None}, - '24GB': {'id': '24GB', - 'name': '24GB', - 'ram': 24576, - 'disk': 960, - 'bandwidth': None}, -} - - -class GoGridNode(Node): - # Generating uuid based on public ip to get around missing id on - # create_node in gogrid api - # - # Used public ip since it is not mutable and specified at create time, - # so uuid of node should not change after add is completed - def get_uuid(self): - return hashlib.sha1( - b("%s:%s" % (self.public_ips, self.driver.type)) - ).hexdigest() - - -class GoGridNodeDriver(BaseGoGridDriver, NodeDriver): - """ - GoGrid node driver - """ - - connectionCls = GoGridConnection - type = Provider.GOGRID - api_name = 'gogrid' - name = 'GoGrid' - website = 'http://www.gogrid.com/' - features = {"create_node": ["generates_password"]} - - _instance_types = GOGRID_INSTANCE_TYPES - - def __init__(self, *args, **kwargs): - """ - @inherits: :class:`NodeDriver.__init__` - """ - super(GoGridNodeDriver, self).__init__(*args, **kwargs) - - def _get_state(self, element): - try: - return STATE[element['state']['name']] - except: - pass - return NodeState.UNKNOWN - - def _get_ip(self, element): - return element.get('ip').get('ip') - - def _get_id(self, element): - return element.get('id') - - def _to_node(self, element, password=None): - state = self._get_state(element) - ip = self._get_ip(element) - id = self._get_id(element) - n = GoGridNode(id=id, - name=element['name'], - state=state, - public_ips=[ip], - private_ips=[], - extra={'ram': element.get('ram').get('name'), - 'description': element.get('description', '')}, - driver=self.connection.driver) - if password: - n.extra['password'] = password - - return n - - def _to_image(self, element): - n = NodeImage(id=element['id'], - name=element['friendlyName'], - driver=self.connection.driver) - return n - - def _to_images(self, object): - return [self._to_image(el) - for el in object['list']] - - def _to_location(self, element): - location = NodeLocation(id=element['id'], - name=element['name'], - country="US", - driver=self.connection.driver) - return location - - def _to_locations(self, object): - return [self._to_location(el) - for el in object['list']] - - def list_images(self, location=None): - params = {} - if location is not None: - params["datacenter"] = location.id - images = self._to_images( - self.connection.request('/api/grid/image/list', params).object) - return images - - def list_nodes(self): - """ - @inherits: :class:`NodeDriver.list_nodes` - :rtype: ``list`` of :class:`GoGridNode` - """ - passwords_map = {} - - res = self._server_list() - try: - for password in self._password_list()['list']: - try: - passwords_map[password['server']['id']] = \ - password['password'] - except KeyError: - pass - except InvalidCredsError: - # some gogrid API keys don't have permission to access the - # password list. - pass - - return [self._to_node(el, passwords_map.get(el.get('id'))) - for el in res['list']] - - def reboot_node(self, node): - """ - @inherits: :class:`NodeDriver.reboot_node` - :type node: :class:`GoGridNode` - """ - id = node.id - power = 'restart' - res = self._server_power(id, power) - if not res.success(): - raise Exception(res.parse_error()) - return True - - def destroy_node(self, node): - """ - @inherits: :class:`NodeDriver.reboot_node` - :type node: :class:`GoGridNode` - """ - id = node.id - res = self._server_delete(id) - if not res.success(): - raise Exception(res.parse_error()) - return True - - def _server_list(self): - return self.connection.request('/api/grid/server/list').object - - def _password_list(self): - return self.connection.request('/api/support/password/list').object - - def _server_power(self, id, power): - # power in ['start', 'stop', 'restart'] - params = {'id': id, 'power': power} - return self.connection.request("/api/grid/server/power", params, - method='POST') - - def _server_delete(self, id): - params = {'id': id} - return self.connection.request("/api/grid/server/delete", params, - method='POST') - - def _get_first_ip(self, location=None): - ips = self.ex_list_ips(public=True, assigned=False, location=location) - try: - return ips[0].ip - except IndexError: - raise LibcloudError('No public unassigned IPs left', - GoGridNodeDriver) - - def list_sizes(self, location=None): - sizes = [] - for key, values in self._instance_types.items(): - attributes = copy.deepcopy(values) - attributes.update({'price': self._get_size_price(size_id=key)}) - sizes.append(NodeSize(driver=self.connection.driver, **attributes)) - - return sizes - - def list_locations(self): - locations = self._to_locations( - self.connection.request('/api/common/lookup/list', - params={'lookup': 'ip.datacenter'}).object) - return locations - - def ex_create_node_nowait(self, **kwargs): - """Don't block until GoGrid allocates id for a node - but return right away with id == None. - - The existence of this method is explained by the fact - that GoGrid assigns id to a node only few minutes after - creation. - - - :keyword name: String with a name for this new node (required) - :type name: ``str`` - - :keyword size: The size of resources allocated to this node . - (required) - :type size: :class:`NodeSize` - - :keyword image: OS Image to boot on node. (required) - :type image: :class:`NodeImage` - - :keyword ex_description: Description of a Node - :type ex_description: ``str`` - - :keyword ex_ip: Public IP address to use for a Node. If not - specified, first available IP address will be picked - :type ex_ip: ``str`` - - :rtype: :class:`GoGridNode` - """ - name = kwargs['name'] - image = kwargs['image'] - size = kwargs['size'] - try: - ip = kwargs['ex_ip'] - except KeyError: - ip = self._get_first_ip(kwargs.get('location')) - - params = {'name': name, - 'image': image.id, - 'description': kwargs.get('ex_description', ''), - 'server.ram': size.id, - 'ip': ip} - - object = self.connection.request('/api/grid/server/add', - params=params, method='POST').object - node = self._to_node(object['list'][0]) - - return node - - def create_node(self, **kwargs): - """Create a new GoGird node - - @inherits: :class:`NodeDriver.create_node` - - :keyword ex_description: Description of a Node - :type ex_description: ``str`` - - :keyword ex_ip: Public IP address to use for a Node. If not - specified, first available IP address will be picked - :type ex_ip: ``str`` - - :rtype: :class:`GoGridNode` - """ - node = self.ex_create_node_nowait(**kwargs) - - timeout = 60 * 20 - waittime = 0 - interval = 2 * 60 - - while node.id is None and waittime < timeout: - nodes = self.list_nodes() - - for i in nodes: - if i.public_ips[0] == node.public_ips[0] and i.id is not None: - return i - - waittime += interval - time.sleep(interval) - - if id is None: - raise Exception( - "Wasn't able to wait for id allocation for the node %s" - % str(node)) - - return node - - def ex_save_image(self, node, name): - """Create an image for node. - - Please refer to GoGrid documentation to get info - how prepare a node for image creation: - - http://wiki.gogrid.com/wiki/index.php/MyGSI - - :keyword node: node to use as a base for image - :type node: :class:`GoGridNode` - - :keyword name: name for new image - :type name: ``str`` - - :rtype: :class:`NodeImage` - """ - params = {'server': node.id, - 'friendlyName': name} - object = self.connection.request('/api/grid/image/save', params=params, - method='POST').object - - return self._to_images(object)[0] - - def ex_edit_node(self, **kwargs): - """Change attributes of a node. - - :keyword node: node to be edited (required) - :type node: :class:`GoGridNode` - - :keyword size: new size of a node (required) - :type size: :class:`NodeSize` - - :keyword ex_description: new description of a node - :type ex_description: ``str`` - - :rtype: :class:`Node` - """ - node = kwargs['node'] - size = kwargs['size'] - - params = {'id': node.id, - 'server.ram': size.id} - - if 'ex_description' in kwargs: - params['description'] = kwargs['ex_description'] - - object = self.connection.request('/api/grid/server/edit', - params=params).object - - return self._to_node(object['list'][0]) - - def ex_edit_image(self, **kwargs): - """Edit metadata of a server image. - - :keyword image: image to be edited (required) - :type image: :class:`NodeImage` - - :keyword public: should be the image public (required) - :type public: ``bool`` - - :keyword ex_description: description of the image (optional) - :type ex_description: ``str`` - - :keyword name: name of the image - :type name: ``str`` - - :rtype: :class:`NodeImage` - """ - - image = kwargs['image'] - public = kwargs['public'] - - params = {'id': image.id, - 'isPublic': str(public).lower()} - - if 'ex_description' in kwargs: - params['description'] = kwargs['ex_description'] - - if 'name' in kwargs: - params['friendlyName'] = kwargs['name'] - - object = self.connection.request('/api/grid/image/edit', - params=params).object - - return self._to_image(object['list'][0]) - - def ex_list_ips(self, **kwargs): - """Return list of IP addresses assigned to - the account. - - :keyword public: set to True to list only - public IPs or False to list only - private IPs. Set to None or not specify - at all not to filter by type - :type public: ``bool`` - - :keyword assigned: set to True to list only addresses - assigned to servers, False to list unassigned - addresses and set to None or don't set at all - not no filter by state - :type assigned: ``bool`` - - :keyword location: filter IP addresses by location - :type location: :class:`NodeLocation` - - :rtype: ``list`` of :class:`GoGridIpAddress` - """ - - params = {} - - if "public" in kwargs and kwargs["public"] is not None: - params["ip.type"] = {True: "Public", - False: "Private"}[kwargs["public"]] - if "assigned" in kwargs and kwargs["assigned"] is not None: - params["ip.state"] = {True: "Assigned", - False: "Unassigned"}[kwargs["assigned"]] - if "location" in kwargs and kwargs['location'] is not None: - params['datacenter'] = kwargs['location'].id - - ips = self._to_ips( - self.connection.request('/api/grid/ip/list', - params=params).object) - return ips
http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py deleted file mode 100644 index 856b068..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/gridspot.py +++ /dev/null @@ -1,127 +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. - -from libcloud.compute.base import NodeDriver, Node -from libcloud.compute.base import NodeState -from libcloud.common.base import ConnectionKey, JsonResponse -from libcloud.compute.types import Provider -from libcloud.common.types import InvalidCredsError - - -class GridspotAPIException(Exception): - def __str__(self): - return self.args[0] - - def __repr__(self): - return "<GridspotAPIException '%s'>" % (self.args[0]) - - -class GridspotResponse(JsonResponse): - """ - Response class for Gridspot - """ - def parse_body(self): - body = super(GridspotResponse, self).parse_body() - - if 'exception_name' in body and body['exception_name']: - raise GridspotAPIException(body['exception_name']) - - return body - - def parse_error(self): - # Gridspot 404s on invalid api key or instance_id - raise InvalidCredsError("Invalid api key/instance_id") - - -class GridspotConnection(ConnectionKey): - """ - Connection class to connect to Gridspot's API servers - """ - - host = 'gridspot.com' - responseCls = GridspotResponse - - def add_default_params(self, params): - params['api_key'] = self.key - return params - - -class GridspotNodeDriver(NodeDriver): - """ - Gridspot (http://www.gridspot.com/) node driver. - """ - - type = Provider.GRIDSPOT - name = 'Gridspot' - website = 'http://www.gridspot.com/' - connectionCls = GridspotConnection - NODE_STATE_MAP = { - 'Running': NodeState.RUNNING, - 'Starting': NodeState.PENDING - } - - def list_nodes(self): - data = self.connection.request( - '/compute_api/v1/list_instances').object - return [self._to_node(n) for n in data['instances']] - - def destroy_node(self, node): - data = {'instance_id': node.id} - self.connection.request('/compute_api/v1/stop_instance', data).object - return True - - def _get_node_state(self, state): - result = self.NODE_STATE_MAP.get(state, NodeState.UNKNOWN) - return result - - def _add_int_param(self, params, data, field): - if data[field]: - try: - params[field] = int(data[field]) - except: - pass - - def _to_node(self, data): - port = None - ip = None - - state = self._get_node_state(data['current_state']) - - if data['vm_ssh_wan_ip_endpoint'] != 'null': - parts = data['vm_ssh_wan_ip_endpoint'].split(':') - ip = parts[0] - port = int(parts[1]) - - extra_params = { - 'winning_bid_id': data['winning_bid_id'], - 'port': port - } - - # Spec is vague and doesn't indicate if these will always be present - self._add_int_param(extra_params, data, 'vm_num_logical_cores') - self._add_int_param(extra_params, data, 'vm_num_physical_cores') - self._add_int_param(extra_params, data, 'vm_ram') - self._add_int_param(extra_params, data, 'start_state_time') - self._add_int_param(extra_params, data, 'ended_state_time') - self._add_int_param(extra_params, data, 'running_state_time') - - return Node( - id=data['instance_id'], - name=data['instance_id'], - state=state, - public_ips=[ip], - private_ips=[], - driver=self.connection.driver, - extra=extra_params) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py deleted file mode 100644 index e56889e..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/hostvirtual.py +++ /dev/null @@ -1,449 +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. - -""" -libcloud driver for the Host Virtual Inc. (VR) API -Home page https://www.hostvirtual.com/ -""" - -import time -import re - -try: - import simplejson as json -except ImportError: - import json - -from libcloud.common.hostvirtual import HostVirtualResponse -from libcloud.common.hostvirtual import HostVirtualConnection -from libcloud.common.hostvirtual import HostVirtualException -from libcloud.compute.providers import Provider -from libcloud.compute.types import NodeState -from libcloud.compute.base import Node, NodeDriver -from libcloud.compute.base import NodeImage, NodeSize, NodeLocation -from libcloud.compute.base import NodeAuthSSHKey, NodeAuthPassword - -API_ROOT = '' - -NODE_STATE_MAP = { - 'BUILDING': NodeState.PENDING, - 'PENDING': NodeState.PENDING, - 'RUNNING': NodeState.RUNNING, # server is powered up - 'STOPPING': NodeState.REBOOTING, - 'REBOOTING': NodeState.REBOOTING, - 'STARTING': NodeState.REBOOTING, - 'TERMINATED': NodeState.TERMINATED, # server is powered down - 'STOPPED': NodeState.STOPPED -} - -DEFAULT_NODE_LOCATION_ID = 21 - - -class HostVirtualComputeResponse(HostVirtualResponse): - pass - - -class HostVirtualComputeConnection(HostVirtualConnection): - responseCls = HostVirtualComputeResponse - - -class HostVirtualNodeDriver(NodeDriver): - type = Provider.HOSTVIRTUAL - name = 'HostVirtual' - website = 'http://www.hostvirtual.com' - connectionCls = HostVirtualComputeConnection - features = {'create_node': ['ssh_key', 'password']} - - def __init__(self, key, secure=True, host=None, port=None): - self.location = None - super(HostVirtualNodeDriver, self).__init__(key=key, secure=secure, - host=host, port=port) - - def list_nodes(self): - try: - result = self.connection.request( - API_ROOT + '/cloud/servers/').object - except HostVirtualException: - return [] - nodes = [] - for value in result: - node = self._to_node(value) - nodes.append(node) - return nodes - - def list_locations(self): - result = self.connection.request(API_ROOT + '/cloud/locations/').object - locations = [] - for dc in result: - locations.append(NodeLocation( - dc["id"], - dc["name"], - dc["name"].split(',')[1].replace(" ", ""), # country - self)) - return locations - - def list_sizes(self, location=None): - params = {} - if location is not None: - params = {'location': location.id} - result = self.connection.request( - API_ROOT + '/cloud/sizes/', - params=params).object - sizes = [] - for size in result: - n = NodeSize(id=size['plan_id'], - name=size['plan'], - ram=size['ram'], - disk=size['disk'], - bandwidth=size['transfer'], - price=size['price'], - driver=self.connection.driver) - sizes.append(n) - return sizes - - def list_images(self): - result = self.connection.request(API_ROOT + '/cloud/images/').object - images = [] - for image in result: - i = NodeImage(id=image["id"], - name=image["os"], - driver=self.connection.driver, - extra=image) - del i.extra['id'] - del i.extra['os'] - images.append(i) - return images - - def create_node(self, name, image, size, **kwargs): - """ - Creates a node - - Example of node creation with ssh key deployed: - - >>> from libcloud.compute.base import NodeAuthSSHKey - >>> key = open('/home/user/.ssh/id_rsa.pub').read() - >>> auth = NodeAuthSSHKey(pubkey=key) - >>> from libcloud.compute.providers import get_driver; - >>> driver = get_driver('hostvirtual') - >>> conn = driver('API_KEY') - >>> image = conn.list_images()[1] - >>> size = conn.list_sizes()[0] - >>> location = conn.list_locations()[1] - >>> name = 'markos-dev' - >>> node = conn.create_node(name, image, size, auth=auth, - >>> location=location) - """ - - dc = None - - auth = self._get_and_check_auth(kwargs.get('auth')) - - if not self._is_valid_fqdn(name): - raise HostVirtualException( - 500, "Name should be a valid FQDN (e.g, hostname.example.com)") - - # simply order a package first - pkg = self.ex_order_package(size) - - if 'location' in kwargs: - dc = kwargs['location'].id - else: - dc = DEFAULT_NODE_LOCATION_ID - - # create a stub node - stub_node = self._to_node({ - 'mbpkgid': pkg['id'], - 'status': 'PENDING', - 'fqdn': name, - 'plan_id': size.id, - 'os_id': image.id, - 'location_id': dc - }) - - # provisioning a server using the stub node - self.ex_provision_node(node=stub_node, auth=auth) - node = self._wait_for_node(stub_node.id) - if getattr(auth, 'generated', False): - node.extra['password'] = auth.password - - return node - - def reboot_node(self, node): - params = {'force': 0, 'mbpkgid': node.id} - result = self.connection.request( - API_ROOT + '/cloud/server/reboot', - data=json.dumps(params), - method='POST').object - - return bool(result) - - def destroy_node(self, node): - params = { - 'mbpkgid': node.id, - # 'reason': 'Submitted through Libcloud API' - } - - result = self.connection.request( - API_ROOT + '/cloud/cancel', data=json.dumps(params), - method='POST').object - - return bool(result) - - def ex_list_packages(self): - """ - List the server packages. - - """ - - try: - result = self.connection.request( - API_ROOT + '/cloud/packages/').object - except HostVirtualException: - return [] - pkgs = [] - for value in result: - pkgs.append(value) - return pkgs - - def ex_order_package(self, size): - """ - Order a server package. - - :param size: - :type node: :class:`NodeSize` - - :rtype: ``str`` - """ - - params = {'plan': size.name} - pkg = self.connection.request(API_ROOT + '/cloud/buy/', - data=json.dumps(params), - method='POST').object - - return pkg - - def ex_cancel_package(self, node): - """ - Cancel a server package. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``str`` - """ - - params = {'mbpkgid': node.id} - result = self.connection.request(API_ROOT + '/cloud/cancel/', - data=json.dumps(params), - method='POST').object - - return result - - def ex_unlink_package(self, node): - """ - Unlink a server package from location. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``str`` - """ - - params = {'mbpkgid': node.id} - result = self.connection.request(API_ROOT + '/cloud/unlink/', - data=json.dumps(params), - method='POST').object - - return result - - def ex_get_node(self, node_id): - """ - Get a single node. - - :param node_id: id of the node that we need the node object for - :type node_id: ``str`` - - :rtype: :class:`Node` - """ - - params = {'mbpkgid': node_id} - result = self.connection.request( - API_ROOT + '/cloud/server', params=params).object - node = self._to_node(result) - return node - - def ex_stop_node(self, node): - """ - Stop a node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - params = {'force': 0, 'mbpkgid': node.id} - result = self.connection.request( - API_ROOT + '/cloud/server/shutdown', - data=json.dumps(params), - method='POST').object - - return bool(result) - - def ex_start_node(self, node): - """ - Start a node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - params = {'mbpkgid': node.id} - result = self.connection.request( - API_ROOT + '/cloud/server/start', - data=json.dumps(params), - method='POST').object - - return bool(result) - - def ex_provision_node(self, **kwargs): - """ - Provision a server on a VR package and get it booted - - :keyword node: node which should be used - :type node: :class:`Node` - - :keyword image: The distribution to deploy on your server (mandatory) - :type image: :class:`NodeImage` - - :keyword auth: an SSH key or root password (mandatory) - :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword` - - :keyword location: which datacenter to create the server in - :type location: :class:`NodeLocation` - - :return: Node representing the newly built server - :rtype: :class:`Node` - """ - - node = kwargs['node'] - - if 'image' in kwargs: - image = kwargs['image'] - else: - image = node.extra['image'] - - params = { - 'mbpkgid': node.id, - 'image': image, - 'fqdn': node.name, - 'location': node.extra['location'], - } - - auth = kwargs['auth'] - - ssh_key = None - password = None - if isinstance(auth, NodeAuthSSHKey): - ssh_key = auth.pubkey - params['ssh_key'] = ssh_key - elif isinstance(auth, NodeAuthPassword): - password = auth.password - params['password'] = password - - if not ssh_key and not password: - raise HostVirtualException( - 500, "SSH key or Root password is required") - - try: - result = self.connection.request(API_ROOT + '/cloud/server/build', - data=json.dumps(params), - method='POST').object - return bool(result) - except HostVirtualException: - self.ex_cancel_package(node) - - def ex_delete_node(self, node): - """ - Delete a node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - - params = {'mbpkgid': node.id} - result = self.connection.request( - API_ROOT + '/cloud/server/delete', data=json.dumps(params), - method='POST').object - - return bool(result) - - def _to_node(self, data): - state = NODE_STATE_MAP[data['status']] - public_ips = [] - private_ips = [] - extra = {} - - if 'plan_id' in data: - extra['size'] = data['plan_id'] - if 'os_id' in data: - extra['image'] = data['os_id'] - if 'fqdn' in data: - extra['fqdn'] = data['fqdn'] - if 'location_id' in data: - extra['location'] = data['location_id'] - if 'ip' in data: - public_ips.append(data['ip']) - - node = Node(id=data['mbpkgid'], name=data['fqdn'], state=state, - public_ips=public_ips, private_ips=private_ips, - driver=self.connection.driver, extra=extra) - return node - - def _wait_for_node(self, node_id, timeout=30, interval=5.0): - """ - :param node_id: ID of the node to wait for. - :type node_id: ``int`` - - :param timeout: Timeout (in seconds). - :type timeout: ``int`` - - :param interval: How long to wait (in seconds) between each attempt. - :type interval: ``float`` - - :return: Node representing the newly built server - :rtype: :class:`Node` - """ - # poll until we get a node - for i in range(0, timeout, int(interval)): - try: - node = self.ex_get_node(node_id) - return node - except HostVirtualException: - time.sleep(interval) - - raise HostVirtualException(412, 'Timeout on getting node details') - - def _is_valid_fqdn(self, fqdn): - if len(fqdn) > 255: - return False - if fqdn[-1] == ".": - fqdn = fqdn[:-1] - valid = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) - if len(fqdn.split(".")) > 1: - return all(valid.match(x) for x in fqdn.split(".")) - else: - return False http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py deleted file mode 100644 index 554c647..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ikoula.py +++ /dev/null @@ -1,31 +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. - -from libcloud.compute.providers import Provider -from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver - -__all__ = [ - 'IkoulaNodeDriver' -] - - -class IkoulaNodeDriver(CloudStackNodeDriver): - type = Provider.IKOULA - name = 'Ikoula' - website = 'http://express.ikoula.co.uk/cloudstack' - - # API endpoint info - host = 'cloudstack.ikoula.com' - path = '/client/api' http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py deleted file mode 100644 index 8913dfb..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/indosat.py +++ /dev/null @@ -1,56 +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. -""" -Indosat Driver -""" - -from libcloud.compute.providers import Provider -from libcloud.common.dimensiondata import (DimensionDataConnection, - API_ENDPOINTS) -from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver - -DEFAULT_REGION = 'indosat-id' - - -class IndosatNodeDriver(DimensionDataNodeDriver): - """ - Indosat node driver, based on Dimension Data driver - """ - - selected_region = None - connectionCls = DimensionDataConnection - name = 'Indosat' - website = 'http://www.indosat.com/' - type = Provider.INDOSAT - features = {'create_node': ['password']} - api_version = 1.0 - - def __init__(self, key, secret=None, secure=True, host=None, port=None, - api_version=None, region=DEFAULT_REGION, **kwargs): - - if region not in API_ENDPOINTS: - raise ValueError('Invalid region: %s' % (region)) - - self.selected_region = API_ENDPOINTS[region] - - super(IndosatNodeDriver, self).__init__( - key=key, - secret=secret, - secure=secure, - host=host, - port=port, - api_version=api_version, - region=region, - **kwargs) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py deleted file mode 100644 index 49415ae..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/internetsolutions.py +++ /dev/null @@ -1,56 +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. -""" -Internet Solutions Driver -""" - -from libcloud.compute.providers import Provider -from libcloud.common.dimensiondata import (DimensionDataConnection, - API_ENDPOINTS) -from libcloud.compute.drivers.dimensiondata import DimensionDataNodeDriver - -DEFAULT_REGION = 'is-af' - - -class InternetSolutionsNodeDriver(DimensionDataNodeDriver): - """ - InternetSolutions node driver, based on Dimension Data driver - """ - - selected_region = None - connectionCls = DimensionDataConnection - name = 'InternetSolutions' - website = 'http://www.is.co.za/' - type = Provider.INTERNETSOLUTIONS - features = {'create_node': ['password']} - api_version = 1.0 - - def __init__(self, key, secret=None, secure=True, host=None, port=None, - api_version=None, region=DEFAULT_REGION, **kwargs): - - if region not in API_ENDPOINTS: - raise ValueError('Invalid region: %s' % (region)) - - self.selected_region = API_ENDPOINTS[region] - - super(InternetSolutionsNodeDriver, self).__init__( - key=key, - secret=secret, - secure=secure, - host=host, - port=port, - api_version=api_version, - region=region, - **kwargs) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py deleted file mode 100644 index d1fa9c1..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/joyent.py +++ /dev/null @@ -1,240 +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. - -""" -Joyent Cloud (http://www.joyentcloud.com) driver. -""" - -import base64 - -try: - import simplejson as json -except: - import json - -from libcloud.utils.py3 import httplib -from libcloud.utils.py3 import b - -from libcloud.common.types import LibcloudError -from libcloud.compute.providers import Provider -from libcloud.common.base import JsonResponse, ConnectionUserAndKey -from libcloud.compute.types import NodeState, InvalidCredsError -from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeSize -from libcloud.utils.networking import is_private_subnet - -API_HOST_SUFFIX = '.api.joyentcloud.com' -API_VERSION = '~6.5' - - -NODE_STATE_MAP = { - 'provisioning': NodeState.PENDING, - 'running': NodeState.RUNNING, - 'stopping': NodeState.TERMINATED, - 'stopped': NodeState.TERMINATED, - 'deleted': NodeState.TERMINATED -} - -VALID_REGIONS = [ - 'us-east-1', 'us-east-2', 'us-east-3', - 'us-west-1', - 'us-sw-1', - 'eu-ams-1' -] -DEFAULT_REGION = 'us-east-1' - - -class JoyentResponse(JsonResponse): - """ - Joyent response class. - """ - - valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, - httplib.NO_CONTENT] - - def parse_error(self): - if self.status == httplib.UNAUTHORIZED: - data = self.parse_body() - raise InvalidCredsError(data['code'] + ': ' + data['message']) - return self.body - - def success(self): - return self.status in self.valid_response_codes - - -class JoyentConnection(ConnectionUserAndKey): - """ - Joyent connection class. - """ - - responseCls = JoyentResponse - - allow_insecure = False - - def add_default_headers(self, headers): - headers['Accept'] = 'application/json' - headers['Content-Type'] = 'application/json; charset=UTF-8' - headers['X-Api-Version'] = API_VERSION - - user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key))) - headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8')) - return headers - - -class JoyentNodeDriver(NodeDriver): - """ - Joyent node driver class. - """ - - type = Provider.JOYENT - name = 'Joyent' - website = 'http://www.joyentcloud.com' - connectionCls = JoyentConnection - features = {'create_node': ['generates_password']} - - def __init__(self, key, secret=None, secure=True, host=None, port=None, - region=DEFAULT_REGION, **kwargs): - # Location is here for backward compatibility reasons - if 'location' in kwargs: - region = kwargs['location'] - - if region not in VALID_REGIONS: - msg = 'Invalid region: "%s". Valid region: %s' - raise LibcloudError(msg % (region, - ', '.join(VALID_REGIONS)), driver=self) - - super(JoyentNodeDriver, self).__init__(key=key, secret=secret, - secure=secure, host=host, - port=port, region=region, - **kwargs) - self.connection.host = region + API_HOST_SUFFIX - - def list_images(self): - result = self.connection.request('/my/datasets').object - - images = [] - for value in result: - extra = {'type': value['type'], 'urn': value['urn'], - 'os': value['os'], 'default': value['default']} - image = NodeImage(id=value['id'], name=value['name'], - driver=self.connection.driver, extra=extra) - images.append(image) - - return images - - def list_sizes(self): - result = self.connection.request('/my/packages').object - - sizes = [] - for value in result: - size = NodeSize(id=value['name'], name=value['name'], - ram=value['memory'], disk=value['disk'], - bandwidth=None, price=0.0, - driver=self.connection.driver) - sizes.append(size) - - return sizes - - def list_nodes(self): - result = self.connection.request('/my/machines').object - - nodes = [] - for value in result: - node = self._to_node(value) - nodes.append(node) - - return nodes - - def reboot_node(self, node): - data = json.dumps({'action': 'reboot'}) - result = self.connection.request('/my/machines/%s' % (node.id), - data=data, method='POST') - return result.status == httplib.ACCEPTED - - def destroy_node(self, node): - result = self.connection.request('/my/machines/%s' % (node.id), - method='DELETE') - return result.status == httplib.NO_CONTENT - - def create_node(self, **kwargs): - name = kwargs['name'] - size = kwargs['size'] - image = kwargs['image'] - - data = json.dumps({'name': name, 'package': size.id, - 'dataset': image.id}) - result = self.connection.request('/my/machines', data=data, - method='POST') - return self._to_node(result.object) - - def ex_stop_node(self, node): - """ - Stop node - - :param node: The node to be stopped - :type node: :class:`Node` - - :rtype: ``bool`` - """ - data = json.dumps({'action': 'stop'}) - result = self.connection.request('/my/machines/%s' % (node.id), - data=data, method='POST') - return result.status == httplib.ACCEPTED - - def ex_start_node(self, node): - """ - Start node - - :param node: The node to be stopped - :type node: :class:`Node` - - :rtype: ``bool`` - """ - data = json.dumps({'action': 'start'}) - result = self.connection.request('/my/machines/%s' % (node.id), - data=data, method='POST') - return result.status == httplib.ACCEPTED - - def ex_get_node(self, node_id): - """ - Return a Node object based on a node ID. - - :param node_id: ID of the node - :type node_id: ``str`` - - :return: A Node object for the node - :rtype: :class:`Node` - """ - result = self.connection.request('/my/machines/%s' % (node_id)) - return self._to_node(result.object) - - def _to_node(self, data): - state = NODE_STATE_MAP[data['state']] - public_ips = [] - private_ips = [] - extra = {} - - for ip in data['ips']: - if is_private_subnet(ip): - private_ips.append(ip) - else: - public_ips.append(ip) - - if 'credentials' in data['metadata']: - extra['password'] = data['metadata']['credentials']['root'] - - node = Node(id=data['id'], name=data['name'], state=state, - public_ips=public_ips, private_ips=private_ips, - driver=self.connection.driver, extra=extra) - return node http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py deleted file mode 100644 index 11c1c7b..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/kili.py +++ /dev/null @@ -1,87 +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. - -""" -HP Public cloud driver which is essentially just a small wrapper around -OpenStack driver. -""" - -from libcloud.compute.types import Provider, LibcloudError -from libcloud.compute.drivers.openstack import OpenStack_1_1_Connection -from libcloud.compute.drivers.openstack import OpenStack_1_1_NodeDriver - -__all__ = [ - 'KiliCloudNodeDriver' -] - -ENDPOINT_ARGS = { - 'service_type': 'compute', - 'name': 'nova', - 'region': 'RegionOne' -} - -AUTH_URL = 'https://api.kili.io/keystone/v2.0/tokens' - - -class KiliCloudConnection(OpenStack_1_1_Connection): - _auth_version = '2.0_password' - - def __init__(self, *args, **kwargs): - self.region = kwargs.pop('region', None) - self.get_endpoint_args = kwargs.pop('get_endpoint_args', None) - super(KiliCloudConnection, self).__init__(*args, **kwargs) - - def get_endpoint(self): - if not self.get_endpoint_args: - raise LibcloudError( - 'KiliCloudConnection must have get_endpoint_args set') - - if '2.0_password' 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 KiliCloudNodeDriver(OpenStack_1_1_NodeDriver): - name = 'Kili Public Cloud' - website = 'http://kili.io/' - connectionCls = KiliCloudConnection - type = Provider.HPCLOUD - - def __init__(self, key, secret, tenant_name, secure=True, - host=None, port=None, **kwargs): - """ - Note: tenant_name argument is required for Kili cloud. - """ - self.tenant_name = tenant_name - super(KiliCloudNodeDriver, self).__init__(key=key, secret=secret, - secure=secure, host=host, - port=port, - **kwargs) - - def _ex_connection_class_kwargs(self): - kwargs = self.openstack_connection_kwargs() - kwargs['get_endpoint_args'] = ENDPOINT_ARGS - kwargs['ex_force_auth_url'] = AUTH_URL - kwargs['ex_tenant_name'] = self.tenant_name - - return kwargs http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py deleted file mode 100644 index 1bc8544..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/ktucloud.py +++ /dev/null @@ -1,103 +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. - -from libcloud.compute.providers import Provider -from libcloud.compute.base import Node, NodeImage, NodeSize -from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver - - -class KTUCloudNodeDriver(CloudStackNodeDriver): - "Driver for KTUCloud Compute platform." - - EMPTY_DISKOFFERINGID = '0' - type = Provider.KTUCLOUD - name = 'KTUCloud' - website = 'https://ucloudbiz.olleh.com/' - - def list_images(self, location=None): - args = { - 'templatefilter': 'executable' - } - if location is not None: - args['zoneid'] = location.id - - imgs = self._sync_request(command='listAvailableProductTypes', - method='GET') - images = [] - - for img in imgs['producttypes']: - images.append( - NodeImage( - img['serviceofferingid'], - img['serviceofferingdesc'], - self, - {'hypervisor': '', - 'format': '', - 'os': img['templatedesc'], - 'templateid': img['templateid'], - 'zoneid': img['zoneid']} - ) - ) - - return images - - def list_sizes(self, location=None): - szs = self._sync_request('listAvailableProductTypes') - sizes = [] - for sz in szs['producttypes']: - diskofferingid = sz.get('diskofferingid', - self.EMPTY_DISKOFFERINGID) - sizes.append(NodeSize( - diskofferingid, - sz['diskofferingdesc'], - 0, 0, 0, 0, self) - ) - return sizes - - def create_node(self, name, size, image, location=None, **kwargs): - params = {'displayname': name, - 'serviceofferingid': image.id, - 'templateid': str(image.extra['templateid']), - 'zoneid': str(image.extra['zoneid'])} - - usageplantype = kwargs.pop('usageplantype', None) - if usageplantype is None: - params['usageplantype'] = 'hourly' - else: - params['usageplantype'] = usageplantype - - if size.id != self.EMPTY_DISKOFFERINGID: - params['diskofferingid'] = size.id - - result = self._async_request( - command='deployVirtualMachine', - params=params, - method='GET') - - node = result['virtualmachine'] - - return Node( - id=node['id'], - name=node['displayname'], - state=self.NODE_STATE_MAP[node['state']], - public_ips=[], - private_ips=[], - driver=self, - extra={ - 'zoneid': image.extra['zoneid'], - 'ip_addresses': [], - 'forwarding_rules': [], - } - ) http://git-wip-us.apache.org/repos/asf/libcloud/blob/8afcda91/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py ---------------------------------------------------------------------- diff --git a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py b/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py deleted file mode 100644 index 3618ac4..0000000 --- a/apache-libcloud-1.0.0rc2/libcloud/compute/drivers/libvirt_driver.py +++ /dev/null @@ -1,335 +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. -from __future__ import with_statement - -import re -import os -import time -import platform -import subprocess -import mimetypes - -from os.path import join as pjoin -from collections import defaultdict - -try: - from lxml import etree as ET -except ImportError: - from xml.etree import ElementTree as ET - -from libcloud.compute.base import NodeDriver, Node -from libcloud.compute.base import NodeState -from libcloud.compute.types import Provider -from libcloud.utils.networking import is_public_subnet - -try: - import libvirt - have_libvirt = True -except ImportError: - have_libvirt = False - - -class LibvirtNodeDriver(NodeDriver): - """ - Libvirt (http://libvirt.org/) node driver. - - To enable debug mode, set LIBVIR_DEBUG environment variable. - """ - - type = Provider.LIBVIRT - name = 'Libvirt' - website = 'http://libvirt.org/' - - NODE_STATE_MAP = { - 0: NodeState.TERMINATED, # no state - 1: NodeState.RUNNING, # domain is running - 2: NodeState.PENDING, # domain is blocked on resource - 3: NodeState.TERMINATED, # domain is paused by user - 4: NodeState.TERMINATED, # domain is being shut down - 5: NodeState.TERMINATED, # domain is shut off - 6: NodeState.UNKNOWN, # domain is crashed - 7: NodeState.UNKNOWN, # domain is suspended by guest power management - } - - def __init__(self, uri): - """ - :param uri: Hypervisor URI (e.g. vbox:///session, qemu:///system, - etc.). - :type uri: ``str`` - """ - if not have_libvirt: - raise RuntimeError('Libvirt driver requires \'libvirt\' Python ' + - 'package') - - self._uri = uri - self.connection = libvirt.open(uri) - - def list_nodes(self): - domains = self.connection.listAllDomains() - nodes = self._to_nodes(domains=domains) - return nodes - - def reboot_node(self, node): - domain = self._get_domain_for_node(node=node) - return domain.reboot(flags=0) == 0 - - def destroy_node(self, node): - domain = self._get_domain_for_node(node=node) - return domain.destroy() == 0 - - def ex_start_node(self, node): - """ - Start a stopped node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - domain = self._get_domain_for_node(node=node) - return domain.create() == 0 - - def ex_shutdown_node(self, node): - """ - Shutdown a running node. - - Note: Usually this will result in sending an ACPI event to the node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - domain = self._get_domain_for_node(node=node) - return domain.shutdown() == 0 - - def ex_suspend_node(self, node): - """ - Suspend a running node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - domain = self._get_domain_for_node(node=node) - return domain.suspend() == 0 - - def ex_resume_node(self, node): - """ - Resume a suspended node. - - :param node: Node which should be used - :type node: :class:`Node` - - :rtype: ``bool`` - """ - domain = self._get_domain_for_node(node=node) - return domain.resume() == 0 - - def ex_take_node_screenshot(self, node, directory, screen=0): - """ - Take a screenshot of a monitoring of a running instance. - - :param node: Node to take the screenshot of. - :type node: :class:`libcloud.compute.base.Node` - - :param directory: Path where the screenshot will be saved. - :type directory: ``str`` - - :param screen: ID of the monitor to take the screenshot of. - :type screen: ``int`` - - :return: Full path where the screenshot has been saved. - :rtype: ``str`` - """ - if not os.path.exists(directory) or not os.path.isdir(directory): - raise ValueError('Invalid value for directory argument') - - domain = self._get_domain_for_node(node=node) - stream = self.connection.newStream() - mime_type = domain.screenshot(stream=stream, screen=0) - extensions = mimetypes.guess_all_extensions(type=mime_type) - - if extensions: - extension = extensions[0] - else: - extension = '.png' - - name = 'screenshot-%s%s' % (int(time.time()), extension) - file_path = pjoin(directory, name) - - with open(file_path, 'wb') as fp: - def write(stream, buf, opaque): - fp.write(buf) - - stream.recvAll(write, None) - - try: - stream.finish() - except Exception: - # Finish is not supported by all backends - pass - - return file_path - - def ex_get_hypervisor_hostname(self): - """ - Return a system hostname on which the hypervisor is running. - """ - hostname = self.connection.getHostname() - return hostname - - def ex_get_hypervisor_sysinfo(self): - """ - Retrieve hypervisor system information. - - :rtype: ``dict`` - """ - xml = self.connection.getSysinfo() - etree = ET.XML(xml) - - attributes = ['bios', 'system', 'processor', 'memory_device'] - - sysinfo = {} - for attribute in attributes: - element = etree.find(attribute) - entries = self._get_entries(element=element) - sysinfo[attribute] = entries - - return sysinfo - - def _to_nodes(self, domains): - nodes = [self._to_node(domain=domain) for domain in domains] - return nodes - - def _to_node(self, domain): - state, max_mem, memory, vcpu_count, used_cpu_time = domain.info() - state = self.NODE_STATE_MAP.get(state, NodeState.UNKNOWN) - - public_ips, private_ips = [], [] - - ip_addresses = self._get_ip_addresses_for_domain(domain) - - for ip_address in ip_addresses: - if is_public_subnet(ip_address): - public_ips.append(ip_address) - else: - private_ips.append(ip_address) - - extra = {'uuid': domain.UUIDString(), 'os_type': domain.OSType(), - 'types': self.connection.getType(), - 'used_memory': memory / 1024, 'vcpu_count': vcpu_count, - 'used_cpu_time': used_cpu_time} - - node = Node(id=domain.ID(), name=domain.name(), state=state, - public_ips=public_ips, private_ips=private_ips, - driver=self, extra=extra) - node._uuid = domain.UUIDString() # we want to use a custom UUID - return node - - def _get_ip_addresses_for_domain(self, domain): - """ - Retrieve IP addresses for the provided domain. - - Note: This functionality is currently only supported on Linux and - only works if this code is run on the same machine as the VMs run - on. - - :return: IP addresses for the provided domain. - :rtype: ``list`` - """ - result = [] - - if platform.system() != 'Linux': - # Only Linux is supported atm - return result - - mac_addresses = self._get_mac_addresses_for_domain(domain=domain) - - cmd = ['arp', '-an'] - child = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, _ = child.communicate() - arp_table = self._parse_arp_table(arp_output=stdout) - - for mac_address in mac_addresses: - if mac_address in arp_table: - ip_addresses = arp_table[mac_address] - result.extend(ip_addresses) - - return result - - def _get_mac_addresses_for_domain(self, domain): - """ - Parses network interface MAC addresses from the provided domain. - """ - xml = domain.XMLDesc() - etree = ET.XML(xml) - elems = etree.findall("devices/interface[@type='network']/mac") - - result = [] - for elem in elems: - mac_address = elem.get('address') - result.append(mac_address) - - return result - - def _get_domain_for_node(self, node): - """ - Return libvirt domain object for the provided node. - """ - domain = self.connection.lookupByUUIDString(node.uuid) - return domain - - def _get_entries(self, element): - """ - Parse entries dictionary. - - :rtype: ``dict`` - """ - elements = element.findall('entry') - - result = {} - for element in elements: - name = element.get('name') - value = element.text - result[name] = value - - return result - - def _parse_arp_table(self, arp_output): - """ - Parse arp command output and return a dictionary which maps mac address - to an IP address. - - :return: Dictionary which maps mac address to IP address. - :rtype: ``dict`` - """ - lines = arp_output.split('\n') - - arp_table = defaultdict(list) - for line in lines: - match = re.match('.*?\((.*?)\) at (.*?)\s+', line) - - if not match: - continue - - groups = match.groups() - ip_address = groups[0] - mac_address = groups[1] - arp_table[mac_address].append(ip_address) - - return arp_table