New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ssh key management for softlayer #321
Changes from 20 commits
49acc51
60b0a71
ff688a3
77a663f
c6ae1dc
b403777
cd2a511
a2db2d6
c244601
20b0239
66a2e0f
605893b
d1954f5
fb6d574
583b14b
6668136
391149d
6099e09
1c7ca6e
2d96355
76c8ad4
6b809b7
9167cc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,13 +17,20 @@ | |
""" | ||
|
||
import time | ||
crypto = False | ||
try: | ||
from Crypto.PublicKey import RSA | ||
crypto = True | ||
except ImportError: | ||
pass | ||
|
||
from libcloud.common.base import ConnectionUserAndKey | ||
from libcloud.common.xmlrpc import XMLRPCResponse, XMLRPCConnection | ||
from libcloud.common.types import InvalidCredsError, LibcloudError | ||
from libcloud.compute.types import Provider, NodeState | ||
from libcloud.compute.base import NodeDriver, Node, NodeLocation, NodeSize, \ | ||
NodeImage | ||
NodeImage, KeyPair | ||
from libcloud.compute.types import KeyPairDoesNotExistError | ||
|
||
DEFAULT_DOMAIN = 'example.com' | ||
DEFAULT_CPU_SIZE = 1 | ||
|
@@ -158,7 +165,6 @@ def request(self, service, method, *args, **kwargs): | |
|
||
args = ({'headers': headers}, ) + args | ||
endpoint = '%s/%s' % (self.endpoint, service) | ||
|
||
return super(SoftLayerConnection, self).request(method, *args, | ||
**{'endpoint': | ||
endpoint}) | ||
|
@@ -204,7 +210,7 @@ class SoftLayerNodeDriver(NodeDriver): | |
website = 'http://www.softlayer.com/' | ||
type = Provider.SOFTLAYER | ||
|
||
features = {'create_node': ['generates_password']} | ||
features = {'create_node': ['generates_password', 'ssh_key']} | ||
|
||
def _to_node(self, host): | ||
try: | ||
|
@@ -330,6 +336,8 @@ def create_node(self, **kwargs): | |
:type ex_datacenter: ``str`` | ||
:keyword ex_os: e.g. UBUNTU_LATEST | ||
:type ex_os: ``str`` | ||
:keyword ex_keyname: The name of the key pair | ||
:type ex_keyname: ``str`` | ||
""" | ||
name = kwargs['name'] | ||
os = 'DEBIAN_LATEST' | ||
|
@@ -402,6 +410,9 @@ def create_node(self, **kwargs): | |
if datacenter: | ||
newCCI['datacenter'] = {'name': datacenter} | ||
|
||
if 'ex_keyname' in kwargs: | ||
newCCI['sshKeys'] = [self._key_name_to_id(kwargs['ex_keyname'])] | ||
|
||
res = self.connection.request( | ||
'SoftLayer_Virtual_Guest', 'createObject', newCCI | ||
).object | ||
|
@@ -411,6 +422,82 @@ def create_node(self, **kwargs): | |
|
||
return self._to_node(raw_node) | ||
|
||
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): | ||
key_pair = KeyPair(name=elem['label'], | ||
public_key=elem['key'], | ||
fingerprint=elem['fingerprint'], | ||
private_key=elem.get('private', None), | ||
driver=self, | ||
extra={'id': elem['id']}) | ||
return key_pair | ||
|
||
def _key_name_to_id(self, key): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Argument should probably be called |
||
result = self.connection.request( | ||
'SoftLayer_Account', 'getSshKeys' | ||
).object | ||
key_id = [x for x in result if x['label'] == key] | ||
if len(key_id) == 0: | ||
raise KeyPairDoesNotExistError(key, self) | ||
else: | ||
return int(key_id[0]['id']) | ||
|
||
def list_key_pairs(self): | ||
result = self.connection.request( | ||
'SoftLayer_Account', 'getSshKeys' | ||
).object | ||
elems = [x for x in result] | ||
key_pairs = self._to_key_pairs(elems=elems) | ||
return key_pairs | ||
|
||
def get_key_pair(self, name): | ||
key_id = self._key_name_to_id(name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor thing - for consistency and explicitness sake, please use keyword arguments ( |
||
result = self.connection.request( | ||
'SoftLayer_Security_Ssh_Key', 'getObject', id=key_id | ||
).object | ||
return self._to_key_pair(result) | ||
|
||
# TODO: Check this with the libcloud guys, | ||
# can we create new dependencies? | ||
def create_key_pair(self, name): | ||
if crypto is False: | ||
raise NotImplementedError("create_key_pair needs" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor style thing - for consistency, please use single quotes around strings. |
||
" the pycrypto library") | ||
key = RSA.generate(2048) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably also allow user to specify key size and default to For example: def create_key_pair(self, name, ex_size=4096) |
||
new_key = { | ||
'key': key.publickey().exportKey("OpenSSH"), | ||
'label': name, | ||
'notes': '', | ||
} | ||
result = self.connection.request( | ||
'SoftLayer_Security_Ssh_Key', 'createObject', new_key | ||
).object | ||
result['private'] = key.exportKey("PEM") | ||
return self._to_key_pair(result) | ||
|
||
def import_key_pair_from_string(self, name, key_material): | ||
new_key = { | ||
'key': key_material, | ||
'label': name, | ||
'notes': '', | ||
} | ||
result = self.connection.request( | ||
'SoftLayer_Security_Ssh_Key', 'createObject', new_key | ||
).object | ||
|
||
key_pair = self._to_key_pair(result) | ||
return key_pair | ||
|
||
def delete_key_pair(self, key_pair): | ||
key = self._key_name_to_id(key_pair) | ||
result = self.connection.request( | ||
'SoftLayer_Security_Ssh_Key', 'deleteObject', id=key | ||
).object | ||
return result | ||
|
||
def _to_image(self, img): | ||
return NodeImage( | ||
id=img['template']['operatingSystemReferenceCode'], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<params> | ||
<param> | ||
<value> | ||
<struct> | ||
<member> | ||
<name>id</name> | ||
<value> | ||
<int>1</int> | ||
</value> | ||
</member> | ||
<member> | ||
<name>key</name> | ||
<value> | ||
<string>ssh-key</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>label</name> | ||
<value> | ||
<string>test1</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>fingerprint</name> | ||
<value> | ||
<string>00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00</string> | ||
</value> | ||
</member> | ||
</struct> | ||
</value> | ||
</param> | ||
<param> | ||
<value> | ||
<struct> | ||
<member> | ||
<name>id</name> | ||
<value> | ||
<int>2</int> | ||
</value> | ||
</member> | ||
<member> | ||
<name>key</name> | ||
<value> | ||
<string>ssh-key</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>label</name> | ||
<value> | ||
<string>test2</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>fingerprint</name> | ||
<value> | ||
<string>00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00</string> | ||
</value> | ||
</member> | ||
</struct> | ||
</value> | ||
</param> | ||
</params> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<params> | ||
<param> | ||
<value> | ||
<struct> | ||
<member> | ||
<name>id</name> | ||
<value> | ||
<int>1</int> | ||
</value> | ||
</member> | ||
<member> | ||
<name>key</name> | ||
<value> | ||
<string>ssh-key</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>label</name> | ||
<value> | ||
<string>my-key-pair</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>label</name> | ||
<value> | ||
<string>my-key-pair</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>fingerprint</name> | ||
<value> | ||
<string>1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f</string> | ||
</value> | ||
</member> | ||
</struct> | ||
</value> | ||
</param> | ||
</params> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<params> | ||
<param> | ||
<value> | ||
<struct> | ||
<member> | ||
<name>status</name> | ||
<value> | ||
<string>success</string> | ||
</value> | ||
</member> | ||
</struct> | ||
</value> | ||
</param> | ||
</params> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<params> | ||
<param> | ||
<value> | ||
<struct> | ||
<member> | ||
<name>id</name> | ||
<value> | ||
<int>1</int> | ||
</value> | ||
</member> | ||
<member> | ||
<name>key</name> | ||
<value> | ||
<string>ssh-key</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>label</name> | ||
<value> | ||
<string>test1</string> | ||
</value> | ||
</member> | ||
<member> | ||
<name>fingerprint</name> | ||
<value> | ||
<string>00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00</string> | ||
</value> | ||
</member> | ||
</struct> | ||
</value> | ||
</param> | ||
</params> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor style thing - for consistency, please move "private" methods to the end of the class after all the "public" ones.