Update existing compute drivers (OpenStack, CloudStack, EC2) which expose key pair management through the extension methods to expose it through a new standard key pair management API.
Also update affected code and tests, deprecate existing extension methods and add some missing tests. Note: Old and now deprecated methods will work until a next major release. Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/b398aebd Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/b398aebd Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/b398aebd Branch: refs/heads/trunk Commit: b398aebdbb50fbba06b46b466e95904c96c1ad99 Parents: eed9777 Author: Tomaz Muraus <[email protected]> Authored: Fri Dec 6 15:36:09 2013 +0100 Committer: Tomaz Muraus <[email protected]> Committed: Sun Dec 8 13:45:42 2013 +0100 ---------------------------------------------------------------------- libcloud/compute/drivers/cloudstack.py | 290 +++++++++++++++---- libcloud/compute/drivers/ec2.py | 196 +++++++++---- libcloud/compute/drivers/openstack.py | 96 ++++-- .../cloudstack/createSSHKeyPair_default.json | 1 + .../compute/fixtures/ec2/create_key_pair.xml | 22 ++ libcloud/test/compute/test_cloudstack.py | 72 +++-- libcloud/test/compute/test_ec2.py | 57 +++- libcloud/test/compute/test_openstack.py | 26 +- 8 files changed, 567 insertions(+), 193 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/compute/drivers/cloudstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index 27498fd..e9742fa 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -15,16 +15,17 @@ from __future__ import with_statement -import os import base64 +import warnings from libcloud.utils.py3 import b from libcloud.utils.py3 import urlparse from libcloud.compute.providers import Provider from libcloud.common.cloudstack import CloudStackDriverMixIn -from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation,\ - NodeSize, StorageVolume +from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation +from libcloud.compute.base import NodeSize, StorageVolume +from libcloud.compute.base import KeyPair from libcloud.compute.types import NodeState, LibcloudError from libcloud.utils.networking import is_private_subnet @@ -649,6 +650,147 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): driver=self)) return list_volumes + def list_key_pairs(self, **kwargs): + """ + List registered key pairs. + + :param projectid: list objects by project + :type projectid: ``str`` + + :param page: The page to list the keypairs from + :type page: ``int`` + + :param keyword: List by keyword + :type keyword: ``str`` + + :param listall: If set to false, list only resources + belonging to the command's caller; + if set to true - list resources that + the caller is authorized to see. + Default value is false + + :type listall: ``bool`` + + :param pagesize: The number of results per page + :type pagesize: ``int`` + + :param account: List resources by account. + Must be used with the domainId parameter + :type account: ``str`` + + :param isrecursive: Defaults to false, but if true, + lists all resources from + the parent specified by the + domainId till leaves. + :type isrecursive: ``bool`` + + :param fingerprint: A public key fingerprint to look for + :type fingerprint: ``str`` + + :param name: A key pair name to look for + :type name: ``str`` + + :param domainid: List only resources belonging to + the domain specified + :type domainid: ``str`` + + :return: A list of key par objects. + :rtype: ``list`` of :class:`libcloud.compute.base.KeyPair` + """ + extra_args = kwargs.copy() + res = self._sync_request(command='listSSHKeyPairs', + params=extra_args, + method='GET') + key_pairs = res.get('sshkeypair', []) + key_pairs = self._to_key_pairs(data=key_pairs) + return key_pairs + + def create_key_pair(self, name, **kwargs): + """ + Create a new key pair object. + + :param name: Key pair name. + :type name: ``str`` + + :param name: Name of the keypair (required) + :type name: ``str`` + + :param projectid: An optional project for the ssh key + :type projectid: ``str`` + + :param domainid: An optional domainId for the ssh key. + If the account parameter is used, + domainId must also be used. + :type domainid: ``str`` + + :param account: An optional account for the ssh key. + Must be used with domainId. + :type account: ``str`` + + :return: Created key pair object. + :rtype: :class:`libcloud.compute.base.KeyPair` + """ + extra_args = kwargs.copy() + + params = {'name': name} + params.update(extra_args) + + res = self._sync_request(command='createSSHKeyPair', + params=params, + method='GET') + key_pair = self._to_key_pair(data=res['keypair']) + return key_pair + + def import_key_pair_from_string(self, name, key_material): + """ + Import a new public key from string. + + :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:`libcloud.compute.base.KeyPair` + """ + res = self._sync_request(command='registerSSHKeyPair', + params={'name': name, + 'publickey': key_material}, + method='GET') + key_pair = self._to_key_pair(data=res['keypair']) + return key_pair + + def delete_key_pair(self, key_pair, **kwargs): + """ + Delete an existing key pair. + + :param key_pair: Key pair object. + :type key_pair: :class`libcloud.compute.base.KeyPair` + + :param projectid: The project associated with keypair + :type projectid: ``str`` + + :param domainid : The domain ID associated with the keypair + :type domainid: ``str`` + + :param account : The account associated with the keypair. + Must be used with the domainId parameter. + :type account: ``str`` + + :return: True of False based on success of Keypair deletion + :rtype: ``bool`` + """ + + extra_args = kwargs.copy() + params = {'name': key_pair.name} + params.update(extra_args) + + res = self._sync_request(command='deleteSSHKeyPair', + params=params, + method='GET') + return res['success'] == 'true' + def ex_list_public_ips(self): """ Lists all Public IP Addresses. @@ -914,13 +1056,22 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :return: A list of keypair dictionaries :rtype: ``list`` of ``dict`` """ + warnings.warn('This method has been deprecated in favor of ' + 'list_key_pairs method') - extra_args = kwargs.copy() - res = self._sync_request(command='listSSHKeyPairs', - params=extra_args, - method='GET') - keypairs = res.get('sshkeypair', []) - return keypairs + key_pairs = self.list_key_pairs(**kwargs) + + result = [] + + for key_pair in key_pairs: + item = { + 'name': key_pair.name, + 'fingerprint': key_pair.fingerprint, + 'privateKey': key_pair.private_key + } + result.append(item) + + return result def ex_create_keypair(self, name, **kwargs): """ @@ -944,50 +1095,18 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :return: A keypair dictionary :rtype: ``dict`` """ - extra_args = kwargs.copy() - - for keypair in self.ex_list_keypairs(): - if keypair['name'] == name: - raise LibcloudError('SSH KeyPair with name=%s already exists' - % (name)) - - params = {'name': name} - params.update(extra_args) - - res = self._sync_request(command='createSSHKeyPair', - params=params, - method='GET') - return res['keypair'] - - def ex_delete_keypair(self, keypair, **kwargs): - """ - Deletes an existing SSH KeyPair - - :param keypair: Name of the keypair (required) - :type keypair: ``str`` - - :param projectid: The project associated with keypair - :type projectid: ``str`` - - :param domainid : The domain ID associated with the keypair - :type domainid: ``str`` + warnings.warn('This method has been deprecated in favor of ' + 'create_key_pair method') - :param account : The account associated with the keypair. - Must be used with the domainId parameter. - :type account: ``str`` - - :return: True of False based on success of Keypair deletion - :rtype: ``bool`` - """ + key_pair = self.create_key_pair(name=name, **kwargs) - extra_args = kwargs.copy() - params = {'name': keypair} - params.update(extra_args) + result = { + 'name': key_pair.name, + 'fingerprint': key_pair.fingerprint, + 'privateKey': key_pair.private_key + } - res = self._sync_request(command='deleteSSHKeyPair', - params=params, - method='GET') - return res['success'] + return result def ex_import_keypair_from_string(self, name, key_material): """ @@ -1001,15 +1120,18 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :rtype: ``dict`` """ - res = self._sync_request(command='registerSSHKeyPair', - params={'name': name, - 'publickey': key_material}, - method='GET') - return { - 'keyName': res['keypair']['name'], - 'keyFingerprint': res['keypair']['fingerprint'] + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_string method') + + key_pair = self.import_key_pair_from_string(name=name, + key_material=key_material) + result = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint } + return result + def ex_import_keypair(self, name, keyfile): """ Imports a new public key where the public key is passed via a filename @@ -1022,9 +1144,45 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :rtype: ``dict`` """ - with open(os.path.expanduser(keyfile)) as fh: - content = fh.read() - return self.ex_import_keypair_from_string(name, content) + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_file method') + + key_pair = self.import_key_pair_from_file(name=name, + key_file_path=keyfile) + result = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint + } + + return result + + def ex_delete_keypair(self, keypair, **kwargs): + """ + Deletes an existing SSH KeyPair + + :param keypair: Name of the keypair (required) + :type keypair: ``str`` + + :param projectid: The project associated with keypair + :type projectid: ``str`` + + :param domainid : The domain ID associated with the keypair + :type domainid: ``str`` + + :param account : The account associated with the keypair. + Must be used with the domainId parameter. + :type account: ``str`` + + :return: True of False based on success of Keypair deletion + :rtype: ``bool`` + """ + warnings.warn('This method has been deprecated in favor of ' + 'delete_key_pair method') + + key_pair = KeyPair(name=keypair, public_key=None, fingerprint=None, + driver=self) + + return self.delete_key_pair(key_pair=key_pair) def ex_list_security_groups(self, **kwargs): """ @@ -1313,3 +1471,15 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): public_ips=public_ips, private_ips=private_ips, driver=self, extra=extra) return node + + def _to_key_pairs(self, data): + key_pairs = [self._to_key_pair(data=item) for item in data] + return key_pairs + + def _to_key_pair(self, data): + key_pair = KeyPair(name=data['name'], + fingerprint=data['fingerprint'], + public_key=data.get('publicKey', None), + private_key=data.get('privateKey', None), + driver=self) + return key_pair http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/compute/drivers/ec2.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index c79d6fe..3db3c35 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -17,12 +17,10 @@ Amazon EC2, Eucalyptus and Nimbus drivers. """ -from __future__ import with_statement - import sys import base64 -import os import copy +import warnings from xml.etree import ElementTree as ET @@ -39,6 +37,7 @@ from libcloud.compute.providers import Provider from libcloud.compute.types import NodeState from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize from libcloud.compute.base import NodeImage, StorageVolume, VolumeSnapshot +from libcloud.compute.base import KeyPair API_VERSION = '2010-08-31' NAMESPACE = 'http://ec2.amazonaws.com/doc/%s/' % (API_VERSION) @@ -912,6 +911,71 @@ class BaseEC2NodeDriver(NodeDriver): response = self.connection.request(self.path, params=params).object return self._get_boolean(response) + def _to_key_pairs(self, elems): + key_pairs = [self._to_key_pair(elem=elem) for elem in elems] + return key_pairs + + def _to_key_pair(self, elem): + name = findtext(element=elem, xpath='keyName', namespace=NAMESPACE) + fingerprint = findtext(element=elem, xpath='keyFingerprint', + namespace=NAMESPACE).strip() + private_key = findtext(element=elem, xpath='keyMaterial', + namespace=NAMESPACE) + + key_pair = KeyPair(name=name, + public_key=None, + fingerprint=fingerprint, + private_key=private_key, + driver=self) + return key_pair + + def list_key_pairs(self): + params = { + 'Action': 'DescribeKeyPairs' + } + + response = self.connection.request(self.path, params=params) + elems = findall(element=response.object, xpath='keySet/item', + namespace=NAMESPACE) + + key_pairs = self._to_key_pairs(elems=elems) + return key_pairs + + def create_key_pair(self, name): + params = { + 'Action': 'CreateKeyPair', + 'KeyName': name + } + + response = self.connection.request(self.path, params=params) + elem = response.object + key_pair = self._to_key_pair(elem=elem) + return key_pair + + def import_key_pair_from_string(self, name, key_material): + base64key = base64.b64encode(b(key_material)) + + params = { + 'Action': 'ImportKeyPair', + 'KeyName': name, + 'PublicKeyMaterial': base64key + } + + response = self.connection.request(self.path, params=params) + elem = response.object + key_pair = self._to_key_pair(elem=elem) + return key_pair + + def delete_key_pair(self, key_pair): + params = { + 'Action': 'DeleteKeyPair', + 'KeyName': key_pair + } + result = self.connection.request(self.path, params=params).object + element = findtext(element=result, xpath='return', + namespace=NAMESPACE) + return element == 'true' + def ex_destroy_image(self, image): params = { 'Action': 'DeregisterImage', @@ -921,7 +985,8 @@ class BaseEC2NodeDriver(NodeDriver): return self._get_boolean(response) def ex_create_keypair(self, name): - """Creates a new keypair + """ + Creates a new keypair @note: This is a non-standard extension API, and only works for EC2. @@ -931,20 +996,18 @@ class BaseEC2NodeDriver(NodeDriver): :rtype: ``dict`` """ - params = { - 'Action': 'CreateKeyPair', - 'KeyName': name, - } - response = self.connection.request(self.path, params=params).object - key_material = findtext(element=response, xpath='keyMaterial', - namespace=NAMESPACE) - key_fingerprint = findtext(element=response, xpath='keyFingerprint', - namespace=NAMESPACE) - return { - 'keyMaterial': key_material, - 'keyFingerprint': key_fingerprint, + warnings.warn('This method has been deprecated in favor of ' + 'create_key_pair method') + + key_pair = self.create_key_pair(name=name) + + result = { + 'keyMaterial': key_pair.private_key, + 'keyFingerprint': key_pair.fingerprint } + return result + def ex_delete_keypair(self, keypair): """ Delete a key pair by name. @@ -956,14 +1019,10 @@ class BaseEC2NodeDriver(NodeDriver): :rtype: ``bool`` """ - params = { - 'Action': 'DeleteKeyPair', - 'KeyName': keypair - } - result = self.connection.request(self.path, params=params).object - element = findtext(element=result, xpath='return', - namespace=NAMESPACE) - return element == 'true' + warnings.warn('This method has been deprecated in favor of ' + 'delete_key_pair method') + + return self.delete_key_pair(name=keypair) def ex_import_keypair_from_string(self, name, key_material): """ @@ -980,23 +1039,17 @@ class BaseEC2NodeDriver(NodeDriver): :rtype: ``dict`` """ - base64key = base64.b64encode(b(key_material)) + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_string method') - params = { - 'Action': 'ImportKeyPair', - 'KeyName': name, - 'PublicKeyMaterial': base64key - } + key_pair = self.import_key_pair_from_string(name=name, + key_material=key_material) - response = self.connection.request(self.path, params=params).object - key_name = findtext(element=response, xpath='keyName', - namespace=NAMESPACE) - key_fingerprint = findtext(element=response, xpath='keyFingerprint', - namespace=NAMESPACE) - return { - 'keyName': key_name, - 'keyFingerprint': key_fingerprint, + result = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint } + return result def ex_import_keypair(self, name, keyfile): """ @@ -1013,9 +1066,17 @@ class BaseEC2NodeDriver(NodeDriver): :rtype: ``dict`` """ - with open(os.path.expanduser(keyfile)) as fh: - content = fh.read() - return self.ex_import_keypair_from_string(name, content) + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_file method') + + key_pair = self.import_key_pair_from_file(name=name, + key_file_path=keyfile) + + result = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint + } + return result def ex_find_or_import_keypair_by_key_material(self, pubkey): """ @@ -1023,16 +1084,27 @@ class BaseEC2NodeDriver(NodeDriver): exists, return any information we have about it. Otherwise, create it. Keys that are created are named based on their comment and fingerprint. + + :rtype: ``dict`` """ key_fingerprint = get_pubkey_ssh2_fingerprint(pubkey) key_comment = get_pubkey_comment(pubkey, default='unnamed') - key_name = "%s-%s" % (key_comment, key_fingerprint) + key_name = '%s-%s' % (key_comment, key_fingerprint) - for keypair in self.ex_list_keypairs(): - if keypair['keyFingerprint'] == key_fingerprint: - return keypair + key_pairs = self.list_key_pairs() + key_pairs = [key_pair for key_pair in key_pairs if + key_pair.fingerprint == key_fingerprint] - return self.ex_import_keypair_from_string(key_name, pubkey) + if len(key_pairs) >= 1: + key_pair = key_pairs[0] + result = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint + } + else: + result = self.ex_import_keypair_from_string(key_name, pubkey) + + return result def ex_list_keypairs(self): """ @@ -1040,34 +1112,32 @@ class BaseEC2NodeDriver(NodeDriver): :rtype: ``list`` of ``dict`` """ - params = { - 'Action': 'DescribeKeyPairs' - } + warnings.warn('This method has been deprecated in favor of ' + 'list_key_pairs method') - response = self.connection.request(self.path, params=params).object - keypairs = [] - for elem in findall(element=response, xpath='keySet/item', - namespace=NAMESPACE): - keypair = { - 'keyName': findtext(element=elem, xpath='keyName', - namespace=NAMESPACE), - 'keyFingerprint': findtext(element=elem, - xpath='keyFingerprint', - namespace=NAMESPACE).strip(), + key_pairs = self.list_key_pairs() + + result = [] + + for key_pair in key_pairs: + item = { + 'keyName': key_pair.name, + 'keyFingerprint': key_pair.fingerprint, } - keypairs.append(keypair) + result.append(item) - return keypairs + return result def ex_describe_all_keypairs(self): """ - Describes all keypairs. This is here for backward compatibilty. + Return names for all the available key pairs. @note: This is a non-standard extension API, and only works for EC2. :rtype: ``list`` of ``str`` """ - return [k['keyName'] for k in self.ex_list_keypairs()] + names = [key_pair.name for key_pair in self.list_key_pairs()] + return names def ex_describe_keypairs(self, name): """ http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/compute/drivers/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index 7d0958a..1866120 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -16,23 +16,19 @@ OpenStack driver """ -from __future__ import with_statement - try: import simplejson as json except ImportError: import json import warnings +import base64 from libcloud.utils.py3 import httplib from libcloud.utils.py3 import b from libcloud.utils.py3 import next from libcloud.utils.py3 import urlparse -import os -import base64 - from xml.etree import ElementTree as ET from libcloud.common.openstack import OpenStackBaseConnection @@ -41,6 +37,7 @@ from libcloud.common.types import MalformedResponseError, ProviderError from libcloud.compute.types import NodeState, Provider from libcloud.compute.base import NodeSize, NodeImage from libcloud.compute.base import NodeDriver, Node, NodeLocation, StorageVolume +from libcloud.compute.base import KeyPair from libcloud.pricing import get_size_price from libcloud.common.base import Response from libcloud.utils.xml import findall @@ -1710,16 +1707,51 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): (rule.id), method='DELETE') return resp.status == httplib.NO_CONTENT - def _to_keypairs(self, obj): - keypairs = obj['keypairs'] - return [self._to_keypair(keypair['keypair']) for keypair in keypairs] + def _to_key_pairs(self, obj): + key_pairs = obj['keypairs'] + key_pairs = [self._to_key_pair(key_pair['keypair']) for key_pair in + key_pairs] + return key_pairs + + def _to_key_pair(self, obj): + key_pair = KeyPair(name=obj['name'], + fingerprint=obj['fingerprint'], + public_key=obj['public_key'], + private_key=obj.get('private_key', None), + driver=self) + return key_pair + + def list_key_pairs(self): + response = self.connection.request('/os-keypairs') + key_pairs = self._to_key_pairs(response.object) + return key_pairs + + def create_key_pair(self, name): + data = {'keypair': {'name': name}} + response = self.connection.request('/os-keypairs', method='POST', + data=data) + key_pair = self._to_key_pair(response.object['keypair']) + return key_pair + + def import_key_pair_from_string(self, name, key_material): + data = {'keypair': {'name': name, 'public_key': key_material}} + response = self.connection.request('/os-keypairs', method='POST', + data=data) + key_pair = self._to_key_pair(response.object['keypair']) + return key_pair + + def delete_key_pair(self, key_pair): + """ + Delete a KeyPair. - def _to_keypair(self, obj): - return OpenStackKeyPair(name=obj['name'], - fingerprint=obj['fingerprint'], - public_key=obj['public_key'], - private_key=obj.get('private_key', None), - driver=self) + :param keypair: KeyPair to delete + :type keypair: :class:`OpenStackKeyPair` + + :rtype: ``bool`` + """ + response = self.connection.request('/os-keypairs/%s' % (key_pair.name), + method='DELETE') + return response.status == httplib.ACCEPTED def ex_list_keypairs(self): """ @@ -1727,8 +1759,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: ``list`` of :class:`OpenStackKeyPair` """ - return self._to_keypairs( - self.connection.request('/os-keypairs').object) + warnings.warn('This method has been deprecated in favor of ' + 'list_key_pairs method') + + return self.list_key_pairs() def ex_create_keypair(self, name): """ @@ -1739,10 +1773,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: :class:`OpenStackKeyPair` """ - return self._to_keypair(self.connection.request( - '/os-keypairs', method='POST', - data={'keypair': {'name': name}} - ).object['keypair']) + warnings.warn('This method has been deprecated in favor of ' + 'create_key_pair method') + + return self.create_key_pair(name=name) def ex_import_keypair(self, name, keyfile): """ @@ -1756,10 +1790,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: :class:`OpenStackKeyPair` """ - with open(os.path.expanduser(keyfile), 'r') as fp: - public_key = fp.read() + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_file method') - return self.ex_import_keypair_from_string(name, public_key) + return self.import_key_pair_from_file(name=name, key_file_path=keyfile) def ex_import_keypair_from_string(self, name, key_material): """ @@ -1773,10 +1807,11 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: :class:`OpenStackKeyPair` """ - return self._to_keypair(self.connection.request( - '/os-keypairs', method='POST', - data={'keypair': {'name': name, 'public_key': key_material}} - ).object['keypair']) + warnings.warn('This method has been deprecated in favor of ' + 'import_key_pair_from_string method') + + return self.import_key_pair_from_string(name=name, + key_material=key_material) def ex_delete_keypair(self, keypair): """ @@ -1787,9 +1822,10 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver): :rtype: ``bool`` """ - resp = self.connection.request('/os-keypairs/%s' % (keypair.name), - method='DELETE') - return resp.status == httplib.ACCEPTED + warnings.warn('This method has been deprecated in favor of ' + 'delete_key_pair method') + + return self.delete_key_pair(key_pair=keypair) def ex_get_size(self, size_id): """ http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/test/compute/fixtures/cloudstack/createSSHKeyPair_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/createSSHKeyPair_default.json b/libcloud/test/compute/fixtures/cloudstack/createSSHKeyPair_default.json new file mode 100644 index 0000000..2d0740d --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/createSSHKeyPair_default.json @@ -0,0 +1 @@ +{ "createsshkeypairresponse" : { "keypair" : {"name":"test-keypair","fingerprint":"51:9f:81:30:ec:82:0c:e5:8c:81:ac:14:27:d0:e5:e2","privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDMaSZY4v228AWWcXYLoojgaZ+K8SbuI8YoPDEi9UWcww5mWSTx\nVl6Ksb8YPFxL6+3/unlfr4zK1LksxgN8XRuZr+YBFGphUB6a5EcyshkXi3mfAE7d\n6a26ah6ySXFK9GmZoXcJqQ1xLC9rKGPL7tWgHmbX1lCbN6QinV0mZVEHNwIDAQAB\nAoGACXQngN7mqwpIx99xfTJEMFTSOyPSEBt5c6zs/NfpI0nmJZej3MGI19NGqkFI\nZ35+4F/ocyN0WIEkG00BJkRMHWdPNd+YnVSuVgEyGCD8hDvBbUEQrmdZ0VfQt+2q\nd52g573s6D6Skk/SZHGi3yHca4H52c3EpLJzThxUmJSSqmECQQD0loEIiQzQaap3\n/Gce7nZeLCSNXf0Q5aKFQv/X22srw6YvJ9/25cLahiFtQUadId9VUXSYTgEKX0ST\nB2CZ4UJxAkEA1fK/PT+YIHaiQIiCK/xTnoIuTvdXmH0IozolRxGAKpQZNvaMpKgn\nvXU84/yztekEPG0pKmCm7CZUZoGdfiJoJwJALwUsAy8NtpdJvU1ZqbmgKdSEpmS2\nPORYjRPnSWEWRlCThyc8SCO9hPMaQ/2zjIuxep5xMsJ0MsFD1pwpdwu2EQJBAMrG\nEZ7ZQTOzfMAxIT7THeWjeIR7RNhP2PnrSB19Zr30M5m2P0Jn5ZJZJWbnwOPuf4dN\n5rA1fr9e7KtiuYQs1A0CQQCT06qHdHaQr78A6YTEbDVr0M57qVrdsm5xyXzCmpMy\n9LxXAACghjHbjF//FEOjNG21I utbCg6cNIRz5EM8+MD+\n-----END RSA PRIVATE KEY-----\n"} } } http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/test/compute/fixtures/ec2/create_key_pair.xml ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/ec2/create_key_pair.xml b/libcloud/test/compute/fixtures/ec2/create_key_pair.xml new file mode 100644 index 0000000..10aec6d --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/create_key_pair.xml @@ -0,0 +1,22 @@ +<CreateKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/2010-08-31/"> + <keyName>my-key-pair</keyName> + <keyFingerprint> + 1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f + </keyFingerprint> + <keyMaterial>---- BEGIN RSA PRIVATE KEY ---- +MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6 +b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAd +BgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcN +MTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYD +VQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25z +b2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFt +YXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ +21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9T +rDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpE +Ibb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4 +nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0Fkb +FFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTb +NYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE +-----END RSA PRIVATE KEY-----</keyMaterial> +</CreateKeyPairResponse> http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/test/compute/test_cloudstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_cloudstack.py b/libcloud/test/compute/test_cloudstack.py index f7c99a4..65a0c28 100644 --- a/libcloud/test/compute/test_cloudstack.py +++ b/libcloud/test/compute/test_cloudstack.py @@ -271,51 +271,81 @@ class CloudStackCommonTestCase(TestCaseMixin): res = node.reboot() self.assertTrue(res) - def test_ex_list_keypairs(self): - keypairs = self.driver.ex_list_keypairs() + def test_list_key_pairs(self): + keypairs = self.driver.list_key_pairs() fingerprint = '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:' + \ '00:00:00:00:00' + self.assertEqual(keypairs[0].name, 'cs-keypair') + self.assertEqual(keypairs[0].fingerprint, fingerprint) + + # Test old and deprecated way + keypairs = self.driver.ex_list_keypairs() + self.assertEqual(keypairs[0]['name'], 'cs-keypair') self.assertEqual(keypairs[0]['fingerprint'], fingerprint) - def test_ex_list_keypairs_no_keypair_key(self): + def test_list_key_pairs_no_keypair_key(self): CloudStackMockHttp.fixture_tag = 'no_keys' - keypairs = self.driver.ex_list_keypairs() + keypairs = self.driver.list_key_pairs() self.assertEqual(keypairs, []) - def test_ex_create_keypair(self): - self.assertRaises( - LibcloudError, - self.driver.ex_create_keypair, - 'cs-keypair') + def test_create_keypair(self): + key_pair = self.driver.create_key_pair(name='test-keypair') - def test_ex_delete_keypair(self): - res = self.driver.ex_delete_keypair('cs-keypair') - self.assertTrue(res) + self.assertEqual(key_pair.name, 'test-keypair') + self.assertTrue(key_pair.fingerprint is not None) + self.assertTrue(key_pair.private_key is not None) + + # Test old and deprecated way + res = self.driver.ex_create_keypair(name='test-keypair') + self.assertEqual(res['name'], 'test-keypair') + self.assertTrue(res['fingerprint'] is not None) + self.assertTrue(res['privateKey'] is not None) - def test_ex_import_keypair(self): + def test_import_keypair_from_file(self): fingerprint = 'c4:a1:e5:d4:50:84:a9:4c:6b:22:ee:d6:57:02:b8:15' - path = os.path.join(os.path.dirname(__file__), "fixtures", - "cloudstack", - "dummy_rsa.pub") + path = os.path.join(os.path.dirname(__file__), 'fixtures', + 'cloudstack', + 'dummy_rsa.pub') + key_pair = self.driver.import_key_pair_from_file('foobar', path) + self.assertEqual(key_pair.name, 'foobar') + self.assertEqual(key_pair.fingerprint, fingerprint) + + # Test old and deprecated way res = self.driver.ex_import_keypair('foobar', path) self.assertEqual(res['keyName'], 'foobar') self.assertEqual(res['keyFingerprint'], fingerprint) def test_ex_import_keypair_from_string(self): fingerprint = 'c4:a1:e5:d4:50:84:a9:4c:6b:22:ee:d6:57:02:b8:15' - path = os.path.join(os.path.dirname(__file__), "fixtures", - "cloudstack", - "dummy_rsa.pub") + path = os.path.join(os.path.dirname(__file__), 'fixtures', + 'cloudstack', + 'dummy_rsa.pub') fh = open(path) - res = self.driver.ex_import_keypair_from_string('foobar', - fh.read()) + key_material = fh.read() fh.close() + + key_pair = self.driver.import_key_pair_from_string('foobar', key_material=key_material) + self.assertEqual(key_pair.name, 'foobar') + self.assertEqual(key_pair.fingerprint, fingerprint) + + # Test old and deprecated way + res = self.driver.ex_import_keypair_from_string('foobar', key_material=key_material) self.assertEqual(res['keyName'], 'foobar') self.assertEqual(res['keyFingerprint'], fingerprint) + def test_delete_key_pair(self): + key_pair = self.driver.list_key_pairs()[0] + + res = self.driver.delete_key_pair(key_pair=key_pair) + self.assertTrue(res) + + # Test old and deprecated way + res = self.driver.ex_delete_keypair(keypair='cs-keypair') + self.assertTrue(res) + def test_ex_list_security_groups(self): groups = self.driver.ex_list_security_groups() self.assertEqual(2, len(groups)) http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/test/compute/test_ec2.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 3952c68..c32bffe 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -375,13 +375,35 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(availability_zone.zone_state, 'available') self.assertEqual(availability_zone.region_name, 'eu-west-1') - def test_ex_list_keypairs(self): + def test_list_keypairs(self): + keypairs = self.driver.list_key_pairs() + + self.assertEqual(len(keypairs), 1) + self.assertEqual(keypairs[0].name, 'gsg-keypair') + self.assertEqual(keypairs[0].fingerprint, null_fingerprint) + + # Test old deprecated method keypairs = self.driver.ex_list_keypairs() self.assertEqual(len(keypairs), 1) self.assertEqual(keypairs[0]['keyName'], 'gsg-keypair') self.assertEqual(keypairs[0]['keyFingerprint'], null_fingerprint) + def test_create_key_pair(self): + key_pair = self.driver.create_key_pair(name='test-keypair') + + fingerprint = ('1f:51:ae:28:bf:89:e9:d8:1f:25:5d' + ':37:2d:7d:b8:ca:9f:f5:f1:6f') + + self.assertEqual(key_pair.name, 'my-key-pair') + self.assertEqual(key_pair.fingerprint, fingerprint) + self.assertTrue(key_pair.private_key is not None) + + # Test old and deprecated method + key_pair = self.driver.ex_create_keypair(name='test-keypair') + self.assertEqual(key_pair['keyFingerprint'], fingerprint) + self.assertTrue(key_pair['keyMaterial'] is not None) + def test_ex_describe_all_keypairs(self): keys = self.driver.ex_describe_all_keypairs() self.assertEqual(keys, ['gsg-keypair']) @@ -397,7 +419,11 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertEqual(keypair2['keyName'], 'gsg-keypair') self.assertEqual(keypair2['keyFingerprint'], null_fingerprint) - def ex_delete_keypair(self): + def ex_delete_key_pair(self): + success = self.driver.delete_key_pair('testkey') + self.assertTrue(success) + + # Test old and deprecated method resp = self.driver.ex_delete_keypair('testkey') self.assertTrue(resp) @@ -410,20 +436,33 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin): self.assertTrue('owner' in tags) self.assertTrue('stack' in tags) - def test_ex_import_keypair_from_string(self): + def test_import_key_pair_from_string(self): path = os.path.join(os.path.dirname(__file__), 'fixtures', 'misc', 'dummy_rsa.pub') - with open(path, 'r') as fh: - key = self.driver.ex_import_keypair_from_string( - 'keypair', fh.read()) + with open(path, 'r') as fp: + key_material = fp.read() + + key = self.driver.import_key_pair_from_string(name='keypair', + key_material=key_material) + self.assertEqual(key.name, 'keypair') + self.assertEqual(key.fingerprint, null_fingerprint) + # Test old and deprecated method + key = self.driver.ex_import_keypair_from_string('keypair', + key_material) self.assertEqual(key['keyName'], 'keypair') self.assertEqual(key['keyFingerprint'], null_fingerprint) - def test_ex_import_keypair(self): + def test_import_key_pair_from_file(self): path = os.path.join(os.path.dirname(__file__), 'fixtures', 'misc', 'dummy_rsa.pub') + + key = self.driver.import_key_pair_from_file('keypair', path) + self.assertEqual(key.name, 'keypair') + self.assertEqual(key.fingerprint, null_fingerprint) + + # Test old and deprecated method key = self.driver.ex_import_keypair('keypair', path) self.assertEqual(key['keyName'], 'keypair') self.assertEqual(key['keyFingerprint'], null_fingerprint) @@ -803,6 +842,10 @@ class EC2MockHttp(MockHttpTestCase): body = self.fixtures.load('describe_key_pairs.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _CreateKeyPair(self, method, url, body, headers): + body = self.fixtures.load('create_key_pair.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _ImportKeyPair(self, method, url, body, headers): body = self.fixtures.load('import_key_pair.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK]) http://git-wip-us.apache.org/repos/asf/libcloud/blob/b398aebd/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index b0e1d2c..3c8377b 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -1327,8 +1327,8 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): result = self.driver.ex_delete_security_group_rule(security_group_rule) self.assertTrue(result) - def test_ex_list_keypairs(self): - keypairs = self.driver.ex_list_keypairs() + def test_list_key_pairs(self): + keypairs = self.driver.list_key_pairs() self.assertEqual(len(keypairs), 2, 'Wrong keypairs count') keypair = keypairs[1] self.assertEqual(keypair.name, 'key2') @@ -1337,9 +1337,9 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertTrue(len(keypair.public_key) > 10) self.assertEqual(keypair.private_key, None) - def test_ex_create_keypair(self): + def test_create_key_pair(self): name = 'key0' - keypair = self.driver.ex_create_keypair(name) + keypair = self.driver.create_key_pair(name=name) self.assertEqual(keypair.name, name) self.assertEqual(keypair.fingerprint, @@ -1347,34 +1347,36 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertTrue(len(keypair.public_key) > 10) self.assertTrue(len(keypair.private_key) > 10) - def test_ex_import_keypair(self): + def test_import_key_pair_from_file(self): name = 'key3' path = os.path.join( - os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + os.path.dirname(__file__), 'fixtures', 'misc', 'dummy_rsa.pub') pub_key = open(path, 'r').read() - keypair = self.driver.ex_import_keypair(name, path) + keypair = self.driver.import_key_pair_from_file(name=name, + key_file_path=path) self.assertEqual(keypair.name, name) self.assertEqual( keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') self.assertEqual(keypair.public_key, pub_key) self.assertEqual(keypair.private_key, None) - def test_ex_import_keypair_from_string(self): + def test_import_key_pair_from_string(self): name = 'key3' path = os.path.join( - os.path.dirname(__file__), "fixtures", "misc", "dummy_rsa.pub") + os.path.dirname(__file__), 'fixtures', 'misc', 'dummy_rsa.pub') pub_key = open(path, 'r').read() - keypair = self.driver.ex_import_keypair_from_string(name, pub_key) + keypair = self.driver.import_key_pair_from_string(name=name, + key_material=pub_key) self.assertEqual(keypair.name, name) self.assertEqual( keypair.fingerprint, '97:10:a6:e7:92:65:7e:69:fe:e6:81:8f:39:3c:8f:5a') self.assertEqual(keypair.public_key, pub_key) self.assertEqual(keypair.private_key, None) - def test_ex_delete_keypair(self): + def test_delete_key_pair(self): keypair = OpenStackKeyPair( name='key1', fingerprint=None, public_key=None, driver=self.driver) - result = self.driver.ex_delete_keypair(keypair) + result = self.driver.delete_key_pair(key_pair=keypair) self.assertTrue(result) def test_ex_list_floating_ip_pools(self):
