You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2015/06/14 12:52:20 UTC
[09/21] libcloud git commit: DO NodeDriver uses DO BaseDriver -
Support for v1 and v2 initialization with authentication secret - Updated v1
DigitalOceanNodeDriver to use updated KeyPair Management - Updated tests to
reflect KeyPair Management changes
DO NodeDriver uses DO BaseDriver - Support for v1 and v2 initialization with authentication secret - Updated v1 DigitalOceanNodeDriver to use updated KeyPair Management - Updated tests to reflect KeyPair Management changes
Signed-off-by: Tomaz Muraus <to...@tomaz.me>
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/9224e8e1
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/9224e8e1
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/9224e8e1
Branch: refs/heads/trunk
Commit: 9224e8e180133f4af499a77366d3ca2d640afeda
Parents: d1a8e22
Author: Javier Castillo II <j....@gmail.com>
Authored: Sun Apr 12 18:02:07 2015 +0000
Committer: Tomaz Muraus <to...@tomaz.me>
Committed: Sun Jun 14 18:05:58 2015 +0800
----------------------------------------------------------------------
libcloud/compute/drivers/digitalocean.py | 172 +++++++++------------
libcloud/test/compute/test_digitalocean_v1.py | 14 +-
2 files changed, 79 insertions(+), 107 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/9224e8e1/libcloud/compute/drivers/digitalocean.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/digitalocean.py b/libcloud/compute/drivers/digitalocean.py
index 32dd199..1cb209b 100644
--- a/libcloud/compute/drivers/digitalocean.py
+++ b/libcloud/compute/drivers/digitalocean.py
@@ -20,6 +20,8 @@ from libcloud.utils.py3 import httplib
from libcloud.common.base import ConnectionUserAndKey, ConnectionKey
from libcloud.common.base import JsonResponse
+from libcloud.common.digitalocean import DigitalOcean_v1_BaseDriver
+from libcloud.common.digitalocean import DigitalOcean_v2_BaseDriver
from libcloud.compute.types import Provider, NodeState, InvalidCredsError
from libcloud.compute.base import NodeDriver, Node
from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, KeyPair
@@ -27,7 +29,7 @@ from libcloud.compute.base import NodeImage, NodeSize, NodeLocation, KeyPair
__all__ = [
'DigitalOceanNodeDriver',
'DigitalOcean_v1_NodeDriver',
- 'DigitalOcean_v1_NodeDriver'
+ 'DigitalOcean_v2_NodeDriver'
]
@@ -35,6 +37,15 @@ class DigitalOceanNodeDriver(NodeDriver):
"""
DigitalOcean NodeDriver defaulting to using APIv2.
+ :keyword key: Required for authentication. Used in both ``v1`` and
+ ``v2`` implementations.
+ :type key: ``str``
+
+ :keyword secret: Used in driver authentication with key. Defaults to
+ None and when set, will cause driver to use ``v1`` for
+ connection and response. (optional)
+ :type secret: ``str``
+
:keyword api_version: Specifies the API version to use. ``v1`` and
``v2`` are the only valid options. Defaults to
using ``v2`` (optional)
@@ -46,7 +57,7 @@ class DigitalOceanNodeDriver(NodeDriver):
def __new__(cls, key, secret=None, api_version='v2', **kwargs):
if cls is DigitalOceanNodeDriver:
- if api_version == 'v1':
+ if api_version == 'v1' or secret != None:
cls = DigitalOcean_v1_NodeDriver
elif api_version == 'v2':
cls = DigitalOcean_v2_NodeDriver
@@ -55,45 +66,7 @@ class DigitalOceanNodeDriver(NodeDriver):
(api_version))
return super(DigitalOceanNodeDriver, cls).__new__(cls, **kwargs)
-
-class DigitalOcean_v1_Response(JsonResponse):
- def parse_error(self):
- if self.status == httplib.FOUND and '/api/error' in self.body:
- # Hacky, but DigitalOcean error responses are awful
- raise InvalidCredsError(self.body)
- elif self.status == httplib.UNAUTHORIZED:
- body = self.parse_body()
- raise InvalidCredsError(body['message'])
- else:
- body = self.parse_body()
-
- if 'error_message' in body:
- error = '%s (code: %s)' % (body['error_message'], self.status)
- else:
- error = body
- return error
-
-
-class DigitalOcean_v2_Response(JsonResponse):
- valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
- httplib.NO_CONTENT]
-
- def parse_error(self):
- if self.status == httplib.UNAUTHORIZED:
- body = self.parse_body()
- raise InvalidCredsError(body['message'])
- else:
- body = self.parse_body()
- if 'message' in body:
- error = '%s (code: %s)' % (body['message'], self.status)
- else:
- error = body
- return error
-
- def success(self):
- return self.status in self.valid_response_codes
-
-
+# TODO Implement v1 driver using KeyPair
class SSHKey(object):
def __init__(self, id, name, pub_key):
self.id = id
@@ -105,52 +78,11 @@ class SSHKey(object):
(self.id, self.name, self.pub_key))
-class DigitalOcean_v1_Connection(ConnectionUserAndKey):
- """
- Connection class for the DigitalOcean (v1) driver.
- """
-
- host = 'api.digitalocean.com'
- responseCls = DigitalOcean_v1_Response
-
- def add_default_params(self, params):
- """
- Add parameters that are necessary for every request
-
- This method adds ``client_id`` and ``api_key`` to
- the request.
- """
- params['client_id'] = self.user_id
- params['api_key'] = self.key
- return params
-
-
-class DigitalOcean_v2_Connection(ConnectionKey):
- """
- Connection class for the DigitalOcean (v2) driver.
- """
-
- host = 'api.digitalocean.com'
- responseCls = DigitalOcean_v2_Response
-
- def add_default_headers(self, headers):
- """
- Add headers that are necessary for every request
-
- This method adds ``token`` to the request.
- """
- headers['Authorization'] = 'Bearer %s' % (self.key)
- headers['Content-Type'] = 'application/json'
- return headers
-
-
-class DigitalOcean_v1_NodeDriver(DigitalOceanNodeDriver):
+class DigitalOcean_v1_NodeDriver(DigitalOcean_v1_BaseDriver, DigitalOceanNodeDriver):
"""
DigitalOcean NodeDriver using v1 of the API.
"""
- connectionCls = DigitalOcean_v1_Connection
-
NODE_STATE_MAP = {'new': NodeState.PENDING,
'off': NodeState.REBOOTING,
'active': NodeState.RUNNING}
@@ -215,17 +147,33 @@ class DigitalOcean_v1_NodeDriver(DigitalOceanNodeDriver):
params=params)
return res.status == httplib.OK
- def ex_list_ssh_keys(self):
+ def list_key_pairs(self):
"""
List all the available SSH keys.
:return: Available SSH keys.
- :rtype: ``list`` of :class:`SSHKey`
+ :rtype: ``list`` of :class:`KeyPair`
"""
data = self.connection.request('/v1/ssh_keys').object['ssh_keys']
- return list(map(self._to_ssh_key, data))
+ return list(map(self._to_key_pair, data))
+
+ def get_key_pair(self, name):
+ """
+ Retrieve a single key pair.
+
+ :param name: Name of the key pair to retrieve.
+ :type name: ``str``
- def ex_create_ssh_key(self, name, ssh_key_pub):
+ :rtype: :class:`.KeyPair`
+ """
+ qkey = [k for k in self.list_key_pairs() if k.name == name][0]
+ data = self.connection.request('/v1/ssh_keys/%s' %
+ qkey.extra['id']).object['ssh_key']
+ return self._to_key_pair(data=data)
+
+ #TODO: This adds the ssh_key_pub parameter. This puts the burden of making
+ # it within the function or on the API. The KeyPair API needs work.
+ def create_key_pair(self, name, ssh_key_pub):
"""
Create a new SSH key.
@@ -239,16 +187,20 @@ class DigitalOcean_v1_NodeDriver(DigitalOceanNodeDriver):
data = self.connection.request('/v1/ssh_keys/new/', method='GET',
params=params).object
assert 'ssh_key' in data
- return self._to_ssh_key(data=data['ssh_key'])
+ #TODO: libcloud.compute.base.KeyPair.create_key_pair doesn't specify
+ # a return value. This looks like it should return a KeyPair
+ return self._to_key_pair(data=data['ssh_key'])
- def ex_destroy_ssh_key(self, key_id):
+ def delete_key_pair(self, key_pair):
"""
- Delete an existing SSH key.
+ Delete an existing key pair.
- :param key_id: SSH key id (required)
- :type key_id: ``str``
+ :param key_pair: Key pair object.
+ :type key_pair: :class:`.KeyPair`
"""
- res = self.connection.request('/v1/ssh_keys/%s/destroy/' % (key_id))
+ res = self.connection.request('/v1/ssh_keys/%s/destroy/' %
+ key_pair.extra['id'])
+ #TODO: This looks like it should return bool like the other delete_*
return res.status == httplib.OK
def _to_node(self, data):
@@ -293,18 +245,24 @@ class DigitalOcean_v1_NodeDriver(DigitalOceanNodeDriver):
return NodeSize(id=data['id'], name=data['name'], ram=ram, disk=0,
bandwidth=0, price=0, driver=self)
+ def _to_key_pair(self, data):
+ try:
+ pubkey = data['ssh_pub_key']
+ except KeyError:
+ pubkey = None
+ return KeyPair(data['name'], public_key=pubkey, fingerprint=None,
+ driver=self, private_key=None, extra={'id':data['id']})
+
def _to_ssh_key(self, data):
return SSHKey(id=data['id'], name=data['name'],
pub_key=data.get('ssh_pub_key', None))
-class DigitalOcean_v2_NodeDriver(DigitalOceanNodeDriver):
+class DigitalOcean_v2_NodeDriver(DigitalOcean_v2_BaseDriver, DigitalOceanNodeDriver):
"""
DigitalOcean NodeDriver using v2 of the API.
"""
- connectionCls = DigitalOcean_v2_Connection
-
NODE_STATE_MAP = {'new': NodeState.PENDING,
'off': NodeState.STOPPED,
'active': NodeState.RUNNING,
@@ -440,8 +398,22 @@ class DigitalOcean_v2_NodeDriver(DigitalOceanNodeDriver):
:return: Available SSH keys.
:rtype: ``list`` of :class:`KeyPair`
"""
- data = self.connection.request('/v2/account/keys').object['ssh_keys']
- return list(map(self._to_key_pairs, data))
+ data = self._paginated_request('/v2/account/keys', 'ssh_keys')
+ return list(map(self._to_key_pair, data))
+
+ def get_key_pair(self, name):
+ """
+ Retrieve a single key pair.
+
+ :param name: Name of the key pair to retrieve.
+ :type name: ``str``
+
+ :rtype: :class:`.KeyPair`
+ """
+ qkey = [k for k in self.list_key_pairs() if k.name == name][0]
+ data = self.connection.request('/v2/account/keys/%s' %
+ qkey.extra['id']).object['ssh_key']
+ return self._to_key_pair(data=data)
def create_key_pair(self, name, public_key):
"""
@@ -456,7 +428,7 @@ class DigitalOcean_v2_NodeDriver(DigitalOceanNodeDriver):
params = {'name': name, 'public_key': public_key}
data = self.connection.request('/v2/account/keys', method='POST',
params=params).object['ssh_key']
- return self._to_key_pairs(data=data)
+ return self._to_key_pair(data=data)
def delete_key_pair(self, key):
"""
@@ -543,7 +515,7 @@ class DigitalOcean_v2_NodeDriver(DigitalOceanNodeDriver):
disk=data['disk'], bandwidth=data['transfer'],
price=data['price_hourly'], driver=self, extra=extra)
- def _to_key_pairs(self, data):
+ def _to_key_pair(self, data):
extra = {'id': data['id']}
return KeyPair(name=data['name'],
fingerprint=data['fingerprint'],
http://git-wip-us.apache.org/repos/asf/libcloud/blob/9224e8e1/libcloud/test/compute/test_digitalocean_v1.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_digitalocean_v1.py b/libcloud/test/compute/test_digitalocean_v1.py
index 030e2ee..c8ff999 100644
--- a/libcloud/test/compute/test_digitalocean_v1.py
+++ b/libcloud/test/compute/test_digitalocean_v1.py
@@ -110,17 +110,17 @@ class DigitalOcean_v1_Tests(LibcloudTestCase):
result = self.driver.ex_rename_node(node, 'fedora helios')
self.assertTrue(result)
- def test_ex_list_ssh_keys(self):
- keys = self.driver.ex_list_ssh_keys()
+ def test_list_key_pairs(self):
+ keys = self.driver.list_key_pairs()
self.assertEqual(len(keys), 1)
- self.assertEqual(keys[0].id, 7717)
+ self.assertEqual(keys[0].extra['id'], 7717)
self.assertEqual(keys[0].name, 'test1')
- self.assertEqual(keys[0].pub_key, None)
+ self.assertEqual(keys[0].public_key, None)
- def test_ex_destroy_ssh_key(self):
- key = self.driver.ex_list_ssh_keys()[0]
- result = self.driver.ex_destroy_ssh_key(key.id)
+ def test_delete_key_pair(self):
+ key = self.driver.list_key_pairs()[0]
+ result = self.driver.delete_key_pair(key)
self.assertTrue(result)