Author: tomaz
Date: Thu Jun 6 20:16:15 2013
New Revision: 1490422
URL: http://svn.apache.org/r1490422
Log:
Backport changes from trunk.
Added:
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/authorizeSecurityGroupIngress_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/authorizeSecurityGroupIngress_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/createSecurityGroup_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/createSecurityGroup_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/deleteSSHKeyPair_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/deleteSSHKeyPair_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/deleteSecurityGroup_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/deleteSecurityGroup_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/listSSHKeyPairs_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/listSSHKeyPairs_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/listSecurityGroups_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/listSecurityGroups_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17188.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17188.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17199.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17199.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17200.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_17200.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/startVirtualMachine_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/startVirtualMachine_default.json
libcloud/branches/0.12.x/libcloud/test/compute/fixtures/cloudstack/stopVirtualMachine_default.json
- copied unchanged from r1490413,
libcloud/trunk/libcloud/test/compute/fixtures/cloudstack/stopVirtualMachine_default.json
Modified:
libcloud/branches/0.12.x/ (props changed)
libcloud/branches/0.12.x/CHANGES
libcloud/branches/0.12.x/libcloud/compute/drivers/cloudstack.py
libcloud/branches/0.12.x/libcloud/test/compute/test_cloudstack.py
Propchange: libcloud/branches/0.12.x/
------------------------------------------------------------------------------
Merged /libcloud/trunk:r1490082-1490416
Modified: libcloud/branches/0.12.x/CHANGES
URL:
http://svn.apache.org/viewvc/libcloud/branches/0.12.x/CHANGES?rev=1490422&r1=1490421&r2=1490422&view=diff
==============================================================================
--- libcloud/branches/0.12.x/CHANGES (original)
+++ libcloud/branches/0.12.x/CHANGES Thu Jun 6 20:16:15 2013
@@ -66,6 +66,18 @@ Changes with Apache Libcloud in deveplom
when generating a random root password in create_node. (LIBCLOUD-334)
[Juan Carlos Moreno]
+ - Add extension methods for managing keypairs to the CloudStack driver.
+ (LIBCLOUD-333)
+ [sebastien goasguen]
+
+ - Add extension methods for managing security groups to the CloudStack
+ driver. (LIBCLOUD-332)
+ [sebastien goasguen]
+
+ - Add extension methods for starting and stoping the node to the
+ CloudStack driver. (LIBCLOUD-338)
+ [sebastien goasguen]
+
*) Storage
- Fix an issue with double encoding the container name in the CloudFiles
Modified: libcloud/branches/0.12.x/libcloud/compute/drivers/cloudstack.py
URL:
http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/drivers/cloudstack.py?rev=1490422&r1=1490421&r2=1490422&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/drivers/cloudstack.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/drivers/cloudstack.py Thu Jun 6
20:16:15 2013
@@ -41,6 +41,14 @@ class CloudStackNode(Node):
"Delete a NAT/firewall rule."
return self.driver.ex_delete_ip_forwarding_rule(self, rule)
+ def ex_start(self):
+ "Starts a stopped virtual machine"
+ return self.driver.ex_start(self)
+
+ def ex_stop(self):
+ "Stops a running virtual machine"
+ return self.driver.ex_stop(self)
+
class CloudStackAddress(object):
"A public IP address."
@@ -252,7 +260,7 @@ class CloudStackNodeDriver(CloudStackDri
location = self.list_locations()[0]
if 'network_id' in kwargs:
- request_args['networkids'] = network_id
+ request_args['networkids'] = kwargs['network_id']
result = self._async_request(
'deployVirtualMachine', name=name, displayname=name,
@@ -296,8 +304,47 @@ class CloudStackNodeDriver(CloudStackDri
self._async_request('rebootVirtualMachine', id=node.id)
return True
+ def ex_start(self, node):
+ """
+ Starts/Resumes a stopped virtual machine
+
+ @type node: L{CloudStackNode}
+
+ @param id: The ID of the virtual machine (required)
+ @type id: C{uuid}
+
+ @param hostid: destination Host ID to deploy the VM to
+ parameter available for root admin only
+ @type hostid: C{uuid}
+
+ @rtype C{str}
+ """
+ res = self._async_request('startVirtualMachine', id=node.id)
+ return res['virtualmachine']['state']
+
+ def ex_stop(self, node):
+ """
+ Stops/Suspends a running virtual machine
+
+ @type node: L{CloudStackNode}
+
+ @param id: The ID of the virtual machine
+ @type id: C{uuid}
+
+ @param forced: Force stop the VM
+ (vm is marked as Stopped even when command
+ fails to be send to the backend).
+ The caller knows the VM is stopped.
+ @type forced: C{bool}
+
+ @rtype C{str}
+ """
+ res = self._async_request('stopVirtualMachine', id=node.id)
+ return res['virtualmachine']['state']
+
def ex_list_disk_offerings(self):
- """Fetch a list of all available disk offerings.
+ """
+ Fetch a list of all available disk offerings.
@rtype: C{list} of L{CloudStackDiskOffering}
"""
@@ -362,7 +409,7 @@ class CloudStackNodeDriver(CloudStackDri
def ex_allocate_public_ip(self, node):
"""
- "Allocate a public IP and bind it to a node.
+ Allocate a public IP and bind it to a node.
@param node: Node which should be used
@type node: L{CloudStackNode}
@@ -463,6 +510,299 @@ class CloudStackNodeDriver(CloudStackDri
self._async_request('deleteIpForwardingRule', id=rule.id)
return True
+ def ex_list_keypairs(self, **kwargs):
+ """
+ List Registered SSH Key Pairs
+
+ @param projectid: list objects by project
+ @type projectid: C{uuid}
+
+ @param page: The page to list the keypairs from
+ @type page: C{int}
+
+ @param keyword: List by keyword
+ @type keyword: C{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: C{bool}
+
+ @param pagesize: The number of results per page
+ @type pagesize: C{int}
+
+ @param account: List resources by account.
+ Must be used with the domainId parameter
+ @type account: C{str}
+
+ @param isrecursive: Defaults to false, but if true,
+ lists all resources from
+ the parent specified by the
+ domainId till leaves.
+ @type isrecursive: C{bool}
+
+ @param fingerprint: A public key fingerprint to look for
+ @type fingerprint: C{str}
+
+ @param name: A key pair name to look for
+ @type name: C{str}
+
+ @param domainid: List only resources belonging to
+ the domain specified
+ @type domainid: C{uuid}
+
+ @return: A list of keypair dictionaries
+ @rtype: L{dict}
+ """
+
+ extra_args = kwargs.copy()
+ res = self._sync_request('listSSHKeyPairs', **extra_args)
+ return res['sshkeypair']
+
+ def ex_create_keypair(self, name, **kwargs):
+ """
+ Creates a SSH KeyPair, returns fingerprint and private key
+
+ @param name: Name of the keypair (required)
+ @type name: C{str}
+
+ @param projectid: An optional project for the ssh key
+ @type projectid: C{str}
+
+ @param domainid: An optional domainId for the ssh key.
+ If the account parameter is used,
+ domainId must also be used.
+ @type domainid: C{str}
+
+ @param account: An optional account for the ssh key.
+ Must be used with domainId.
+ @type account: C{str}
+
+ @return: A keypair dictionary
+ @rtype: C{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))
+
+ res = self._sync_request('createSSHKeyPair', name=name, **extra_args)
+ return res['keypair']
+
+ def ex_delete_keypair(self, name, **kwargs):
+ """
+ Deletes an existing SSH KeyPair
+
+ @param name: Name of the keypair (required)
+ @type name: C{str}
+
+ @param projectid: The project associated with keypair
+ @type projectid: C{uuid}
+
+ @param domainid : The domain ID associated with the keypair
+ @type domainid: C{uuid}
+
+ @param account : The account associated with the keypair.
+ Must be used with the domainId parameter.
+ @type account: C{str}
+
+ @return: True of False based on success of Keypair deletion
+ @rtype: C{bool}
+ """
+
+ extra_args = kwargs.copy()
+
+ res = self._sync_request('deleteSSHKeyPair', name=name, **extra_args)
+ return res['success']
+
+ def ex_list_security_groups(self, **kwargs):
+ """
+ Lists Security Groups
+
+ @param domainid: List only resources belonging to the domain specified
+ @type domainid: C{uuid}
+
+ @param account: List resources by account. Must be used with
+ the domainId parameter.
+ @type account: C{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: C{bool}
+
+ @param pagesize: Number of entries per page
+ @type pagesize: C{int}
+
+ @param keyword: List by keyword
+ @type keyword: C{str}
+
+ @param tags: List resources by tags (key/value pairs)
+ @type tags: C{dict}
+
+ @param id: list the security group by the id provided
+ @type id: C{uuid}
+
+ @param securitygroupname: lists security groups by name
+ @type securitygroupname: C{str}
+
+ @param virtualmachineid: lists security groups by virtual machine id
+ @type virtualmachineid: C{uuid}
+
+ @param projectid: list objects by project
+ @type projectid: C{uuid}
+
+ @param isrecursive: (boolean) defaults to false, but if true,
+ lists all resources from the parent
+ specified by the domainId till leaves.
+ @type isrecursive: C{bool}
+
+ @param page: (integer)
+ @type page: C{int}
+
+ @rtype C{list}
+ """
+ extra_args = kwargs
+ return self._sync_request('listSecurityGroups',
+ **extra_args)['securitygroup']
+
+ def ex_create_security_group(self, name, **kwargs):
+ """
+ Creates a new Security Group
+
+ @param name: name of the security group (required)
+ @type name: C{str}
+
+ @param account: An optional account for the security group.
+ Must be used with domainId.
+ @type account: C{str}
+
+ @param domainid: An optional domainId for the security group.
+ If the account parameter is used,
+ domainId must also be used.
+ @type domainid: C{uuid}
+
+ @param description: The description of the security group
+ @type description: C{str}
+
+ @param projectid: Deploy vm for the project
+ @type projectid: C{uuid}
+
+ @rtype: C{dict}
+ """
+
+ extra_args = kwargs.copy()
+
+ for sg in self.ex_list_security_groups():
+ if name in sg['name']:
+ raise LibcloudError('This Security Group name already exists')
+
+ return self._sync_request('createSecurityGroup',
+ name=name, **extra_args)['securitygroup']
+
+ def ex_delete_security_group(self, name):
+ """
+ Deletes a given Security Group
+
+ @param domainid: The domain ID of account owning
+ the security group
+ @type domainid: C{uuid}
+
+ @param id: The ID of the security group.
+ Mutually exclusive with name parameter
+ @type id: C{uuid}
+
+ @param name: The ID of the security group.
+ Mutually exclusive with id parameter
+ @type name: C{str}
+
+ @param account: The account of the security group.
+ Must be specified with domain ID
+ @type account: C{str}
+
+ @param projectid: The project of the security group
+ @type projectid: C{uuid}
+
+ @rtype: C{bool}
+ """
+
+ return self._sync_request('deleteSecurityGroup', name=name)['success']
+
+ def ex_authorize_security_group_ingress(self, securitygroupname,
+ protocol, cidrlist, startport,
+ endport=None):
+ """
+ Creates a new Security Group Ingress rule
+
+ @param domainid: An optional domainId for the security group.
+ If the account parameter is used,
+ domainId must also be used.
+ @type domainid: C{uuid}
+
+ @param startport: Start port for this ingress rule
+ @type startport: C{int}
+
+ @param securitygroupid: The ID of the security group.
+ Mutually exclusive with securityGroupName
+ parameter
+ @type securitygroupid: C{uuid}
+
+ @param cidrlist: The cidr list associated
+ @type cidrlist: C{list}
+
+ @param usersecuritygrouplist: user to security group mapping
+ @type usersecuritygrouplist: C{map}
+
+ @param securitygroupname: The name of the security group.
+ Mutually exclusive with
+ securityGroupName parameter
+ @type securitygroupname: C{str}
+
+ @param account: An optional account for the security group.
+ Must be used with domainId.
+ @type account: C{str}
+
+ @param icmpcode: Error code for this icmp message
+ @type icmpcode: C{int}
+
+ @param protocol: TCP is default. UDP is the other supported protocol
+ @type protocol: C{str}
+
+ @param icmptype: type of the icmp message being sent
+ @type icmptype: C{int}
+
+ @param projectid: An optional project of the security group
+ @type projectid: C{uuid}
+
+ @param endport: end port for this ingress rule
+ @type endport: C{int}
+
+ @rtype: C{list}
+ """
+
+ protocol = protocol.upper()
+ if protocol not in ('TCP', 'ICMP'):
+ raise LibcloudError('Only TCP and ICMP are allowed')
+
+ args = {
+ 'securitygroupname': securitygroupname,
+ 'protocol': protocol,
+ 'startport': int(startport),
+ 'cidrlist': cidrlist
+ }
+ if endport is None:
+ args['endport'] = int(startport)
+
+ return self._async_request('authorizeSecurityGroupIngress',
+ **args)['securitygroup']
+
def ex_register_iso(self, name, url, location=None, **kwargs):
"""
Registers an existing ISO by URL.
Modified: libcloud/branches/0.12.x/libcloud/test/compute/test_cloudstack.py
URL:
http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/test/compute/test_cloudstack.py?rev=1490422&r1=1490421&r2=1490422&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/test/compute/test_cloudstack.py (original)
+++ libcloud/branches/0.12.x/libcloud/test/compute/test_cloudstack.py Thu Jun
6 20:16:15 2013
@@ -1,3 +1,18 @@
+# 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
@@ -17,11 +32,13 @@ except AttributeError:
from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
from libcloud.compute.types import DeploymentError, LibcloudError
+from libcloud.compute.base import Node, NodeImage, NodeSize, NodeLocation
from libcloud.test import MockHttpTestCase
from libcloud.test.compute import TestCaseMixin
from libcloud.test.file_fixtures import ComputeFileFixtures
+
class CloudStackNodeDriverTest(unittest.TestCase, TestCaseMixin):
def setUp(self):
CloudStackNodeDriver.connectionCls.conn_classes = \
@@ -104,9 +121,9 @@ class CloudStackNodeDriverTest(unittest.
location = self.driver.list_locations()[0]
self.assertRaises(
- LibcloudError,
- self.driver.create_volume,
- 'vol-0', location, 11)
+ LibcloudError,
+ self.driver.create_volume,
+ 'vol-0', location, 11)
def test_create_volume_with_custom_disk_size_offering(self):
CloudStackMockHttp.fixture_tag = 'withcustomdisksize'
@@ -128,6 +145,60 @@ class CloudStackNodeDriverTest(unittest.
self.assertTrue(attachReturnVal)
+ def test_list_nodes(self):
+ node = self.driver.list_nodes()[0]
+ self.assertEquals('test', node.name)
+
+ def test_list_locations(self):
+ location = self.driver.list_locations()[0]
+ self.assertEquals('Sydney', location.name)
+
+ def test_start_node(self):
+ node = self.driver.list_nodes()[0]
+ res = node.ex_start()
+ self.assertEquals('Starting', res)
+
+ def test_stop_node(self):
+ node = self.driver.list_nodes()[0]
+ res = node.ex_stop()
+ self.assertEquals('Stopped', res)
+
+ def test_list_keypairs(self):
+ keypairs = self.driver.ex_list_keypairs()
+ 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)
+
+ def test_create_keypair(self):
+ self.assertRaises(LibcloudError, self.driver.ex_create_keypair,
+ 'cs-keypair')
+
+ def test_delete_keypair(self):
+ res = self.driver.ex_delete_keypair('cs-keypair')
+ self.assertTrue(res)
+
+ def test_list_security_groups(self):
+ groups = self.driver.ex_list_security_groups()
+ self.assertEqual(groups[0]['name'], 'default')
+
+ def test_create_security_group(self):
+ group = self.driver.ex_create_security_group(name='MySG')
+ self.assertEqual(group['name'], 'MySG')
+
+ def test_delete_security_group(self):
+ res = self.driver.ex_delete_security_group(name='MySG')
+ self.assertTrue(res)
+
+ def test_authorize_security_group_ingress(self):
+ res = self.driver.ex_authorize_security_group_ingress('MySG',
+ 'TCP',
+ '22',
+ '22',
+ '0.0.0.0/0')
+ self.assertTrue(res)
+
class CloudStackMockHttp(MockHttpTestCase):
fixtures = ComputeFileFixtures('cloudstack')