libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject [1/4] libcloud git commit: Added RunAbove's Compute driver with tests and docs.
Date Sat, 08 Aug 2015 19:15:12 GMT
Repository: libcloud
Updated Branches:
  refs/heads/trunk ebe71ac1e -> 2a13767fe


Added RunAbove's Compute driver with tests and docs.

Closes #550

Signed-off-by: Tomaz Muraus <tomaz@tomaz.me>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/f6038f6f
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/f6038f6f
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/f6038f6f

Branch: refs/heads/trunk
Commit: f6038f6f9f38594d7d544de5b6e6dc78720cd29d
Parents: ebe71ac
Author: ZuluPro <montheanthony@hotmail.com>
Authored: Tue Jul 7 19:45:27 2015 -0400
Committer: Tomaz Muraus <tomaz@tomaz.me>
Committed: Sat Aug 8 20:25:37 2015 +0200

----------------------------------------------------------------------
 CHANGES.rst                                     |   4 +
 docs/_static/images/provider_logos/runabove.png | Bin 0 -> 15052 bytes
 docs/compute/drivers/runabove.rst               |  72 +++++
 docs/examples/compute/runabove/create_node.py   |  12 +
 libcloud/common/runabove.py                     | 134 +++++++++
 libcloud/compute/drivers/runabove.py            | 295 +++++++++++++++++++
 libcloud/compute/providers.py                   |   2 +
 libcloud/compute/types.py                       |   1 +
 libcloud/test/common/test_runabove.py           |  29 ++
 .../fixtures/runabove/auth_time_get.json        |   1 +
 .../compute/fixtures/runabove/flavor_get.json   |   1 +
 .../fixtures/runabove/flavor_get_detail.json    |   1 +
 .../compute/fixtures/runabove/image_get.json    |   1 +
 .../fixtures/runabove/image_get_detail.json     |   1 +
 .../compute/fixtures/runabove/instance_get.json |   1 +
 .../fixtures/runabove/instance_get_detail.json  |   1 +
 .../fixtures/runabove/instance_post.json        |   1 +
 .../compute/fixtures/runabove/region_get.json   |   1 +
 .../test/compute/fixtures/runabove/ssh_get.json |   2 +
 .../fixtures/runabove/ssh_get_detail.json       |   1 +
 libcloud/test/compute/test_runabove.py          | 148 ++++++++++
 libcloud/test/secrets.py-dist                   |   1 +
 22 files changed, 710 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 5a22402..e364427 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -275,6 +275,10 @@ Compute
   (GITHUB-553)
   [Rico Echwald-Tijsen]
 
