Updated Branches: refs/heads/trunk bf681d8d1 -> 34ec154b1
[LIBCLOUD-404]: Add Nephoscale driver. Signed-off-by: Tomaz Muraus <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/da2fd8d8 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/da2fd8d8 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/da2fd8d8 Branch: refs/heads/trunk Commit: da2fd8d859b7998bbfd99e4c61de1e62f8dea0e4 Parents: bf681d8 Author: Markos Gogoulos <[email protected]> Authored: Mon Sep 30 14:00:58 2013 +0300 Committer: Tomaz Muraus <[email protected]> Committed: Mon Oct 7 12:55:06 2013 +0200 ---------------------------------------------------------------------- libcloud/compute/drivers/nephoscale.py | 459 +++++++++++++++++++ libcloud/compute/providers.py | 4 +- libcloud/compute/types.py | 2 + libcloud/data/pricing.json | 16 + .../fixtures/nephoscale/list_images.json | 243 ++++++++++ .../compute/fixtures/nephoscale/list_keys.json | 25 + .../fixtures/nephoscale/list_locations.json | 31 ++ .../compute/fixtures/nephoscale/list_nodes.json | 161 +++++++ .../fixtures/nephoscale/list_password_keys.json | 18 + .../compute/fixtures/nephoscale/list_sizes.json | 178 +++++++ .../fixtures/nephoscale/list_ssh_keys.json | 18 + .../fixtures/nephoscale/success_action.json | 11 + libcloud/test/compute/test_nephoscale.py | 189 ++++++++ 13 files changed, 1354 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/compute/drivers/nephoscale.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/nephoscale.py b/libcloud/compute/drivers/nephoscale.py new file mode 100644 index 0000000..80dba81 --- /dev/null +++ b/libcloud/compute/drivers/nephoscale.py @@ -0,0 +1,459 @@ +# 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. + +""" +NephoScale Cloud driver (http://www.nephoscale.com) +API documentation: http://docs.nephoscale.com +Created by Markos Gogoulos (https://mist.io) +""" + +import base64 +import sys +import string +import random +import time +import os +import binascii + +try: + import simplejson as json +except: + import json + +from libcloud.utils.py3 import httplib +from libcloud.utils.py3 import b +from libcloud.utils.py3 import urlencode + +from libcloud.compute.providers import Provider +from libcloud.compute.base import is_private_subnet +from libcloud.common.base import JsonResponse, ConnectionUserAndKey +from libcloud.compute.types import (NodeState, InvalidCredsError, + LibcloudError) +from libcloud.compute.base import (Node, NodeDriver, NodeImage, NodeSize, + NodeLocation) + +API_HOST = 'api.nephoscale.com' + +NODE_STATE_MAP = { + 'on': NodeState.RUNNING, + 'off': NodeState.UNKNOWN, + 'unknown': NodeState.UNKNOWN, +} + +VALID_RESPONSE_CODES = [httplib.OK, httplib.ACCEPTED, httplib.CREATED, + httplib.NO_CONTENT] + +#used in create_node and specifies how many times to get the list of nodes and +#check if the newly created node is there. This is because when a request is +#sent to create a node, NephoScale replies with the job id, and not the node +#itself thus we don't have the ip addresses, that are required in deploy_node +CONNECT_ATTEMPTS = 10 + + +class NodeKey(object): + def __init__(self, id, name, public_key=None, key_group=None, + password=None): + self.id = id + self.name = name + self.key_group = key_group + self.password = password + self.public_key = public_key + + def __repr__(self): + return (('<NodeKey: id=%s, name=%s>') % + (self.id, self.name)) + + +class NephoscaleResponse(JsonResponse): + """ + Nephoscale API Response + """ + + def parse_error(self): + if self.status == httplib.UNAUTHORIZED: + raise InvalidCredsError('Authorization Failed') + if self.status == httplib.NOT_FOUND: + raise Exception("The resource you are looking for is not found.") + + return self.body + + def success(self): + return self.status in VALID_RESPONSE_CODES + + +class NephoscaleConnection(ConnectionUserAndKey): + """ + Nephoscale connection class. + Authenticates to the API through Basic Authentication + with username/password + """ + host = API_HOST + responseCls = NephoscaleResponse + + def add_default_headers(self, headers): + """ + Add parameters that are necessary for every request + """ + user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key))) + headers['Authorization'] = 'Basic %s' % (user_b64.decode('utf-8')) + return headers + + +class NephoscaleNodeDriver(NodeDriver): + """ + Nephoscale node driver class. + + >>> from libcloud.compute.types import Provider + >>> from libcloud.compute.providers import get_driver + >>> driver = get_driver('nephoscale') + >>> conn = driver('nepho_user','nepho_password') + >>> conn.list_nodes() + """ + + type = Provider.NEPHOSCALE + api_name = 'nephoscale' + name = 'NephoScale' + website = 'http://www.nephoscale.com' + connectionCls = NephoscaleConnection + features = {'create_node': ['ssh_key']} + + def list_locations(self): + """ + List available zones for deployment + + :rtype: ``list`` of :class:`NodeLocation` + """ + result = self.connection.request('/datacenter/zone/').object + locations = [] + for value in result.get('data', []): + location = NodeLocation(id=value.get('id'), + name=value.get('name'), + country='US', + driver=self) + locations.append(location) + return locations + + def list_images(self): + """ + List available images for deployment + + :rtype: ``list`` of :class:`NodeImage` + """ + result = self.connection.request('/image/server/').object + images = [] + for value in result.get('data', []): + extra = {'architecture': value.get('architecture'), + 'disks': value.get('disks'), + 'billable_type': value.get('billable_type'), + 'pcpus': value.get('pcpus'), + 'cores': value.get('cores'), + 'uri': value.get('uri'), + 'storage': value.get('storage'), + } + image = NodeImage(id=value.get('id'), + name=value.get('friendly_name'), + driver=self, + extra=extra) + images.append(image) + return images + + def list_sizes(self): + """ + List available sizes containing prices + + :rtype: ``list`` of :class:`NodeSize` + """ + result = self.connection.request('/server/type/cloud/').object + sizes = [] + for value in result.get('data', []): + value_id = value.get('id') + size = NodeSize(id=value_id, + name=value.get('friendly_name'), + ram=value.get('ram'), + disk=value.get('storage'), + bandwidth=None, + price=self._get_size_price(size_id=str(value_id)), + driver=self) + sizes.append(size) + + return sorted(sizes, key=lambda k: k.price) + + def list_nodes(self): + """ + List available nodes + + :rtype: ``list`` of :class:`Node` + """ + result = self.connection.request('/server/cloud/').object + nodes = [self._to_node(value) for value in result.get('data', [])] + return nodes + + def rename_node(self, node, name, hostname=None): + """rename a cloud server, optionally specify hostname too""" + data = {'name': name} + if hostname: + data['hostname'] = hostname + params = urlencode(data) + result = self.connection.request('/server/cloud/%s/' % node.id, + data=params, method='PUT').object + return result.get('response') in VALID_RESPONSE_CODES + + def reboot_node(self, node): + """reboot a running node""" + result = self.connection.request('/server/cloud/%s/initiator/restart/' + % node.id, method='POST').object + return result.get('response') in VALID_RESPONSE_CODES + + def ex_start_node(self, node): + """start a stopped node""" + result = self.connection.request('/server/cloud/%s/initiator/start/' + % node.id, method='POST').object + return result.get('response') in VALID_RESPONSE_CODES + + def ex_stop_node(self, node): + """stop a running node""" + result = self.connection.request('/server/cloud/%s/initiator/stop/' + % node.id, method='POST').object + return result.get('response') in VALID_RESPONSE_CODES + + def destroy_node(self, node): + """destroy a node""" + result = self.connection.request('/server/cloud/%s/' % node.id, + method='DELETE').object + return result.get('response') in VALID_RESPONSE_CODES + + def ex_list_keypairs(self, ssh=False, password=False, key_group=None): + """ + List available console and server keys + There are two types of keys for NephoScale, ssh and password keys. + If run without arguments, lists all keys. Otherwise list only + ssh keys, or only password keys. + Password keys with key_group 4 are console keys. When a server + is created, it has two keys, one password or ssh key, and + one password console key. + + :keyword ssh: if specified, show ssh keys only (optional) + :type ssh: ``bool`` + + :keyword password: if specified, show password keys only (optional) + :type password: ``bool`` + + :keyword key_group: if specified, show keys with this key_group only + eg key_group=4 for console password keys (optional) + :type key_group: ``int`` + + :rtype: ``list`` of :class:`NodeKey` + """ + if (ssh and password): + raise LibcloudError('You can only supply ssh or password. To \ +get all keys call with no arguments') + if ssh: + result = self.connection.request('/key/sshrsa/').object + elif password: + result = self.connection.request('/key/password/').object + else: + result = self.connection.request('/key/').object + keys = [self._to_key(value) for value in result.get('data', [])] + + if key_group: + keys = [key for key in keys if + key.key_group == key_group] + return keys + + def ex_create_keypair(self, name, public_key=None, password=None, + key_group=None): + """Creates a key, ssh or password, for server or console + The group for the key (key_group) is 1 for Server and 4 for Console + Returns the id of the created key + """ + if public_key: + if not key_group: + key_group = 1 + data = { + 'name': name, + 'public_key': public_key, + 'key_group': key_group + + } + params = urlencode(data) + result = self.connection.request('/key/sshrsa/', data=params, + method='POST').object + else: + if not key_group: + key_group = 4 + if not password: + password = self.random_password() + data = { + 'name': name, + 'password': password, + 'key_group': key_group + } + params = urlencode(data) + result = self.connection.request('/key/password/', data=params, + method='POST').object + return result.get('data', {}).get('id', '') + + def ex_delete_keypair(self, key_id, ssh=False): + """Delete an ssh key or password given it's id + """ + if ssh: + result = self.connection.request('/key/sshrsa/%s/' % key_id, + method='DELETE').object + else: + result = self.connection.request('/key/password/%s/' % key_id, + method='DELETE').object + return result.get('response') in VALID_RESPONSE_CODES + + def create_node(self, name, size, image, server_key=None, + console_key=None, zone=None, **kwargs): + """Creates the node, and sets the ssh key, console key + NephoScale will respond with a 200-200 response after sending a valid + request. If nowait=True is specified in the args, we then ask a few + times until the server is created and assigned a public IP address, + so that deploy_node can be run + + >>> from libcloud.compute.types import Provider + >>> from libcloud.compute.providers import get_driver + >>> driver = get_driver('nephoscale') + >>> conn = driver('nepho_user','nepho_password') + >>> conn.list_nodes() + >>> name = 'staging-server' + >>> size = conn.list_sizes()[0] + <NodeSize: id=27, ...name=CS025 - 0.25GB, 10GB, ...> + >>> image = conn.list_images()[9] + <NodeImage: id=49, name=Linux Ubuntu Server 10.04 LTS 64-bit, ...> + >>> server_keys = conn.ex_list_keypairs(key_group=1)[0] + <NodeKey: id=71211, name=markos> + >>> server_key = conn.ex_list_keypairs(key_group=1)[0].id + 70867 + >>> console_keys = conn.ex_list_keypairs(key_group=4)[0] + <NodeKey: id=71213, name=mistio28434> + >>> console_key = conn.ex_list_keypairs(key_group=4)[0].id + 70907 + >>> node = conn.create_node(name=name, size=size, image=image, \ + console_key=console_key, server_key=server_key) + + We can also create an ssh key, plus a console key and + deploy node with them + >>> server_key = conn.ex_create_keypair(name, public_key=key) + 71211 + >>> console_key = conn.ex_create_keypair(name, key_group=4) + 71213 + + We can increase the number of connect attempts to wait until + the node is created, so that deploy_node has ip address to + deploy the script + We can also specify the location + >>> location = conn.list_locations()[0] + >>> node = conn.create_node(name=name, + size=size, + image=image, + console_key=console_key, + server_key=server_key, + connect_attempts=10, + nowait=True, + zone=location.id) + """ + try: + hostname = kwargs.get('hostname', name) + service_type = size.id + image = image.id + connect_attempts = int(kwargs.get('connect_attempts', + CONNECT_ATTEMPTS)) + except Exception: + e = sys.exc_info()[1] + raise Exception("Error on create node: %s" % e) + + data = {'name': name, + 'hostname': hostname, + 'service_type': service_type, + 'image': image, + 'server_key': server_key, + 'console_key': console_key, + 'zone': zone + } + + params = urlencode(data) + try: + node = self.connection.request('/server/cloud/', data=params, + method='POST') + except Exception: + e = sys.exc_info()[1] + raise Exception("Failed to create node %s" % e) + node = Node(id='', name=name, state=NodeState.UNKNOWN, public_ips=[], + private_ips=[], driver=self) + + nowait = kwargs.get('ex_wait', False) + if not nowait: + return node + else: + #try to get the created node public ips, for use in deploy_node + #At this point we don't have the id of the newly created Node, + #so search name in nodes + created_node = False + while connect_attempts > 0: + nodes = self.list_nodes() + created_node = [c_node for c_node in nodes if + c_node.name == name] + if created_node: + return created_node[0] + else: + time.sleep(60) + connect_attempts = connect_attempts - 1 + return node + + def _to_node(self, data): + """Convert node in Node instances + """ + + state = NODE_STATE_MAP.get(data.get('power_status'), '4') + public_ips = [] + private_ips = [] + ip_addresses = data.get('ipaddresses', '') + #E.g. "ipaddresses": "198.120.14.6, 10.132.60.1" + if ip_addresses: + for ip in ip_addresses.split(','): + ip = ip.replace(' ', '') + if is_private_subnet(ip): + private_ips.append(ip) + else: + public_ips.append(ip) + extra = { + 'zone_data': data.get('zone'), + 'zone': data.get('zone', {}).get('name'), + 'image': data.get('image', {}).get('friendly_name'), + 'create_time': data.get('create_time'), + 'network_ports': data.get('network_ports'), + 'is_console_enabled': data.get('is_console_enabled'), + 'service_type': data.get('service_type', {}).get('friendly_name'), + 'hostname': data.get('hostname') + } + + node = Node(id=data.get('id'), name=data.get('name'), state=state, + public_ips=public_ips, private_ips=private_ips, + driver=self, extra=extra) + return node + + def _to_key(self, data): + return NodeKey(id=data.get('id'), + name=data.get('name'), + password=data.get('password'), + key_group=data.get('key_group'), + public_key=data.get('public_key')) + + def random_password(self, size=8): + value = os.urandom(size) + password = binascii.hexlify(value).decode('ascii') + return password[:size] http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/compute/providers.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py index fbc9c63..32c60d6 100644 --- a/libcloud/compute/providers.py +++ b/libcloud/compute/providers.py @@ -126,7 +126,9 @@ DRIVERS = { Provider.ABIQUO: ('libcloud.compute.drivers.abiquo', 'AbiquoNodeDriver'), Provider.DIGITAL_OCEAN: - ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver') + ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver'), + Provider.NEPHOSCALE: + ('libcloud.compute.drivers.nephoscale', 'NephoscaleNodeDriver') } http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/compute/types.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py index 926c750..aefbf5a 100644 --- a/libcloud/compute/types.py +++ b/libcloud/compute/types.py @@ -71,6 +71,7 @@ class Provider(object): :cvar KTUCLOUD: kt ucloud driver :cvar GRIDSPOT: Gridspot driver :cvar ABIQUO: Abiquo driver + @cvar NEPHOSCALE: NephoScale driver """ DUMMY = 'dummy' EC2 = 'ec2_us_east' @@ -117,6 +118,7 @@ class Provider(object): HOSTVIRTUAL = 'hostvirtual' ABIQUO = 'abiquo' DIGITAL_OCEAN = 'digitalocean' + NEPHOSCALE = 'nephoscale' # Deprecated constants which are still supported EC2_US_EAST = 'ec2_us_east' http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/data/pricing.json ---------------------------------------------------------------------- diff --git a/libcloud/data/pricing.json b/libcloud/data/pricing.json index 905d657..7a9b731 100644 --- a/libcloud/data/pricing.json +++ b/libcloud/data/pricing.json @@ -171,6 +171,22 @@ "m3.2xlarge": 1.40 }, + "nephoscale" : { + "1": 0.60, + "3": 0.063, + "5": 0.031, + "7": 0.125, + "9": 0.188, + "11": 0.35, + "27": 0.0, + "46": 0.10, + "48": 0.15, + "50": 0.28, + "52": 0.48, + "54": 0.938, + "56": 0.75 + }, + "nimbus" : { "m1.small": 0.0, "m1.large": 0.0, http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_images.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_images.json b/libcloud/test/compute/fixtures/nephoscale/list_images.json new file mode 100644 index 0000000..1ede35d --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_images.json @@ -0,0 +1,243 @@ +{ + "success": true, + "total_count": 18, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.5 32-bit", + "uri": "https://api.nephoscale.com/image/server/3/", + "max_memory": 128, + "id": 3, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.5 64-bit", + "uri": "https://api.nephoscale.com/image/server/5/", + "max_memory": 128, + "id": 5, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Windows Server 2008 64-bit", + "uri": "https://api.nephoscale.com/image/server/21/", + "max_memory": 128, + "id": 21, + "is_default": true, + "create_time": "2010-12-20 14:25:36", + "architecture": "x86_64", + "base_type": "windows" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 5.05 32-bit", + "uri": "https://api.nephoscale.com/image/server/23/", + "max_memory": 128, + "id": 23, + "is_default": true, + "create_time": "2010-12-20 16:51:20", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 5.05 64-bit", + "uri": "https://api.nephoscale.com/image/server/25/", + "max_memory": 128, + "id": 25, + "is_default": true, + "create_time": "2010-12-20 16:55:42", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Windows Server 2003 Enterprise 64-bit", + "uri": "https://api.nephoscale.com/image/server/33/", + "max_memory": 128, + "id": 33, + "is_default": true, + "create_time": "2011-03-02 14:20:49", + "architecture": "x86_64", + "base_type": "windows" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.7 64-bit", + "uri": "https://api.nephoscale.com/image/server/41/", + "max_memory": 128, + "id": 41, + "is_default": true, + "create_time": "2011-09-19 17:30:04", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 32-bit", + "uri": "https://api.nephoscale.com/image/server/43/", + "max_memory": 128, + "id": 43, + "is_default": true, + "create_time": "2011-10-01 02:26:17", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.7 32-bit", + "uri": "https://api.nephoscale.com/image/server/45/", + "max_memory": 128, + "id": 45, + "is_default": true, + "create_time": "2011-10-05 19:41:30", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/49/", + "max_memory": 128, + "id": 49, + "is_default": true, + "create_time": "2011-10-08 05:01:10", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 6.0.3 64-bit", + "uri": "https://api.nephoscale.com/image/server/51/", + "max_memory": 128, + "id": 51, + "is_default": true, + "create_time": "2011-10-08 19:54:41", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian 5.0.9 64-bit", + "uri": "https://api.nephoscale.com/image/server/55/", + "max_memory": 128, + "id": 55, + "is_default": false, + "create_time": "2011-10-13 12:53:36", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian 5.0.9 32-bit", + "uri": "https://api.nephoscale.com/image/server/57/", + "max_memory": 128, + "id": 57, + "is_default": false, + "create_time": "2011-10-13 12:55:09", + "architecture": "x86", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 6.2 64-bit", + "uri": "https://api.nephoscale.com/image/server/59/", + "max_memory": 128, + "id": 59, + "is_default": true, + "create_time": "2011-10-15 17:11:34", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux CentOS 5.8 64-bit", + "uri": "https://api.nephoscale.com/image/server/64/", + "max_memory": 128, + "id": 64, + "is_default": true, + "create_time": "2012-03-28 19:54:10", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 12.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/75/", + "max_memory": 128, + "id": 75, + "is_default": true, + "create_time": "2012-05-18 08:41:03", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "cloud", + "is_active": true, + "friendly_name": "VOD Cloud Storage Proxy (FTP:HTTP)", + "uri": "https://api.nephoscale.com/image/server/101/", + "max_memory": 128, + "id": 101, + "is_default": false, + "create_time": "2012-08-30 08:49:55", + "architecture": "x86_64", + "base_type": "linux" + }, + { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Debian 7.1 64-bit", + "uri": "https://api.nephoscale.com/image/server/177/", + "max_memory": 128, + "id": 177, + "is_default": true, + "create_time": "2013-09-10 16:12:10", + "architecture": "x86_64", + "base_type": "linux" + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_keys.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_keys.json new file mode 100644 index 0000000..f6f9205 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_keys.json @@ -0,0 +1,25 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "name": "mistio-ssh", + "key_group": 1, + "uri": "https://api.nephoscale.com/key/sshrsa/72209/", + "key_type": 2, + "create_time": "2013-10-02 07:24:37", + "id": 72209 + }, + { + "name": "mistio-testing", + "key_group": 4, + "uri": "https://api.nephoscale.com/key/password/72211/", + "key_type": 1, + "create_time": "2013-10-02 07:27:10", + "id": 72211 + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_locations.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_locations.json b/libcloud/test/compute/fixtures/nephoscale/list_locations.json new file mode 100644 index 0000000..952fac4 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_locations.json @@ -0,0 +1,31 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "datacenter": { + "airport_code": "SJC", + "name": "SJC-1", + "uri": "https://api.nephoscale.com/datacenter/1/", + "id": 1 + }, + "uri": "https://api.nephoscale.com/datacenter/zone/86945/", + "id": 86945, + "name": "SJC-1" + }, + { + "datacenter": { + "airport_code": "RIC", + "name": "RIC-1", + "uri": "https://api.nephoscale.com/datacenter/3/", + "id": 3 + }, + "uri": "https://api.nephoscale.com/datacenter/zone/87729/", + "id": 87729, + "name": "RIC-1" + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_nodes.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_nodes.json b/libcloud/test/compute/fixtures/nephoscale/list_nodes.json new file mode 100644 index 0000000..7fdff6c --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_nodes.json @@ -0,0 +1,161 @@ +{ + "success": true, + "total_count": 2, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "server_keys": [ + { + "key_type": 2, + "key_group": 1, + "id": 71757, + "uri": "https://api.nephoscale.com/key/sshrsa/71157/" + } + ], + "name": "mongodb-staging", + "zone": { + "uri": "https://api.nephoscale.com/datacenter/zone/88211/", + "id": 87729, + "name": "RIC-1" + }, + "image": { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Ubuntu Server 10.04 LTS 64-bit", + "uri": "https://api.nephoscale.com/image/server/49/", + "max_memory": 128, + "id": 49, + "is_default": true, + "create_time": "2011-10-08 05:01:10", + "architecture": "x86_64", + "has_agent": true, + "base_type": "linux" + }, + "hostname": "mongodb-staging", + "podzone": "P1A2", + "uri": "https://api.nephoscale.com/server/cloud/87241/", + "ipaddresses": "198.89.117.16", + "power_status": "on", + "create_time": "2013-09-25 07:38:53", + "postinit_state": 1, + "console_keys": [ + { + "key_type": 1, + "key_group": 4, + "id": 71761, + "uri": "https://api.nephoscale.com/key/password/71761/" + } + ], + "memory": 512, + "service_type": { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "id": 5, + "billable_type": 1 + }, + "network_ports": [ + { + "macaddress": "00:16:3e:06:dc:41", + "devname": "eth0", + "network_domain": { + "domain_type": 0, + "name": "default_public_network_RIC" + } + }, + { + "macaddress": "00:16:3e:06:dc:45", + "devname": "eth1", + "network_domain": { + "domain_type": 1, + "name": "default_private_network_RIC" + } + } + ], + "id": 88241, + "is_console_enabled": true + }, + { + "server_keys": [ + { + "key_type": 2, + "key_group": 1, + "id": 72049, + "uri": "https://api.nephoscale.com/key/sshrsa/72049/" + } + ], + "name": "backup-server2", + "zone": { + "uri": "https://api.nephoscale.com/datacenter/zone/88751/", + "id": 87729, + "name": "RIC-1" + }, + "image": { + "max_cpu": 64, + "deployable_type": "both", + "is_active": true, + "friendly_name": "Linux Debian Server 6.0.3 64-bit", + "uri": "https://api.nephoscale.com/image/server/51/", + "max_memory": 128, + "id": 51, + "is_default": true, + "create_time": "2011-10-08 19:54:41", + "architecture": "x86_64", + "has_agent": true, + "base_type": "linux" + }, + "hostname": "backup-server2", + "podzone": "P1A2", + "uri": "https://api.nephoscale.com/server/cloud/88751/", + "ipaddresses": "198.89.112.115", + "power_status": "on", + "create_time": "2013-10-02 05:02:50", + "postinit_state": 1, + "console_keys": [ + { + "key_type": 1, + "key_group": 4, + "id": 72165, + "uri": "https://api.nephoscale.com/key/password/72165/" + } + ], + "memory": 512, + "service_type": { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "id": 5, + "billable_type": 1 + }, + "network_ports": [ + { + "macaddress": "00:16:3e:06:f5:2f", + "devname": "eth0", + "network_domain": { + "domain_type": 0, + "name": "default_public_network_RIC" + } + }, + { + "macaddress": "00:16:3e:06:f5:33", + "devname": "eth1", + "network_domain": { + "domain_type": 1, + "name": "default_private_network_RIC" + } + } + ], + "id": 88751, + "is_console_enabled": true + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json new file mode 100644 index 0000000..ca3c629 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_password_keys.json @@ -0,0 +1,18 @@ +{ + "success": true, + "total_count": 1, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "name": "mistio-testing", + "key_group": 4, + "uri": "https://api.nephoscale.com/key/password/72211/", + "key_type": 1, + "create_time": "2013-10-02 07:27:10", + "password": "23d493j5", + "id": 72211 + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_sizes.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_sizes.json b/libcloud/test/compute/fixtures/nephoscale/list_sizes.json new file mode 100644 index 0000000..c6d89f3 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_sizes.json @@ -0,0 +1,178 @@ +{ + "success": true, + "total_count": 13, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "sku": { + "name": "CS16.16", + "description": "Cloud Server 16 GB RAM, 16 Cores" + }, + "storage": 800, + "ram": 16384, + "friendly_name": "CS16.16 - 16GB, 16Core, 800GB", + "uri": "https://api.nephoscale.com/server/type/cloud/1/", + "vcpus": 16, + "id": 1, + "billable_type": 1 + }, + { + "sku": { + "name": "CS1", + "description": "Cloud Server 1 GB RAM, 1 Core" + }, + "storage": 50, + "ram": 1024, + "friendly_name": "CS1 - 1GB, 1Core, 50GB", + "uri": "https://api.nephoscale.com/server/type/cloud/3/", + "vcpus": 1, + "id": 3, + "billable_type": 1 + }, + { + "sku": { + "name": "CS05", + "description": "Cloud Server 0.5 GB RAM, 1 Core" + }, + "storage": 25, + "ram": 512, + "friendly_name": "CS05 - 0.5GB, 1Core, 25GB", + "uri": "https://api.nephoscale.com/server/type/cloud/5/", + "vcpus": 1, + "id": 5, + "billable_type": 1 + }, + { + "sku": { + "name": "CS2.2", + "description": "Cloud Server 2 GB RAM, 2 Cores" + }, + "storage": 100, + "ram": 2048, + "friendly_name": "CS2.2 - 2GB, 2Core, 100GB", + "uri": "https://api.nephoscale.com/server/type/cloud/7/", + "vcpus": 2, + "id": 7, + "billable_type": 1 + }, + { + "sku": { + "name": "CS4.4", + "description": "Cloud Server 4 GB RAM, 4 Cores" + }, + "storage": 200, + "ram": 4096, + "friendly_name": "CS4.4 - 4GB, 4Core, 200GB", + "uri": "https://api.nephoscale.com/server/type/cloud/9/", + "vcpus": 4, + "id": 9, + "billable_type": 1 + }, + { + "sku": { + "name": "CS8.8", + "description": "Cloud Server 8 GB RAM, 8 Cores" + }, + "storage": 400, + "ram": 8192, + "friendly_name": "CS8.8 - 8GB, 8Core, 400GB", + "uri": "https://api.nephoscale.com/server/type/cloud/11/", + "vcpus": 8, + "id": 11, + "billable_type": 1 + }, + { + "sku": { + "name": "CS025", + "description": "Cloud Server 0.25 GB RAM" + }, + "storage": 15, + "ram": 256, + "friendly_name": "CS025 - 0.25GB, 10GB", + "uri": "https://api.nephoscale.com/server/type/cloud/27/", + "vcpus": 1, + "id": 27, + "billable_type": 1 + }, + { + "sku": { + "name": "CS2.1", + "description": "Cloud Server 2 GB RAM, 1 Core" + }, + "storage": 75, + "ram": 2048, + "friendly_name": "CS2.1 - 2GB, 1Core, 75GB", + "uri": "https://api.nephoscale.com/server/type/cloud/46/", + "vcpus": 1, + "id": 46, + "billable_type": 1 + }, + { + "sku": { + "name": "CS4.2", + "description": "Cloud Server 4 GB RAM, 2 Cores" + }, + "storage": 150, + "ram": 4096, + "friendly_name": "CS4.2 - 4GB, 2Core, 150GB", + "uri": "https://api.nephoscale.com/server/type/cloud/48/", + "vcpus": 2, + "id": 48, + "billable_type": 1 + }, + { + "sku": { + "name": "CS8.4", + "description": "Cloud Server 8 GB RAM, 4 Cores" + }, + "storage": 300, + "ram": 8192, + "friendly_name": "CS8.4 - 8GB, 4Core, 300GB", + "uri": "https://api.nephoscale.com/server/type/cloud/50/", + "vcpus": 4, + "id": 50, + "billable_type": 1 + }, + { + "sku": { + "name": "CS16.8", + "description": "Cloud Server 16 GB RAM, 8 Cores" + }, + "storage": 600, + "ram": 16384, + "friendly_name": "CS16.8 - 16GB, 8Core, 600GB", + "uri": "https://api.nephoscale.com/server/type/cloud/52/", + "vcpus": 8, + "id": 52, + "billable_type": 1 + }, + { + "sku": { + "name": "CS32.16", + "description": "Cloud Server 32 GB RAM, 16 Cores" + }, + "storage": 1200, + "ram": 32768, + "friendly_name": "CS32.16 - 32GB, 16Core, 1200GB", + "uri": "https://api.nephoscale.com/server/type/cloud/54/", + "vcpus": 16, + "id": 54, + "billable_type": 1 + }, + { + "sku": { + "name": "CS32.8", + "description": "Cloud Server 32 GB RAM, 8 Cores" + }, + "storage": 1000, + "ram": 32768, + "friendly_name": "CS32.8 - 32GB, 8Core, 1000GB", + "uri": "https://api.nephoscale.com/server/type/cloud/56/", + "vcpus": 8, + "id": 56, + "billable_type": 1 + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json b/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json new file mode 100644 index 0000000..dc83a8f --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/list_ssh_keys.json @@ -0,0 +1,18 @@ +{ + "success": true, + "total_count": 1, + "subcode": 0, + "message": "Your request was processed successfully.", + "data": [ + { + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBs+gQwoeFNa+4pYz2AKz5Op7EqrzeP3YsyTKxx7P9gt4aSt5w8Z+lRn3p3CVG+th5i6lZqOxWgCZ1kp2KEKNbSsA2HWl3OwkY8IqHGSEeMrF+3A2Ncz88kUIAWzCswxPY4uqb/yA4EzEQDk7PJj7Q1DruObhOm7qyHT40n2KJ3TqHJQlV9XE3RcXSaQcwUt0YFXFMx8wkgy0NKqqSiQuH8RofyfnOABEzKAARGbcQjZWxh2ITzUmwMxUCBa0X5wvblgcE6/pRZN5Xq6NQr2XEU5Z48+mLy6asdasdwrM0v10Y7ojDL/TosK/8T5+d5yaRsvtBlBstDZhNWY31n5iCLxx user@mistio", + "name": "mistio-ssh", + "key_group": 1, + "uri": "https://api.nephoscale.com/key/sshrsa/72209/", + "key_type": 2, + "create_time": "2013-10-02 07:24:37", + "id": 72209 + } + ], + "response": 200 +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/fixtures/nephoscale/success_action.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/nephoscale/success_action.json b/libcloud/test/compute/fixtures/nephoscale/success_action.json new file mode 100644 index 0000000..62db155 --- /dev/null +++ b/libcloud/test/compute/fixtures/nephoscale/success_action.json @@ -0,0 +1,11 @@ +{ + "subcode": 0, + "message": "Your request was processed successfully.", + "data": { + "id": 141229, + "resource_type": "/job", + "uri": "https://api.nephoscale.com/job/141229/" + }, + "response": 202, + "success": true +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/da2fd8d8/libcloud/test/compute/test_nephoscale.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_nephoscale.py b/libcloud/test/compute/test_nephoscale.py new file mode 100644 index 0000000..7a83808 --- /dev/null +++ b/libcloud/test/compute/test_nephoscale.py @@ -0,0 +1,189 @@ +# 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. +# +#Created by Markos Gogoulos (https://mist.io) +# + +import sys +import unittest +from libcloud.utils.py3 import httplib + +from libcloud.compute.drivers.nephoscale import NephoscaleNodeDriver +from libcloud.compute.base import Node + +from libcloud.test import MockHttp +from libcloud.test.compute import TestCaseMixin +from libcloud.test.file_fixtures import ComputeFileFixtures +from libcloud.common.types import InvalidCredsError, LibcloudError + + +class NephoScaleTest(unittest.TestCase, TestCaseMixin): + def setUp(self): + NephoscaleNodeDriver.connectionCls.conn_classes = ( + None, NephoscaleMockHttp) + self.driver = NephoscaleNodeDriver('user', 'password') + + def test_list_sizes(self): + sizes = self.driver.list_sizes() + self.assertEqual(len(sizes), 13) + for size in sizes: + self.assertEqual(type(size.disk), int) + self.assertEqual(type(size.ram), int) + + def test_list_images(self): + images = self.driver.list_images() + self.assertEqual(len(images), 18) + for image in images: + arch = image.extra.get('architecture') + self.assertTrue(arch.startswith('x86')) + + def test_list_locations(self): + locations = self.driver.list_locations() + self.assertEqual(len(locations), 2) + self.assertEqual(locations[0].name, "SJC-1") + + def test_list_nodes(self): + nodes = self.driver.list_nodes() + self.assertEqual(len(nodes), 2) + self.assertEqual(nodes[0].extra.get('zone'), 'RIC-1') + self.assertEqual(nodes[0].name, 'mongodb-staging') + self.assertEqual(nodes[0].extra.get('service_type'), + 'CS05 - 0.5GB, 1Core, 25GB') + + def test_list_keys(self): + keys = self.driver.ex_list_keypairs() + self.assertEqual(len(keys), 2) + self.assertEqual(keys[0].name, 'mistio-ssh') + + def test_list_ssh_keys(self): + ssh_keys = self.driver.ex_list_keypairs(ssh=True) + self.assertEqual(len(ssh_keys), 1) + self.assertTrue(ssh_keys[0].public_key.startswith('ssh-rsa')) + + def test_list_password_keys(self): + password_keys = self.driver.ex_list_keypairs(password=True) + self.assertEqual(len(password_keys), 1) + self.assertEquals(password_keys[0].password, '23d493j5') + + def test_reboot_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.reboot_node(node) + self.assertTrue(result) + + def test_destroy_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.destroy_node(node) + self.assertTrue(result) + + def test_stop_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.ex_stop_node(node) + self.assertTrue(result) + + def test_start_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.ex_start_node(node) + self.assertTrue(result) + + def test_rename_node(self): + node = self.driver.list_nodes()[0] + result = self.driver.rename_node(node, 'new-name') + self.assertTrue(result) + + def test_create_node(self): + name = 'mongodb-staging' + size = self.driver.list_sizes()[0] + image = self.driver.list_images()[3] + node = self.driver.create_node(name=name, + size=size, + nowait=True, + image=image) + self.assertEqual(node.name, 'mongodb-staging') + + def test_create_node_no_name(self): + size = self.driver.list_sizes()[0] + image = self.driver.list_images()[3] + self.assertRaises(TypeError, self.driver.create_node, size=size, + image=image) + + def test_delete_ssh_keys(self): + key = self.driver.ex_delete_keypair(key_id=72209, ssh=True) + + def test_delete_password_keys(self): + key = self.driver.ex_delete_keypair(key_id=72211) + + +class NephoscaleMockHttp(MockHttp): + fixtures = ComputeFileFixtures('nephoscale') + + def _server_type_cloud(self, method, url, body, headers): + body = self.fixtures.load('list_sizes.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load('success_action.json') + else: + body = self.fixtures.load('list_nodes.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _image_server(self, method, url, body, headers): + body = self.fixtures.load('list_images.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _datacenter_zone(self, method, url, body, headers): + body = self.fixtures.load('list_locations.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key(self, method, url, body, headers): + body = self.fixtures.load('list_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_sshrsa(self, method, url, body, headers): + body = self.fixtures.load('list_ssh_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_password(self, method, url, body, headers): + body = self.fixtures.load('list_password_keys.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_restart(self, method, url, body, + headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_start(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _server_cloud_88241_initiator_stop(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_password_72211(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _key_sshrsa_72209(self, method, url, body, headers): + body = self.fixtures.load('success_action.json') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + +if __name__ == '__main__': + sys.exit(unittest.main())
