Author: tomaz
Date: Mon Jul 16 05:25:58 2012
New Revision: 1361909

URL: http://svn.apache.org/viewvc?rev=1361909&view=rev
Log:
Add Gridspot compute driver. Contributed by Amir Elaguizy, part of LIBCLOUD-223.

Added:
    libcloud/trunk/libcloud/compute/drivers/gridspot.py
    libcloud/trunk/libcloud/test/compute/test_gridspot.py
Modified:
    libcloud/trunk/CHANGES
    libcloud/trunk/libcloud/compute/types.py
    libcloud/trunk/libcloud/test/secrets.py-dist

Modified: libcloud/trunk/CHANGES
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/CHANGES?rev=1361909&r1=1361908&r2=1361909&view=diff
==============================================================================
--- libcloud/trunk/CHANGES (original)
+++ libcloud/trunk/CHANGES Mon Jul 16 05:25:58 2012
@@ -73,10 +73,14 @@ Changes with Apache Libcloud in developm
     - Add 'auth_user_variable' to the  OpenStackAuthConnection class.
       [Mark Everett]
 
-    - Fix a bug with repeated URLs in some requests the vCloud driver. 
-      ; LIBCLOUD-22
+    - Fix a bug with repeated URLs in some requests the vCloud driver.
+      ; LIBCLOUD-222
       [Michal Galet]
 
+    - New Gridspot driver with basic list and destroy functionality. ;
+      LIBCLOUD-223
+      [Amir Elaguizy]
+
   *) DNS
 
     - Add support for GEO RecordType to Zerigo driver. ; LIBCLOUD-203

Added: libcloud/trunk/libcloud/compute/drivers/gridspot.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/gridspot.py?rev=1361909&view=auto
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/gridspot.py (added)
+++ libcloud/trunk/libcloud/compute/drivers/gridspot.py Mon Jul 16 05:25:58 2012
@@ -0,0 +1,127 @@
+# 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)

Modified: libcloud/trunk/libcloud/compute/types.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/types.py?rev=1361909&r1=1361908&r2=1361909&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/types.py (original)
+++ libcloud/trunk/libcloud/compute/types.py Mon Jul 16 05:25:58 2012
@@ -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
@@ -118,8 +119,9 @@ class Provider(object):
     ELASTICHOSTS_CA1 = 44
     JOYENT = 45
     VCL = 46
-    KTUCLOUD=47
+    KTUCLOUD = 47
     RACKSPACE_NOVA_LON = 48
+    GRIDSPOT = 49
 
 
 class NodeState(object):

Added: libcloud/trunk/libcloud/test/compute/test_gridspot.py
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_gridspot.py?rev=1361909&view=auto
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_gridspot.py (added)
+++ libcloud/trunk/libcloud/test/compute/test_gridspot.py Mon Jul 16 05:25:58 
2012
@@ -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()
+             # Above command should have thrown an InvalidCredsException
+            self.assertTrue(False)
+        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())

Modified: libcloud/trunk/libcloud/test/secrets.py-dist
URL: 
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/secrets.py-dist?rev=1361909&r1=1361908&r2=1361909&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/secrets.py-dist (original)
+++ libcloud/trunk/libcloud/test/secrets.py-dist Mon Jul 16 05:25:58 2012
@@ -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')


Reply via email to