+- Add new driver for RunAbove (https://www.runabove.com) provider.
+  (GITHUB-550)
+  [ZuluPro]
+
 Storage
 ~~~~~~~
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/docs/_static/images/provider_logos/runabove.png
----------------------------------------------------------------------
diff --git a/docs/_static/images/provider_logos/runabove.png b/docs/_static/images/provider_logos/runabove.png
new file mode 100644
index 0000000..c249888
Binary files /dev/null and b/docs/_static/images/provider_logos/runabove.png differ

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/docs/compute/drivers/runabove.rst
----------------------------------------------------------------------
diff --git a/docs/compute/drivers/runabove.rst b/docs/compute/drivers/runabove.rst
new file mode 100644
index 0000000..adf1b9d
--- /dev/null
+++ b/docs/compute/drivers/runabove.rst
@@ -0,0 +1,72 @@
+Cloudwatt Compute Driver Documentation
+======================================
+
+`RunAbove`_ is a public cloud offer created by OVH Group with datacenters
+in North America and Europe.
+
+.. figure:: /_static/images/provider_logos/runabove.png
+    :align: center
+    :width: 300
+    :target: https://www.runabove.com/index.xml
+
+RunAbove driver uses the OVH/RunAbove API so for more information about
+that, please refer to `RunAbove knowledge base`_ page and `API console`_.
+
+Instantiating a driver
+----------------------
+
+When you instantiate a driver you need to pass the following arguments to the
+driver constructor:
+
+* ``user_id`` - Application key
+* ``secret`` - Application secret
+* ``ex_consumer_key`` - Consumer key
+
+For get application key and secret, you must first register an application
+at https://api.runabove.com/createApp/. Next step, create a consumer key with
+following command: ::
+
+    curl -X POST \
+        -H 'X-Ra-Application: youApplicationKey' \
+        -H 'Content-Type: application/json' \
+        -d '{
+            "accessRules":
+                [
+                    {"method":"GET","path":"/*"},
+                    {"method":"POST","path":"/*"},
+                    {"method":"DELETE","path":"/*"},
+                    {"method":"PUT","path":"/*"},
+                ],
+                "redirection":"http://runabove.com"
+            }' \
+        "https://api.runabove.com/1.0/auth/credential"
+
+This will answer a JSON like below with inside your Consumer Key and
+``validationUrl``. Follow this link for valid your key. ::
+
+    {
+      "validationUrl":"https://api.runabove.com/login/?credentialToken=fIDK6KCVHfEMuSTP3LV84D3CsHTq4T3BhOrmEEdd2hQ0CNcfVgGVWZRqIlolDJ3W",
+      "consumerKey":"y7epYeHCIqoO17BzBgxluvB4XLedpba9",
+      "state":"pendingValidation"
+    }
+
+Now you have and can use you credentials with Libcloud.
+
+Examples
+--------
+
+Create instance
+~~~~~~~~~~~~~~~
+
+.. literalinclude:: /examples/compute/runabove/create_node.py
+
+API Docs
+--------
+
+.. autoclass:: libcloud.compute.drivers.runabove.RunAboveNodeDriver
+    :members:
+    :inherited-members:
+
+.. _`Runabove`: https://www.runabove.com/index.xml
+.. _`RunAbove knowledge base`: https://community.runabove.com/kb/
+.. _`API console`: https://api.runabove.com/console/#/

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/docs/examples/compute/runabove/create_node.py
----------------------------------------------------------------------
diff --git a/docs/examples/compute/runabove/create_node.py b/docs/examples/compute/runabove/create_node.py
new file mode 100644
index 0000000..7137ff4
--- /dev/null
+++ b/docs/examples/compute/runabove/create_node.py
@@ -0,0 +1,12 @@
+from libcloud.compute.types import Provider
+from libcloud.compute.providers import get_driver
+
+RunAbove = get_driver(Provider.RUNABOVE)
+driver = RunAbove('yourAppKey', 'yourAppSecret', 'YourConsumerKey')
+
+image = [i for i in driver.list_images() if 'Debian 8' == i.name][0]
+size = [s for s in driver.list_sizes() if s.name == 'ra.s'][0]
+location = [l for l in driver.list_locations() if l.id == 'SBG-1'][0]
+
+node = driver.create_node(name='yournode', size=size, image=image,
+                          location=location)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/common/runabove.py
----------------------------------------------------------------------
diff --git a/libcloud/common/runabove.py b/libcloud/common/runabove.py
new file mode 100644
index 0000000..6345980
--- /dev/null
+++ b/libcloud/common/runabove.py
@@ -0,0 +1,134 @@
+# 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 hashlib
+import time
+try:
+    import simplejson as json
+except ImportError:
+    import json
+from libcloud.common.base import ConnectionUserAndKey, JsonResponse
+from libcloud.httplib_ssl import LibcloudHTTPSConnection
+
+API_HOST = 'api.runabove.com'
+API_ROOT = '/1.0'
+LOCATIONS = {
+    'SBG-1': {'id': 'SBG-1', 'name': 'Strasbourg 1', 'country': 'FR'},
+    'BHS-1': {'id': 'BHS-1', 'name': 'Montreal 1', 'country': 'CA'}
+}
+DEFAULT_ACCESS_RULES = [
+    {"method": "GET", "path": "/*"},
+    {"method": "POST", "path": "/*"},
+    {"method": "PUT", "path": "/*"},
+    {"method": "DELETE", "path": "/*"},
+]
+
+
+class RunAboveException(Exception):
+    pass
+
+
+class RunAboveConnection(ConnectionUserAndKey):
+    """
+    A connection to the RunAbove API
+
+    Wraps SSL connections to the RunAbove API, automagically injecting the
+    parameters that the API needs for each request.
+    """
+    host = API_HOST
+    request_path = API_ROOT
+    responseCls = JsonResponse
+    timestamp = None
+    ua = []
+    LOCATIONS = LOCATIONS
+    _timedelta = None
+
+    allow_insecure = True
+
+    def __init__(self, user_id, *args, **kwargs):
+        self.consumer_key = kwargs.pop('ex_consumer_key', None)
+        if self.consumer_key is None:
+            consumer_key_json = self.request_consumer_key(user_id)
+            msg = "Your consumer key isn't validated, " \
+                "go to '{validationUrl}' for valid it. After instantiate " \
+                "your driver with \"ex_consumer_key='{consumerKey}'\"."\
+                .format(**consumer_key_json)
+            raise RunAboveException(msg)
+        super(RunAboveConnection, self).__init__(user_id, *args, **kwargs)
+
+    def request_consumer_key(self, user_id):
+        action = self.request_path + '/auth/credential'
+        data = json.dumps({
+            "accessRules": DEFAULT_ACCESS_RULES,
+            "redirection": "http://runabove.com",
+        })
+        headers = {
+            'Content-Type': 'application/json',
+            'X-Ra-Application': user_id,
+        }
+        httpcon = LibcloudHTTPSConnection(self.host)
+        httpcon.request(method='POST', url=action, body=data, headers=headers)
+        response = httpcon.getresponse().read()
+        json_response = json.loads(response)
+        httpcon.close()
+        return json_response
+
+    def get_timestamp(self):
+        if not self._timedelta:
+            action = API_ROOT + '/auth/time'
+            response = self.connection.request('GET', action, headers={})
+            timestamp = int(response)
+            self._time_delta = timestamp - int(time.time())
+        return int(time.time()) + self._timedelta
+
+    def make_signature(self, method, action, data, timestamp):
+        full_url = 'https://%s%s' % (API_HOST, action)
+        sha1 = hashlib.sha1()
+        base_signature = "+".join([
+            self.key,
+            self.consumer_key,
+            method.upper(),
+            full_url,
+            data if data else '',
+            str(timestamp),
+        ])
+        sha1.update(base_signature.encode())
+        signature = '$1$' + sha1.hexdigest()
+        return signature
+
+    def add_default_params(self, params):
+        return params
+
+    def add_default_headers(self, headers):
+        headers.update({
+            "X-Ra-Application": self.user_id,
+            "X-Ra-Consumer": self.consumer_key,
+            "Content-type": "application/json",
+        })
+        return headers
+
+    def request(self, action, params=None, data=None, headers=None,
+                method='GET', raw=False):
+        data = json.dumps(data) if data else None
+        timestamp = self.get_timestamp()
+        signature = self.make_signature(method, action, data, timestamp)
+        headers = headers or {}
+        headers.update({
+            "X-Ra-Timestamp": timestamp,
+            "X-Ra-Signature": signature
+        })
+        return super(RunAboveConnection, self)\
+            .request(action, params=params, data=data, headers=headers,
+                     method=method, raw=raw)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/compute/drivers/runabove.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/runabove.py b/libcloud/compute/drivers/runabove.py
new file mode 100644
index 0000000..5e5252b
--- /dev/null
+++ b/libcloud/compute/drivers/runabove.py
@@ -0,0 +1,295 @@
+# 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.common.runabove import API_ROOT, RunAboveConnection
+from libcloud.compute.base import NodeDriver, NodeSize, Node, NodeLocation
+from libcloud.compute.base import NodeImage
+from libcloud.compute.types import Provider
+from libcloud.compute.drivers.openstack import OpenStackNodeDriver
+from libcloud.compute.drivers.openstack import OpenStackKeyPair
+
+
+class RunAboveNodeDriver(NodeDriver):
+    """libcloud driver for the RunAbove API
+
+    Rough mapping of which is which:
+
+        list_nodes              linode.list
+        reboot_node             linode.reboot
+        destroy_node            linode.delete
+        create_node             linode.create, linode.update,
+                                linode.disk.createfromdistribution,
+                                linode.disk.create, linode.config.create,
+                                linode.ip.addprivate, linode.boot
+        list_sizes              avail.linodeplans
+        list_images             avail.distributions
+        list_locations          avail.datacenters
+        list_volumes            linode.disk.list
+        destroy_volume          linode.disk.delete
+
+    For more information on the Linode API, be sure to read the reference:
+
+        http://www.linode.com/api/
+    """
+    type = Provider.RUNABOVE
+    name = "RunAbove"
+    website = 'https://www.runabove.com/'
+    connectionCls = RunAboveConnection
+    features = {'create_node': ['ssh_key']}
+    api_name = 'runabove'
+
+    NODE_STATE_MAP = OpenStackNodeDriver.NODE_STATE_MAP
+
+    def __init__(self, key, secret, ex_consumer_key=None):
+        """Instantiate the driver with the given API key
+
+        :param   key: the API key to use (required)
+        :type    key: ``str``
+
+        :rtype: ``None``
+        """
+        self.datacenter = None
+        self.consumer_key = ex_consumer_key
+        NodeDriver.__init__(self, key, secret, ex_consumer_key=ex_consumer_key)
+
+    def _ex_connection_class_kwargs(self):
+        return {'ex_consumer_key': self.consumer_key}
+
+    def _add_required_headers(self, headers, method, action, data, timestamp):
+        timestamp = self.connection.get_timestamp()
+        signature = self.connection.make_signature(method, action, data,
+                                                   str(timestamp))
+        headers.update({
+            "X-Ra-Timestamp": timestamp,
+            "X-Ra-Signature": signature
+        })
+
+    def list_nodes(self, location=None):
+        """
+        List all Linodes that the API key can access
+
+        This call will return all Linodes that the API key in use has access
+         to.
+        If a node is in this list, rebooting will work; however, creation and
+        destruction are a separate grant.
+
+        :return: List of node objects that the API key can access
+        :rtype: ``list`` of :class:`Node`
+        """
+        action = API_ROOT + '/instance'
+        data = {}
+        if location:
+            data['region'] = location.id
+        response = self.connection.request(action, data=data)
+        return self._to_nodes(response.object)
+
+    def ex_get_node(self, node_id):
+        action = API_ROOT + '/instance/' + node_id
+        response = self.connection.request(action, method='GET')
+        return self._to_node(response.object)
+
+    def reboot_node(self, node):
+        raise NotImplementedError(
+            "reboot_node not implemented for this driver")
+
+    def create_node(self, **kwargs):
+        action = API_ROOT + '/instance'
+        data = {
+            'name': kwargs["name"],
+            'imageId': kwargs["image"].id,
+            'flavorId': kwargs["size"].id,
+            'region': kwargs["location"].id,
+        }
+        if kwargs.get('ex_keyname'):
+            data['sshKeyName'] = kwargs['ex_keyname']
+        response = self.connection.request(action, data=data, method='POST')
+        return self._to_node(response.object)
+
+    def destroy_node(self, node):
+        action = API_ROOT + '/instance/' + node.id
+        self.connection.request(action, method='DELETE')
+        return True
+
+    def list_sizes(self, location=None):
+        """
+        List available RunAbove flavors.
+
+        :keyword location: the facility to retrieve plans in
+        :type    location: :class:`NodeLocation`
+
+        :rtype: ``list`` of :class:`NodeSize`
+        """
+        action = API_ROOT + '/flavor'
+        data = {}
+        if location:
+            data['region'] = location.id
+        response = self.connection.request(action, data=data)
+        return self._to_sizes(response.object)
+
+    def ex_get_size(self, size_id):
+        action = API_ROOT + '/flavor/' + size_id
+        response = self.connection.request(action)
+        return self._to_size(response.object)
+
+    def list_images(self, location=None, size=None):
+        """
+        List available Linux distributions
+
+        Retrieve all Linux distributions that can be deployed to a Linode.
+
+        :rtype: ``list`` of :class:`NodeImage`
+        """
+        action = API_ROOT + '/image'
+        data = {}
+        if location:
+            data['region'] = location.id
+        if size:
+            data['flavorId'] = size.id
+        response = self.connection.request(action, data=data)
+        return self._to_images(response.object)
+
+    def get_image(self, image_id):
+        action = API_ROOT + '/image/' + image_id
+        response = self.connection.request(action)
+        return self._to_image(response.object)
+
+    def list_locations(self):
+        """
+        List available facilities for deployment
+
+        Retrieve all facilities that a Linode can be deployed in.
+
+        :rtype: ``list`` of :class:`NodeLocation`
+        """
+        action = API_ROOT + '/region'
+        data = self.connection.request(action)
+        return self._to_locations(data.object)
+
+    def list_key_pairs(self, location=None):
+        action = API_ROOT + '/ssh'
+        data = {}
+        if location:
+            data['region'] = location.id
+        response = self.connection.request(action, data=data)
+        return self._to_key_pairs(response.object)
+
+    def get_key_pair(self, name, location):
+        action = API_ROOT + '/ssh/' + name
+        data = {'region': location.id}
+        response = self.connection.request(action, data=data)
+        return self._to_key_pair(response.object)
+
+    def import_key_pair_from_string(self, name, key_material, location):
+        """
+        Import a new public key.
+
+        :param name: Key pair name.
+        :type name: ``str``
+
+        :param key_material: Public key material.
+        :type key_material: ``str``
+
+        :return: Imported key pair object.
+        :rtype: :class:`.KeyPair`
+        """
+        action = API_ROOT + '/ssh'
+        data = {'name': name, 'publicKey': key_material, 'region': location.id}
+        response = self.connection.request(action, data=data, method='POST')
+        return self._to_key_pair(response.object)
+
+    def delete_key_pair(self, name, location):
+        """
+        Delete an existing key pair.
+
+        :param key_pair: Key pair object or ID.
+        :type key_pair: :class.KeyPair` or ``int``
+
+        :return:   True of False based on success of Keypair deletion
+        :rtype:    ``bool``
+        """
+        action = API_ROOT + '/ssh/' + name
+        data = {'name': name, 'region': location.id}
+        self.connection.request(action, data=data, method='DELETE')
+        return True
+
+    def create_volume(self, size, name):
+        raise NotImplementedError(
+            "create_volume not implemented for this driver")
+
+    def destroy_volume(self, volume):
+        raise NotImplementedError(
+            "destroy_volume not implemented for this driver")
+
+    def ex_list_volumes(self, node, disk_id=None):
+        raise NotImplementedError(
+            "list_volumes not implemented for this driver")
+
+    def _to_volume(self, obj):
+        pass
+
+    def _to_volumes(self, objs):
+        return [self._to_volume(obj) for obj in objs]
+
+    def _to_location(self, obj):
+        location = self.connection.LOCATIONS[obj]
+        return NodeLocation(driver=self, **location)
+
+    def _to_locations(self, objs):
+        return [self._to_location(obj) for obj in objs]
+
+    def _to_node(self, obj):
+        extra = obj.copy()
+        if 'flavorId' in extra:
+            public_ips = [obj.pop('ip')]
+        else:
+            ip = extra.pop('ipv4')
+            public_ips = [ip] if ip else []
+        del extra['instanceId']
+        del extra['name']
+        return Node(id=obj['instanceId'], name=obj['name'],
+                    state=self.NODE_STATE_MAP[obj['status']],
+                    public_ips=public_ips, private_ips=[], driver=self,
+                    extra=extra)
+
+    def _to_nodes(self, objs):
+        return [self._to_node(obj) for obj in objs]
+
+    def _to_size(self, obj):
+        extra = {'vcpus': obj['vcpus'], 'type': obj['type'],
+                 'region': obj['region']}
+        return NodeSize(id=obj['id'], name=obj['name'], ram=obj['ram'],
+                        disk=obj['disk'], bandwidth=None, price=None,
+                        driver=self, extra=extra)
+
+    def _to_sizes(self, objs):
+        return [self._to_size(obj) for obj in objs]
+
+    def _to_image(self, obj):
+        extra = {'region': obj['region'], 'visibility': obj['visibility'],
+                 'deprecated': obj['deprecated']}
+        return NodeImage(id=obj['id'], name=obj['name'], driver=self,
+                         extra=extra)
+
+    def _to_images(self, objs):
+        return [self._to_image(obj) for obj in objs]
+
+    def _to_key_pair(self, obj):
+        extra = {'region': obj['region']}
+        return OpenStackKeyPair(name=obj['name'], public_key=obj['publicKey'],
+                                driver=self, fingerprint=obj['fingerPrint'],
+                                extra=extra)
+
+    def _to_key_pairs(self, objs):
+        return [self._to_key_pair(obj) for obj in objs]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/compute/providers.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/providers.py b/libcloud/compute/providers.py
index f2df4d5..a1ede8e 100644
--- a/libcloud/compute/providers.py
+++ b/libcloud/compute/providers.py
@@ -165,6 +165,8 @@ DRIVERS = {
     ('libcloud.compute.drivers.packet', 'PacketNodeDriver'),
     Provider.ONAPP:
     ('libcloud.compute.drivers.onapp', 'OnAppNodeDriver'),
+    Provider.RUNABOVE:
+    ('libcloud.compute.drivers.runabove', 'RunAboveNodeDriver'),
 
     # Deprecated
     Provider.CLOUDSIGMA_US:

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/compute/types.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/types.py b/libcloud/compute/types.py
index 64785ef..ddb9365 100644
--- a/libcloud/compute/types.py
+++ b/libcloud/compute/types.py
@@ -134,6 +134,7 @@ class Provider(object):
     AURORACOMPUTE = 'aurora_compute'
     CLOUDWATT = 'cloudwatt'
     PACKET = 'packet'
+    RUNABOVE = 'runabove'
 
     # OpenStack based providers
     HPCLOUD = 'hpcloud'

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/common/test_runabove.py
----------------------------------------------------------------------
diff --git a/libcloud/test/common/test_runabove.py b/libcloud/test/common/test_runabove.py
new file mode 100644
index 0000000..aead6c7
--- /dev/null
+++ b/libcloud/test/common/test_runabove.py
@@ -0,0 +1,29 @@
+# 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 re
+from libcloud.test import MockHttp
+
+FORMAT_URL = re.compile(r'[./-]')
+
+
+class BaseRunAboveMockHttp(MockHttp):
+
+    def _get_method_name(self, type, use_param, qs, path):
+        return "_json"
+
+    def _json(self, method, url, body, headers):
+        meth_name = '_json%s_%s' % (FORMAT_URL.sub('_', url), method.lower())
+        return getattr(self, meth_name)(method, url, body, headers)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/auth_time_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/auth_time_get.json b/libcloud/test/compute/fixtures/runabove/auth_time_get.json
new file mode 100644
index 0000000..f84eeec
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/auth_time_get.json
@@ -0,0 +1 @@
+1437075564

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/flavor_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/flavor_get.json b/libcloud/test/compute/fixtures/runabove/flavor_get.json
new file mode 100644
index 0000000..19ed785
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/flavor_get.json
@@ -0,0 +1 @@
+[{"id":"foo-id","disk":80,"name":"ra.intel.ha.l","ram":8192,"vcpus":2,"region":"SBG-1","type":"ra.s"},{"id":"bar-id","disk":80,"name":"ra.intel.ha.l","ram":8192,"vcpus":2,"region":"SBG-1","type":"ra.s"}]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/flavor_get_detail.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/flavor_get_detail.json b/libcloud/test/compute/fixtures/runabove/flavor_get_detail.json
new file mode 100644
index 0000000..9695862
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/flavor_get_detail.json
@@ -0,0 +1 @@
+{"id":"foo-id","disk":80,"name":"ra.intel.ha.l","ram":8192,"vcpus":2,"region":"SBG-1","type":"ra.s"}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/image_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/image_get.json b/libcloud/test/compute/fixtures/runabove/image_get.json
new file mode 100644
index 0000000..aa552d9
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/image_get.json
@@ -0,0 +1 @@
+[{"id":"foo-id","name":"Debian 8","region":"SBG-1","visibility":"public","deprecated":false},{"id":"bar-id","name":"CentOs","region":"SBG-1","visibility":"public","deprecated":false}]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/image_get_detail.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/image_get_detail.json b/libcloud/test/compute/fixtures/runabove/image_get_detail.json
new file mode 100644
index 0000000..6048ed3
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/image_get_detail.json
@@ -0,0 +1 @@
+{"id":"foo-id","name":"Debian 8","region":"SBG-1","visibility":"public","deprecated":false}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/instance_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/instance_get.json b/libcloud/test/compute/fixtures/runabove/instance_get.json
new file mode 100644
index 0000000..16f609e
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/instance_get.json
@@ -0,0 +1 @@
+[{"instanceId":"foo","name":"test_vm","ip":"92.0.0.1","flavorId":"551dc104-4174-495a-af34-4aafe75f22ca","imageId":"1f3b49ad-3566-4838-93f8-b657a36b870f","keyName":"mykey","status":"ACTIVE","created":"2015-05-29T11:20:48Z","region":"SBG-1"},
{"instanceId":"bar","name":"test_vm2","ip":"92.0.0.2","flavorId":"551dc104-4174-495a-af34-4aafe75f22ca","imageId":"1f3b49ad-3566-4838-93f8-b657a36b870f","keyName":"mykey","status":"ACTIVE","created":"2015-05-29T11:40:48Z","region":"SBG-1"}]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/instance_get_detail.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/instance_get_detail.json b/libcloud/test/compute/fixtures/runabove/instance_get_detail.json
new file mode 100644
index 0000000..9f8ab5b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/instance_get_detail.json
@@ -0,0 +1 @@
+{"instanceId":"c8de03b5-16cb-4eed-aa57-cfb1800faf00","name":"testvm","ipv4":"92.0.0.1","created":"2015-05-29T11:20:48Z","status":"ACTIVE","flavor":{"id":"551dc104-4174-495a-af34-4aafe75f22ca","disk":30,"name":"ra.intel.sb.l","ram":4096,"vcpus":1,"region":"SBG-1","type":"ra.sb"},"image":{"id":"1f3b49ad-3566-4838-93f8-b657a36b870f","name":"Debian
8","region":"SBG-1","visibility":"","deprecated":false},"sshKey":{"publicKey":"ssh-dss AAAAB3NzaC1kc3MAAACBALDA8LBVhtEDgns2AFtF4hO7BckyFWUyuwCUy1Frh8UZSkrhRtfNcrbir/qcoABCDEF8k1+qdqfq6RB/wYjXAqtoVABCDEFgh5D0aWKSveYH9F/zuwyH+CGcu+fBLo/q6JAqnXziFluK9jcnu1vam3FvTD9nAsx5UYSvdTo+axlbAAAAFQCFBEBKWuCabLfUQpAoiMQSNpCsFQAAAIAVHwW6+LyDPyS/o6aV6icX0Gw8K9AG4zjj+OZWd0HxJP/ABCDEFYrY63WC2vUAbmR0hrmoa3dS1Fw6ABCDEFs41z9xzLydW8jLiopI655LrDtnABCDEFYtr8idKq3j3IvmBfynvzhmb0r101agiMABCDEFjGQbRAbL42pej5ee5gAAAIAbdNorJ2iFrczABCDEF5Vjap+gqFGUYMwaHlta26WWf+ZHQdX/Kqsc4nABCDEF4jjoj7doiBjN7aRAr9f3JEDUboTatpdOzanU9gSrryCBykz5RK016ABCDEFxdmaar/U47qEKa4TYwmYyA5UH+KmbYmJExC5
 ovXlI25oAbc1eQg== user@host","name":"mykey","fingerPrint":"75:19:bf:48:dc:31:5b:91:b2:e5:ba:3b:f0:00:ba:ar","region":"SBG-1"},"region":"SBG-1"}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/instance_post.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/instance_post.json b/libcloud/test/compute/fixtures/runabove/instance_post.json
new file mode 100644
index 0000000..15a4075
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/instance_post.json
@@ -0,0 +1 @@
+{"instanceId":"df4e55cb-90e6-4f20-a604-7084f311f498","name":"testvm","ipv4":"","created":"2015-07-16T22:35:55Z","status":"BUILD","flavor":{"id":"551dc104-4174-495a-af34-4aafe75f22ca","disk":30,"name":"ra.intel.sb.l","ram":4096,"vcpus":1,"region":"SBG-1","type":"ra.sb"},"image":{"id":"1f3b49ad-3566-4838-93f8-b657a36b870f","name":"Debian
8","region":"SBG-1","visibility":"","deprecated":false},"sshKey":{"publicKey":"ssh-dss AAAAB3NzaC1kc3MAAACBALDA8LBVhtEDgns2AFtF4hO7BckyFWUyuwCUy1Frh8UZSkrhRtfNcrbir/qcoql5psB8k1+qdqfq6RB/wYjXAqtoVNF+ydCgh5D0aWKSveYH9F/zuwyH+CGcu+fBLo/q6JAqnXziFluK9jcnu1vam3FvTD9nAsx5UYSvdTo+axlbAAAAFQCFBEBKWuCabLfUQpAoiMQSNpCsFQAAAIAVHwW6+LyDPyS/o6aV6icX0Gw8K9AG4zjj+OZWd0HxJP/Rn7ND1YrY63WC2vUAbmR0hrmoa3dS1Fw6553n+Ks41z9xzLydW8jLiopI655LrDtnMEzDpIYtr8idKq3j3IvmBfynvzhmb0r101agiMdEKsUJjGQbRAbL42pej5ee5gAAAIAbdNorJ2iFrcz8OyYi25Vjap+gqFGUYMwaHlta26WWf+ZHQdX/Kqsc4n4twkb94jjoj7doiBjN7aRAr9f3JEDUboTatpdOzanU9gSrryCBykz5RK016wwOU8fxdmaar/U47qEKa4TYwmYyA5UH+KmbYmJExC5ovXlI25oA
 bc1eQg== amonthe@amonthe","name":"mykey","fingerPrint":"75:19:bf:48:dc:31:5b:91:b2:e5:ba:3b:fa:dc:ef:a7","region":"BHS-1"},"region":"SBG-1"}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/region_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/region_get.json b/libcloud/test/compute/fixtures/runabove/region_get.json
new file mode 100644
index 0000000..9ceda7f
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/region_get.json
@@ -0,0 +1 @@
+["SBG-1","BHS-1"]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/ssh_get.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/ssh_get.json b/libcloud/test/compute/fixtures/runabove/ssh_get.json
new file mode 100644
index 0000000..47dccfc
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/ssh_get.json
@@ -0,0 +1,2 @@
+
+[{"publicKey":"ssh-dss AAAAB3NzaC1kc3MAAACBALDA8LBVhtEDgns2AFtF4hO7BckyFWUyuwCUy1Frh8UZSkrhRtfNcrbir/qcoql5psB8k1+qdqfq6RB/wYjXAqtoVNF+ABCDE5D0aWKSveYH9F/zuwyABCDEu+fBLo/q6JAqnXziFluK9jcnu1vam3FvTABCDEx5UYSvdTo+axlbAAAAFQCFBEABCDEabLfUQpAoiMQSNpCsFQAAAIAVHwW6ABCDEyS/o6aV6icX0Gw8K9AG4zjj+OZWd0HxJP/RnABCDErY63WC2vUAbmR0hrmoa3dS1Fw6553n+Ks41z9xzLydWABCDEpI655LrDtnMEzDpIYtr8idKq3j3IvmBfynvzhmb0r101agiMdEKsUJjGQbRAbL42ABCDEe5gAAAIAbdNorJ2iFrcz8OyYi25Vjap+gqFGUYMwaHlta26WWf+ZHQABCDEsc4n4twkb94jjoj7doiBjN7aRABCDEJEDUboTatpdOzanU9gSrryCBykz5RK016wwOU8fxdmaar/U47qEKa4TYwmYyA5UH+KmbYmJExC5ovXlI25oAbc1eQg==
user@host","name":"mykey","fingerPrint":"75:19:bf:48:dc:31:5b:91:b2:e5:ba:3b:f0:00:ba:ar","region":"SGB-1"}]

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/fixtures/runabove/ssh_get_detail.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/runabove/ssh_get_detail.json b/libcloud/test/compute/fixtures/runabove/ssh_get_detail.json
new file mode 100644
index 0000000..7373992
--- /dev/null
+++ b/libcloud/test/compute/fixtures/runabove/ssh_get_detail.json
@@ -0,0 +1 @@
+{"publicKey":"ssh-dss AAAAB3NzaC1kc3MAAACBALDA8LBVhtEDgns2AFtF4hO7BckyFWUyuwCUy1Frh8UZSkrhRtfNcrbir/qcoql5psB8k1+qdqfq6RB/wYjXAqtoVNF+ABCDE5D0aWKSveYH9F/zuwyABCDEu+fBLo/q6JAqnXziFluK9jcnu1vam3FvTABCDEx5UYSvdTo+axlbAAAAFQCFBEABCDEabLfUQpAoiMQSNpCsFQAAAIAVHwW6ABCDEyS/o6aV6icX0Gw8K9AG4zjj+OZWd0HxJP/RnABCDErY63WC2vUAbmR0hrmoa3dS1Fw6553n+Ks41z9xzLydWABCDEpI655LrDtnMEzDpIYtr8idKq3j3IvmBfynvzhmb0r101agiMdEKsUJjGQbRAbL42ABCDEe5gAAAIAbdNorJ2iFrcz8OyYi25Vjap+gqFGUYMwaHlta26WWf+ZHQABCDEsc4n4twkb94jjoj7doiBjN7aRABCDEJEDUboTatpdOzanU9gSrryCBykz5RK016wwOU8fxdmaar/U47qEKa4TYwmYyA5UH+KmbYmJExC5ovXlI25oAbc1eQg==
user@host","name":"mykey","fingerPrint":"75:19:bf:48:dc:31:5b:91:b2:e5:ba:3b:f0:00:ba:ar","region":"SGB-1"}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/compute/test_runabove.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_runabove.py b/libcloud/test/compute/test_runabove.py
new file mode 100644
index 0000000..54e881e
--- /dev/null
+++ b/libcloud/test/compute/test_runabove.py
@@ -0,0 +1,148 @@
+# 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 mock import patch
+
+from libcloud.utils.py3 import httplib
+
+from libcloud.compute.drivers.runabove import RunAboveNodeDriver
+
+from libcloud.test.common.test_runabove import BaseRunAboveMockHttp
+from libcloud.test.secrets import RUNABOVE_PARAMS
+from libcloud.test.file_fixtures import ComputeFileFixtures
+
+
+class RunAboveMockHttp(BaseRunAboveMockHttp):
+    """Fixtures needed for tests related to rating model"""
+    fixtures = ComputeFileFixtures('runabove')
+
+    def _json_1_0_auth_time_get(self, method, url, body, headers):
+        body = self.fixtures.load('auth_time_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_region_get(self, method, url, body, headers):
+        body = self.fixtures.load('region_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_flavor_foo_id_get(self, method, url, body, headers):
+        body = self.fixtures.load('flavor_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_flavor_get(self, method, url, body, headers):
+        body = self.fixtures.load('flavor_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_image_get(self, method, url, body, headers):
+        body = self.fixtures.load('image_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_image_foo_id_get(self, method, url, body, headers):
+        body = self.fixtures.load('image_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_ssh_get(self, method, url, body, headers):
+        body = self.fixtures.load('ssh_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_ssh_post(self, method, url, body, headers):
+        body = self.fixtures.load('ssh_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_ssh_mykey_get(self, method, url, body, headers):
+        body = self.fixtures.load('ssh_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_instance_get(self, method, url, body, headers):
+        body = self.fixtures.load('instance_get.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_instance_foo_get(self, method, url, body, headers):
+        body = self.fixtures.load('instance_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_instance_foo_delete(self, method, url, body, headers):
+        return (httplib.OK, '', {}, httplib.responses[httplib.OK])
+
+    def _json_1_0_instance_post(self, method, url, body, headers):
+        body = self.fixtures.load('instance_get_detail.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
+@patch('libcloud.common.runabove.RunAboveConnection._timedelta', 42)
+class RunAboveTests(unittest.TestCase):
+    def setUp(self):
+        RunAboveNodeDriver.connectionCls.conn_classes = (
+            RunAboveMockHttp, RunAboveMockHttp)
+        RunAboveMockHttp.type = None
+        self.driver = RunAboveNodeDriver(*RUNABOVE_PARAMS)
+
+    def test_list_locations(self):
+        images = self.driver.list_locations()
+        self.assertTrue(len(images) > 0)
+
+    def test_list_images(self):
+        images = self.driver.list_images()
+        self.assertTrue(len(images) > 0)
+
+    def test_get_image(self):
+        image = self.driver.get_image('foo-id')
+        self.assertEqual(image.id, 'foo-id')
+
+    def test_list_sizes(self):
+        sizes = self.driver.list_sizes()
+        self.assertTrue(len(sizes) > 0)
+
+    def test_get_size(self):
+        size = self.driver.ex_get_size('foo-id')
+        self.assertEqual(size.id, 'foo-id')
+
+    def test_list_key_pairs(self):
+        keys = self.driver.list_sizes()
+        self.assertTrue(len(keys) > 0)
+
+    def test_get_key_pair(self):
+        location = self.driver.list_locations()[0]
+        key = self.driver.get_key_pair('mykey', location)
+        self.assertEqual(key.name, 'mykey')
+
+    def test_import_key_pair_from_string(self):
+        location = self.driver.list_locations()[0]
+        key = self.driver.import_key_pair_from_string('mykey', 'material',
+                                                      location)
+        self.assertEqual(key.name, 'mykey')
+
+    def test_list_nodes(self):
+        nodes = self.driver.list_nodes()
+        self.assertTrue(len(nodes) > 0)
+
+    def test_get_node(self):
+        node = self.driver.ex_get_node('foo')
+        self.assertEqual(node.name, 'testvm')
+
+    def test_create_node(self):
+        location = self.driver.list_locations()[0]
+        image = self.driver.list_sizes(location)[0]
+        size = self.driver.list_sizes(location)[0]
+        node = self.driver.create_node(name='testvm', image=image, size=size,
+                                       location=location)
+        self.assertEqual(node.name, 'testvm')
+
+    def test_destroy_node(self):
+        node = self.driver.list_nodes()[0]
+        self.driver.destroy_node(node)
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

http://git-wip-us.apache.org/repos/asf/libcloud/blob/f6038f6f/libcloud/test/secrets.py-dist
----------------------------------------------------------------------
diff --git a/libcloud/test/secrets.py-dist b/libcloud/test/secrets.py-dist
index fbadf81..3ebcac3 100644
--- a/libcloud/test/secrets.py-dist
+++ b/libcloud/test/secrets.py-dist
@@ -33,6 +33,7 @@ OPENSTACK_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
 OPENNEBULA_PARAMS = ('user', 'key')
 DIMENSIONDATA_PARAMS = ('user', 'password')
 OPSOURCE_PARAMS = ('user', 'password')
+RUNABOVE_PARAMS = ('application_key', 'application_secret', 'consumer_key')
 RACKSPACE_PARAMS = ('user', 'key')
 RACKSPACE_NOVA_PARAMS = ('user_name', 'api_key', False, 'host', 8774)
 SLICEHOST_PARAMS = ('key',)


Mime
View raw message