From b98f6e9cb46d96cca5699a4c63e20eb17f1901db Mon Sep 17 00:00:00 2001 From: "aelaguiz@gmail.com" Date: Sun, 15 Jul 2012 20:43:24 -0500 Subject: [PATCH 1/2] Initial addition of support for gridspot api --- libcloud/compute/types.py | 2 + libcloud/test/compute/test_gridspot.py | 232 ++++++++++++++++++++++++++++++++ libcloud/test/secrets.py-dist | 1 + 3 files changed, 235 insertions(+), 0 deletions(-) create mode 100644 libcloud/test/compute/test_gridspot.py diff --git libcloud/compute/types.py libcloud/compute/types.py index bb20876..cc2a5ef 100644 --- libcloud/compute/types.py +++ libcloud/compute/types.py @@ -68,6 +68,7 @@ class Provider(object): @cvar JOYENT: Joyent driver @cvar VCL: VCL driver @cvar KTUCLOUD: kt ucloud driver + @cvar GRIDSPOT: Gridspot driver """ DUMMY = 0 EC2 = 1 # deprecated name @@ -120,6 +121,7 @@ class Provider(object): VCL = 46 KTUCLOUD=47 RACKSPACE_NOVA_LON = 48 + GRIDSPOT=49 class NodeState(object): diff --git libcloud/test/compute/test_gridspot.py libcloud/test/compute/test_gridspot.py new file mode 100644 index 0000000..7680362 --- /dev/null +++ libcloud/test/compute/test_gridspot.py @@ -0,0 +1,232 @@ +# 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. + +import sys +import unittest +from libcloud.utils.py3 import httplib + +try: + import simplejson as json +except ImportError: + import json + +from libcloud.common.types import InvalidCredsError +from libcloud.compute.drivers.gridspot import GridspotNodeDriver +from libcloud.compute.types import NodeState + +from libcloud.test import MockHttp +from libcloud.test.compute import TestCaseMixin +from libcloud.test.secrets import GRIDSPOT_PARAMS + +class GridspotTest(unittest.TestCase, TestCaseMixin): + + def setUp(self): + GridspotNodeDriver.connectionCls.conn_classes = ( + None, + GridspotMockHttp + ) + GridspotMockHttp.type = None + self.driver = GridspotNodeDriver(*GRIDSPOT_PARAMS) + + def test_invalid_creds(self): + """ + Tests the error-handling for passing a bad API Key to the Gridspot API + """ + GridspotMockHttp.type = 'BAD_AUTH' + try: + self.driver.list_nodes() + self.assertTrue(False) # Above command should have thrown an InvalidCredsException + except InvalidCredsError: + self.assertTrue(True) + + def test_list_nodes(self): + nodes = self.driver.list_nodes() + self.assertEqual(len(nodes), 2) + + running_node = nodes[0] + starting_node = nodes[1] + + self.assertEqual(running_node.id, 'inst_CP2WrQi2WIS4iheyAVkQYw') + self.assertEqual(running_node.state, NodeState.RUNNING) + self.assertTrue('69.4.239.74' in running_node.public_ips) + self.assertEqual(running_node.extra['port'], 62394) + self.assertEqual(running_node.extra['vm_ram'], 1429436743) + self.assertEqual(running_node.extra['start_state_time'], 1342108905) + self.assertEqual(running_node.extra['vm_num_logical_cores'], 8) + self.assertEqual(running_node.extra['vm_num_physical_cores'], 4) + self.assertEqual(running_node.extra['winning_bid_id'],\ + 'bid_X5xhotGYiGUk7_RmIqVafA') + self.assertFalse('ended_state_time' in running_node.extra) + self.assertEqual(running_node.extra['running_state_time'], 1342108989) + + self.assertEqual(starting_node.id, 'inst_CP2WrQi2WIS4iheyAVkQYw2') + self.assertEqual(starting_node.state, NodeState.PENDING) + self.assertTrue('69.4.239.74' in starting_node.public_ips) + self.assertEqual(starting_node.extra['port'], 62395) + self.assertEqual(starting_node.extra['vm_ram'], 1429436744) + self.assertEqual(starting_node.extra['start_state_time'], 1342108906) + self.assertEqual(starting_node.extra['vm_num_logical_cores'], 7) + self.assertEqual(starting_node.extra['vm_num_physical_cores'], 5) + self.assertEqual(starting_node.extra['winning_bid_id'],\ + 'bid_X5xhotGYiGUk7_RmIqVafA1') + self.assertFalse('ended_state_time' in starting_node.extra) + self.assertEqual(starting_node.extra['running_state_time'], 1342108990) + + def test_create_node(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_destroy_node(self): + """ + Test destroy_node for Gridspot driver + """ + node = self.driver.list_nodes()[0] + self.assertTrue(self.driver.destroy_node(node)) + + def test_destroy_node_failure(self): + """ + Gridspot does not fail a destroy node unless the parameters are bad, in + which case it 404s + """ + self.assertTrue(True) + + def test_reboot_node(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_reboot_node_failure(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_resize_node(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_reboot_node_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_images_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_create_node_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_destroy_node_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_sizes_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_resize_node_failure(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_images(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_sizes(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_locations(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + + def test_list_locations_response(self): + """ + Gridspot does not implement this functionality + """ + self.assertTrue(True) + +class GridspotMockHttp(MockHttp): + + def _compute_api_v1_list_instances_BAD_AUTH(self, method, url, body, headers): + return (httplib.NOT_FOUND, "", {}, httplib.responses[httplib.NOT_FOUND]) + + def _compute_api_v1_list_instances(self, method, url, body, headers): + body = json.dumps({ + "instances": [ + { + "instance_id": "inst_CP2WrQi2WIS4iheyAVkQYw", + "vm_num_logical_cores": 8, + "vm_num_physical_cores": 4, + "winning_bid_id": "bid_X5xhotGYiGUk7_RmIqVafA", + "vm_ram": 1429436743, + "start_state_time": 1342108905, + "vm_ssh_wan_ip_endpoint": "69.4.239.74:62394", + "current_state": "Running", + "ended_state_time": "null", + "running_state_time": 1342108989 + }, + { + "instance_id": "inst_CP2WrQi2WIS4iheyAVkQYw2", + "vm_num_logical_cores": 7, + "vm_num_physical_cores": 5, + "winning_bid_id": "bid_X5xhotGYiGUk7_RmIqVafA1", + "vm_ram": 1429436744, + "start_state_time": 1342108906, + "vm_ssh_wan_ip_endpoint": "69.4.239.74:62395", + "current_state": "Starting", + "ended_state_time": "null", + "running_state_time": 1342108990 + } + ], + "exception_name": "" + }) + + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + + def _compute_api_v1_stop_instance(self, method, url, body, headers): + body = json.dumps({ "exception_name": "" }) + + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + +if __name__ == '__main__': + sys.exit(unittest.main()) + + + diff --git libcloud/test/secrets.py-dist libcloud/test/secrets.py-dist index 616fe0a..a4ad23f 100644 --- libcloud/test/secrets.py-dist +++ libcloud/test/secrets.py-dist @@ -37,6 +37,7 @@ VOXEL_PARAMS = ('key', 'secret') VPSNET_PARAMS = ('user', 'key') JOYENT_PARAMS = ('user', 'key') VCL_PARAMS = ('user', 'pass', True, 'foo.bar.com') +GRIDSPOT_PARAMS = ('key',) # Storage STORAGE_S3_PARAMS = ('key', 'secret') -- 1.7.7.5 (Apple Git-26) From 84f2f3049b05078892fe50156470e8fb068506c9 Mon Sep 17 00:00:00 2001 From: "aelaguiz@gmail.com" Date: Sun, 15 Jul 2012 20:43:42 -0500 Subject: [PATCH 2/2] Adding gridspot driver to repo --- libcloud/compute/drivers/gridspot.py | 128 ++++++++++++++++++++++++++++++++++ 1 files changed, 128 insertions(+), 0 deletions(-) create mode 100644 libcloud/compute/drivers/gridspot.py diff --git libcloud/compute/drivers/gridspot.py libcloud/compute/drivers/gridspot.py new file mode 100644 index 0000000..c5a8348 --- /dev/null +++ libcloud/compute/drivers/gridspot.py @@ -0,0 +1,128 @@ +# 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 "" % (self.args[0]) + +class GridspotResponse(JsonResponse): + """ + Response class for Gridspot + """ + def parse_body(self): + body = super(GridspotResponse, self).parse_body() + + if 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): + """ + Add key and format parameters to the request. Eventually should add + unique_id to prevent re-execution of a single request. + """ + 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 + + 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 = self.connection.request( + '/compute_api/v1/stop_instance', {'instance_id': node.id}).object + return True + + def _nodestate(self, state): + if state == 'Running': + return NodeState.RUNNING + elif state == 'Starting': + return NodeState.PENDING + else: + return NodeState.UNKNOWN + + 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._nodestate(data['current_state']) + + if "null" != data['vm_ssh_wan_ip_endpoint']: + 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) -- 1.7.7.5 (Apple Git-26)