From 1511d4cbffb20b2f039f2e4c098f871ccab7ec79 Mon Sep 17 00:00:00 2001 From: Sebastien Goasguen Date: Wed, 28 Aug 2013 11:18:50 -0400 Subject: [PATCH] LIBCLOUD-388: Added deploy_node for basic zone, improved consistency of params in create_node --- docs/compute/examples.rst | 3 +- .../create_cloudstack_node_keypair_secgroup.py | 9 +- libcloud/compute/drivers/cloudstack.py | 126 ++++++++++++++++----- .../cloudstack/queryAsyncJobResult_17164.json | 2 +- libcloud/test/compute/test_cloudstack.py | 2 +- 5 files changed, 102 insertions(+), 40 deletions(-) diff --git a/docs/compute/examples.rst b/docs/compute/examples.rst index a9418aa..6d71233 100644 --- a/docs/compute/examples.rst +++ b/docs/compute/examples.rst @@ -100,7 +100,8 @@ Create a node on a CloudStack provider using a provided key pair and security gr This example assumes the provided key pair already exists. If the key pair doesn't exist yet, you can create it using the provider's own UI, or - :func:`ex_create_keypair` driver method. + :func:`ex_create_keypair` driver method. This functionality is only available + in trunk. This example demonstrates how to create a node using an existing key pair. Created node also gets added to the provided security groups. diff --git a/docs/examples/compute/create_cloudstack_node_keypair_secgroup.py b/docs/examples/compute/create_cloudstack_node_keypair_secgroup.py index d1c8d90..8f04410 100644 --- a/docs/examples/compute/create_cloudstack_node_keypair_secgroup.py +++ b/docs/examples/compute/create_cloudstack_node_keypair_secgroup.py @@ -13,11 +13,7 @@ TEMPLATE_ID = 'id of the template you want to use' KEYPAIR_NAME = 'keypairname' # The security groups you want this node to be added to -SECURITY_GROUP_NAMES = 'secgroup1, secgroup2' - -# Extra arguments to pass to node creation -EXTRA_ARGS = {"keypair": KEYPAIR_NAME, - "securitygroupnames": SECURITY_GROUP_NAMES} +SECURITY_GROUP_NAMES = ['secgroup1', 'secgroup2'] cls = get_driver(Provider.CLOUDSTACK) driver = cls(key=ACCESS_ID, secret=SECRET_KEY, secure=True, @@ -29,4 +25,5 @@ size = [s for s in sizes if s.id == SIZE_ID][0] image = [i for i in images if i.id == IMAGE_ID][0] node = driver.create_node(name='test-node-1', image=image, size=size, - extra_args=EXTRA_ARGS) + ex_security_groups=SECURITY_GROUP_NAMES, + ex_keyname=KEYPAIR_NAME) diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index b9c08db..d870cbf 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -16,11 +16,14 @@ from __future__ import with_statement import os +import base64 + +from libcloud.utils.py3 import b from libcloud.compute.providers import Provider from libcloud.common.cloudstack import CloudStackDriverMixIn from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation,\ - NodeSize, StorageVolume + NodeSize, StorageVolume, is_private_subnet from libcloud.compute.types import NodeState, LibcloudError @@ -155,6 +158,8 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): website = 'http://cloudstack.org/' type = Provider.CLOUDSTACK + features = {"create_node": ["ssh_key", "generates_password"]} + NODE_STATE_MAP = { 'Running': NodeState.RUNNING, 'Starting': NodeState.REBOOTING, @@ -251,6 +256,14 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): private_ips.append(nic['ipaddress']) public_ips = public_ips_map.get(vm['id'], {}).keys() + public_ips.extend([ip for ip in private_ips + if not is_private_subnet(ip)]) + + keypair, password = None, None + if 'keypair' in vm.keys(): + keypair = vm['keypair'] + if 'password' in vm.keys(): + password = vm['password'] node = CloudStackNode( id=vm['id'], @@ -259,7 +272,11 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): public_ips=public_ips, private_ips=private_ips, driver=self, - extra={'zoneid': vm['zoneid'], } + extra={'zoneid': vm['zoneid'], + 'password': password, + 'key_name': keypair, + 'created': vm['created'] + } ) addresses = public_ips_map.get(vm['id'], {}).items() @@ -281,7 +298,6 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): node.extra['ip_forwarding_rules'] = rules rules = [] - for addr in addrs: result = self._sync_request('listPortForwardingRules') for r in result.get('portforwardingrule', []): @@ -310,57 +326,103 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): 0, self)) return sizes - def create_node(self, name, size, image, location=None, extra_args=None, - **kwargs): + def create_node(self, **kwargs): """ + Create a new node + @inherits: L{NodeDriver.create_node} - @keyword extra_args: Extra argument passed to the - "deployVirtualMachine" call. A list of available arguments can be found - at http://cloudstack.apache.org/docs/api/apidocs-4.0.0/root_admin/ \ - deployVirtualMachine.html - @type extra_args: C{dict} + @keyword ex_keyname: Name of existing keypair + @type ex_keyname: C{str} - @rtype: L{CloudStackNode} - """ + @keyword ex_userdata: String containing user data + @type ex_userdata: C{str} - if extra_args: - request_args = extra_args.copy() - else: - request_args = {} + @keyword networks: The server is launched into a set of Networks. + @type networks: L{CloudStackNetwork} - if location is None: - location = self.list_locations()[0] + @keyword ex_security_groups: List of security groups to assign to + the node + @type ex_security_groups: C{list} of C{str} - if 'network_id' in kwargs: - request_args['networkids'] = kwargs['network_id'] + @rtype: L{CloudStackNode} + """ - result = self._async_request( - 'deployVirtualMachine', name=name, displayname=name, - serviceofferingid=size.id, templateid=image.id, - zoneid=location.id, **request_args - ) + server_params = self._create_args_to_params(None, **kwargs) - node = result['virtualmachine'] - state = self.NODE_STATE_MAP[node['state']] + node = self._async_request('deployVirtualMachine', + **server_params)['virtualmachine'] public_ips = [] - private_ips = [nic['ipaddress'] for nic in node['nic']] + private_ips = [] + for nic in node['nic']: + if is_private_subnet(nic['ipaddress']): + private_ips.append(nic['ipaddress']) + else: + public_ips.append(nic['ipaddress']) + + keypair, password = None, None + if keypair in node.keys(): + keypair = node['keypair'] + if password in node.keys(): + password = node['password'] return CloudStackNode( id=node['id'], name=node['displayname'], - state=state, + state=self.NODE_STATE_MAP[node['state']], public_ips=public_ips, private_ips=private_ips, driver=self, - extra={'zoneid': location.id, + extra={'zoneid': server_params['zoneid'], 'ip_addresses': [], 'ip_forwarding_rules': [], - 'port_forwarding_rules': [] + 'port_forwarding_rules': [], + 'password': password, + 'key_name': keypair, + 'created': node['created'] } + ) + def _create_args_to_params(self, node, **kwargs): + server_params = { + 'name': kwargs.get('name'), + } + + if 'name' in kwargs: + server_params['displayname'] = kwargs.get('name') + + if 'size' in kwargs: + server_params['serviceofferingid'] = kwargs.get('size').id + + if 'image' in kwargs: + server_params['templateid'] = kwargs.get('image').id + + if 'location' in kwargs: + server_params['zoneid'] = kwargs.get('location').id + else: + server_params['zoneid'] = self.list_locations()[0].id + + if 'ex_keyname' in kwargs: + server_params['keypair'] = kwargs['ex_keyname'] + + if 'ex_userdata' in kwargs: + server_params['userdata'] = base64.b64encode( + b(kwargs['ex_userdata'])).decode('ascii') + + if 'networks' in kwargs: + networks = kwargs['networks'] + networks = ','.join([network.id for network in networks]) + server_params['networkids'] = networks + + if 'ex_security_groups' in kwargs: + security_groups = kwargs['ex_security_groups'] + security_groups = ','.join(security_groups) + server_params['securitygroupnames'] = security_groups + + return server_params + def destroy_node(self, node): """ @inherits: L{NodeDriver.reboot_node} @@ -642,6 +704,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): public_port, private_port) node.extra['port_forwarding_rules'].append(rule) + node.public_ips.append(address) return rule def ex_delete_port_forwarding_rule(self, node, rule): @@ -658,6 +721,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): """ node.extra['port_forwarding_rules'].remove(rule) + node.public_ips.remove(rule.address) res = self._async_request('deletePortForwardingRule', id=rule.id) return res['success'] diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17164.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17164.json index 31fd39e..33dc746 100644 --- a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17164.json +++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17164.json @@ -1 +1 @@ -{ "queryasyncjobresultresponse" : {"jobid":17164,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"virtualmachine":{"id":2602,"name":"fred","displayname":"fred","account":"fakeaccount","domainid":801,"domain":"AA000062-libcloud-dev","created":"2011-06-23T05:48:31+0000","state":"Running","haenable":false,"zoneid":1,"zonename":"Sydney","templateid":421,"templatename":"XEN Basic Ubuntu 10.04 Server x64 PV r2.0","templatedisplaytext":"XEN Basic Ubuntu 10.04 Server x64 PV r2.0","passwordenabled":false,"serviceofferingid":105,"serviceofferingname":"Compute Micro PRD","cpunumber":1,"cpuspeed":1200,"memory":384,"guestosid":12,"rootdeviceid":0,"rootdevicetype":"IscsiLUN","securitygroup":[],"nic":[{"id":3893,"networkid":860,"netmask":"255.255.240.0","gateway":"1.1.1.1","ipaddress":"1.1.1.2","traffictype":"Guest","type":"Virtual","isdefault":true}],"hypervisor":"XenServer"}}} } +{ "queryasyncjobresultresponse" : {"jobid":17164,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"virtualmachine":{"id":2602,"name":"fred","displayname":"fred","account":"fakeaccount","domainid":801,"domain":"AA000062-libcloud-dev","created":"2011-06-23T05:48:31+0000","state":"Running","haenable":false,"zoneid":1,"zonename":"Sydney","templateid":421,"templatename":"XEN Basic Ubuntu 10.04 Server x64 PV r2.0","templatedisplaytext":"XEN Basic Ubuntu 10.04 Server x64 PV r2.0","passwordenabled":false,"serviceofferingid":105,"serviceofferingname":"Compute Micro PRD","cpunumber":1,"cpuspeed":1200,"memory":384,"guestosid":12,"rootdeviceid":0,"rootdevicetype":"IscsiLUN","securitygroup":[],"nic":[{"id":3893,"networkid":860,"netmask":"255.255.240.0","gateway":"1.1.1.1","ipaddress":"192.168.1.2","traffictype":"Guest","type":"Virtual","isdefault":true}],"hypervisor":"XenServer"}}} } diff --git a/libcloud/test/compute/test_cloudstack.py b/libcloud/test/compute/test_cloudstack.py index 575fe53..ece5f2a 100644 --- a/libcloud/test/compute/test_cloudstack.py +++ b/libcloud/test/compute/test_cloudstack.py @@ -98,7 +98,7 @@ class CloudStackNodeDriverTest(unittest.TestCase, TestCaseMixin): self.assertEqual(node.name, 'fred') self.assertEqual(node.public_ips, []) - self.assertEqual(node.private_ips, ['1.1.1.2']) + self.assertEqual(node.private_ips, ['192.168.1.2']) self.assertEqual(node.extra['zoneid'], default_location.id) def test_list_images_no_images_available(self): -- 1.8.2.3