You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by se...@apache.org on 2015/02/27 10:12:53 UTC
libcloud git commit: Add Affinity Group support to CloudStack
Repository: libcloud
Updated Branches:
refs/heads/trunk 62864f52e -> 52d96c7f5
Add Affinity Group support to CloudStack
Signed-off-by: Sebastien Goasguen <ru...@gmail.com>
This closes #468
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/52d96c7f
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/52d96c7f
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/52d96c7f
Branch: refs/heads/trunk
Commit: 52d96c7f5d4284f6574d1dd9bf7c14ba6102fa95
Parents: 62864f5
Author: Mateusz Korszun <mk...@gmail.com>
Authored: Tue Feb 17 12:20:39 2015 +0100
Committer: Sebastien Goasguen <ru...@gmail.com>
Committed: Fri Feb 27 04:12:15 2015 -0500
----------------------------------------------------------------------
CHANGES.rst | 4 +
libcloud/compute/drivers/cloudstack.py | 213 +++++++++++++++++++
.../cloudstack/createAffinityGroup_default.json | 1 +
.../cloudstack/deleteAffinityGroup_default.json | 1 +
.../listAffinityGroupTypes_default.json | 1 +
.../cloudstack/listAffinityGroups_default.json | 1 +
.../cloudstack/queryAsyncJobResult_1300004.json | 1 +
.../cloudstack/queryAsyncJobResult_1300005.json | 1 +
.../cloudstack/queryAsyncJobResult_1300006.json | 62 ++++++
.../updateVMAffinityGroup_default.json | 1 +
libcloud/test/compute/test_cloudstack.py | 43 +++-
11 files changed, 328 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 7c410c2..094af05 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -42,6 +42,10 @@ Compute
(GITHUB-465)
[Avi Nanhkoesingh]
+- Add affinity group support to CloudStack driver
+ (LIBCLOUD-671, GITHUB-468)
+ [Mateusz Korszun]
+
DNS
~~~
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/compute/drivers/cloudstack.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py
index ef3b4d3..fb1961e 100644
--- a/libcloud/compute/drivers/cloudstack.py
+++ b/libcloud/compute/drivers/cloudstack.py
@@ -1063,6 +1063,85 @@ class CloudStackProject(object):
self.driver.name))
+class CloudStackAffinityGroup(object):
+ """
+ Class representing a CloudStack AffinityGroup.
+ """
+
+ def __init__(self, id, account, description, domain, domainid, name,
+ group_type, virtualmachine_ids):
+ """
+ A CloudStack Affinity Group.
+
+ @note: This is a non-standard extension API, and only works for
+ CloudStack.
+
+ :param id: CloudStack Affinity Group ID
+ :type id: ``str``
+
+ :param account: An account for the affinity group. Must be used
+ with domainId.
+ :type account: ``str``
+
+ :param description: optional description of the affinity group
+ :type description: ``str``
+
+ :param domain: the domain name of the affinity group
+ :type domain: ``str``
+
+ :param domainid: domain ID of the account owning the affinity
+ group
+ :type domainid: ``str``
+
+ :param name: name of the affinity group
+ :type name: ``str``
+
+ :param group_type: the type of the affinity group
+ :type group_type: :class:`CloudStackAffinityGroupType`
+
+ :param virtualmachine_ids: virtual machine Ids associated with
+ this affinity group
+ :type virtualmachine_ids: ``str``
+
+ :rtype: :class:`CloudStackAffinityGroup`
+ """
+ self.id = id
+ self.account = account
+ self.description = description
+ self.domain = domain
+ self.domainid = domainid
+ self.name = name
+ self.type = group_type
+ self.virtualmachine_ids = virtualmachine_ids
+
+ def __repr__(self):
+ return (('<CloudStackAffinityGroup: id=%s, name=%s, type=%s>')
+ % (self.id, self.name, self.type))
+
+
+class CloudStackAffinityGroupType(object):
+ """
+ Class representing a CloudStack AffinityGroupType.
+ """
+
+ def __init__(self, type_name):
+ """
+ A CloudStack Affinity Group Type.
+
+ @note: This is a non-standard extension API, and only works for
+ CloudStack.
+
+ :param type_name: the type of the affinity group
+ :type type_name: ``str``
+
+ :rtype: :class:`CloudStackAffinityGroupType`
+ """
+ self.type = type_name
+
+ def __repr__(self):
+ return (('<CloudStackAffinityGroupType: type=%s>') % self.type)
+
+
class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
"""
Driver for the CloudStack API.
@@ -1313,6 +1392,11 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
:keyword ex_rootdisksize: String with rootdisksize for the template
:type ex_rootdisksize: ``str``
+ :keyword ex_affinity_groups: List of affinity groups to assign to
+ the node
+ :type ex_affinity_groups: ``list`` of
+ :class:`.CloudStackAffinityGroup`
+
:rtype: :class:`.CloudStackNode`
"""
@@ -1342,6 +1426,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
ex_ip_address = kwargs.get('ex_ip_address', None)
ex_start_vm = kwargs.get('ex_start_vm', None)
ex_rootdisksize = kwargs.get('ex_rootdisksize', None)
+ ex_affinity_groups = kwargs.get('ex_affinity_groups', None)
if name:
server_params['name'] = name
@@ -1391,6 +1476,10 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
if ex_start_vm is not None:
server_params['startvm'] = ex_start_vm
+ if ex_affinity_groups:
+ affinity_group_ids = ','.join(ag.id for ag in ex_affinity_groups)
+ server_params['affinitygroupids'] = affinity_group_ids
+
return server_params
def destroy_node(self, node, ex_expunge=False):
@@ -3087,6 +3176,111 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
method='GET')
return True
+ def ex_create_affinity_group(self, name, group_type):
+ """
+ Creates a new Affinity Group
+
+ :param name: Name of the affinity group
+ :type name: ``str``
+
+ :param group_type: Type of the affinity group from the available
+ affinity/anti-affinity group types
+ :type group_type: :class:`CloudStackAffinityGroupType`
+
+ :param description: Optional description of the affinity group
+ :type description: ``str``
+
+ :param domainid: domain ID of the account owning the affinity group
+ :type domainid: ``str``
+
+ :rtype: :class:`CloudStackAffinityGroup`
+ """
+
+ for ag in self.ex_list_affinity_groups():
+ if name == ag.name:
+ raise LibcloudError('This Affinity Group name already exists')
+
+ params = {'name': name, 'type': group_type.type}
+
+ result = self._async_request(command='createAffinityGroup',
+ params=params,
+ method='GET')
+
+ return self._to_affinity_group(result['affinitygroup'])
+
+ def ex_delete_affinity_group(self, affinity_group):
+ """
+ Delete an Affinity Group
+
+ :param affinity_group: Instance of affinity group
+ :type affinity_group: :class:`CloudStackAffinityGroup`
+
+ :rtype ``bool``
+ """
+ return self._async_request(command='deleteAffinityGroup',
+ params={'id': affinity_group.id},
+ method='GET')['success']
+
+ def ex_update_node_affinity_group(self, node, affinity_group_list):
+ """
+ Updates the affinity/anti-affinity group associations of a virtual
+ machine. The VM has to be stopped and restarted for the new properties
+ to take effect.
+
+ :param node: Node to update.
+ :type node: :class:`CloudStackNode`
+
+ :param affinity_group_list: List of CloudStackAffinityGroup to
+ associate
+ :type affinity_group_list: ``list`` of :class:`CloudStackAffinityGroup`
+
+ :rtype :class:`CloudStackNode`
+ """
+ affinity_groups = ','.join(ag.id for ag in affinity_group_list)
+
+ result = self._async_request(command='updateVMAffinityGroup',
+ params={
+ 'id': node.id,
+ 'affinitygroupids': affinity_groups},
+ method='GET')
+ return self._to_node(data=result['virtualmachine'])
+
+ def ex_list_affinity_groups(self):
+ """
+ List Affinity Groups
+
+ :rtype ``list`` of :class:`CloudStackAffinityGroup`
+ """
+ result = self._sync_request(command='listAffinityGroups', method='GET')
+
+ if not result.get('count'):
+ return []
+
+ affinity_groups = []
+ for ag in result['affinitygroup']:
+ affinity_groups.append(self._to_affinity_group(ag))
+
+ return affinity_groups
+
+ def ex_list_affinity_group_types(self):
+ """
+ List Affinity Group Types
+
+ :rtype ``list`` of :class:`CloudStackAffinityGroupTypes`
+ """
+ result = self._sync_request(command='listAffinityGroupTypes',
+ method='GET')
+
+ if not result.get('count'):
+ return []
+
+ affinity_group_types = []
+ for agt in result['affinityGroupType']:
+ affinity_group_types.append(
+ CloudStackAffinityGroupType(agt['type']))
+
+ return affinity_group_types
+
def ex_register_iso(self, name, url, location=None, **kwargs):
"""
Registers an existing ISO by URL.
@@ -3984,6 +4178,11 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
if security_groups:
security_groups = [sg['name'] for sg in security_groups]
+ affinity_groups = data.get('affinitygroup', [])
+
+ if affinity_groups:
+ affinity_groups = [ag['id'] for ag in affinity_groups]
+
created = data.get('created', False)
extra = self._get_extra_dict(data,
@@ -3991,6 +4190,7 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
# Add additional parameters to extra
extra['security_group'] = security_groups
+ extra['affinity_group'] = affinity_groups
extra['ip_addresses'] = []
extra['ip_forwarding_rules'] = []
extra['port_forwarding_rules'] = []
@@ -4016,6 +4216,19 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
driver=self)
return key_pair
+ def _to_affinity_group(self, data):
+ affinity_group = CloudStackAffinityGroup(
+ id=data['id'],
+ name=data['name'],
+ group_type=CloudStackAffinityGroupType(data['type']),
+ account=data.get('account', ''),
+ domain=data.get('domain', ''),
+ domainid=data.get('domainid', ''),
+ description=data.get('description', ''),
+ virtualmachine_ids=data.get('virtualmachineIds', ''))
+
+ return affinity_group
+
def _get_resource_tags(self, tag_set):
"""
Parse tags from the provided element and return a dictionary with
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/createAffinityGroup_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/createAffinityGroup_default.json b/libcloud/test/compute/fixtures/cloudstack/createAffinityGroup_default.json
new file mode 100644
index 0000000..e853e2a
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/createAffinityGroup_default.json
@@ -0,0 +1 @@
+{"createaffinitygroupresponse": { "jobid" : 1300004}}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/deleteAffinityGroup_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/deleteAffinityGroup_default.json b/libcloud/test/compute/fixtures/cloudstack/deleteAffinityGroup_default.json
new file mode 100644
index 0000000..1328cf0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/deleteAffinityGroup_default.json
@@ -0,0 +1 @@
+{"deleteaffinitygroupresponse": { "jobid" : 1300005}}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/listAffinityGroupTypes_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/listAffinityGroupTypes_default.json b/libcloud/test/compute/fixtures/cloudstack/listAffinityGroupTypes_default.json
new file mode 100644
index 0000000..1366745
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/listAffinityGroupTypes_default.json
@@ -0,0 +1 @@
+{ "listaffinitygrouptypesresponse" : {"count": 1, "affinityGroupType": [{"type": "MyAGType"}] }}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/listAffinityGroups_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/listAffinityGroups_default.json b/libcloud/test/compute/fixtures/cloudstack/listAffinityGroups_default.json
new file mode 100644
index 0000000..2955f68
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/listAffinityGroups_default.json
@@ -0,0 +1 @@
+{ "listaffinitygroupsresponse" : {"count": 1, "affinitygroup" : [{"id":"11112", "name": "MyAG", "type": "MyAGType"}] }}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300004.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300004.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300004.json
new file mode 100644
index 0000000..cd4ece0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300004.json
@@ -0,0 +1 @@
+{ "queryasyncjobresultresponse" : {"jobid":1300004,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"affinitygroup" : {"id":"11113", "name": "MyAG2", "type": "MyAGType"} }} }
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300005.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300005.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300005.json
new file mode 100644
index 0000000..8c19147
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300005.json
@@ -0,0 +1 @@
+{ "queryasyncjobresultresponse" : {"jobid":1300005,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"success" : true }} }
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300006.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300006.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300006.json
new file mode 100644
index 0000000..4d022d0
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_1300006.json
@@ -0,0 +1,62 @@
+{ "queryasyncjobresultresponse": {
+ "accountid": "86d47ca2-726b-4b85-a18a-77d6b0d79829",
+ "userid": "20cd68f5-0633-48a5-826e-e4e2a00dd6b8",
+ "jobstatus": 1,
+ "jobprocstatus": 0,
+ "jobresultcode": 0,
+ "jobresulttype": "object",
+ "jobresult": {
+ "virtualmachine": {
+ "id": "19253fbf-abb7-4013-a8a1-97df3b93f206",
+ "name": "TestNode",
+ "projectid": "b90442d1-079b-4066-ab7d-41f8f3a5078b",
+ "project": "Test Project",
+ "domainid": "dc0314d4-09aa-4e8f-8a54-419ecf344635",
+ "domain": "Test Domain",
+ "created": "2014-03-06T15:39:44-0600",
+ "state": "Running",
+ "haenable": false,
+ "zoneid": "d630b15a-a9e1-4641-bee8-355005b7a14d",
+ "zonename": "TestZone",
+ "templateid": "a032e8a0-3411-48b7-9e78-ff66823e6561",
+ "templatename": "OL-6.3.1-64-13.11.01",
+ "templatedisplaytext": "OL-6.3.1-64-13.11.01",
+ "passwordenabled": true,
+ "serviceofferingid": "519f8667-26d0-40e5-a1cd-da04be1fd9b5",
+ "serviceofferingname": "Test Service Offering",
+ "cpunumber": 1,
+ "cpuspeed": 2000,
+ "memory": 2000,
+ "guestosid": "b8506c91-6d8e-4086-8659-f6296a7b71ac",
+ "rootdeviceid": 0,
+ "rootdevicetype": "ROOT",
+ "securitygroup": [],
+ "password": "mW6crjxag",
+ "nic": [
+ {
+ "id": "1c144283-979a-4359-b695-3334dc403457",
+ "networkid": "1bf4acce-19a5-4830-ab1d-444f8acb9986",
+ "networkname": "Public",
+ "netmask": "255.255.252.0",
+ "gateway": "10.1.2.2",
+ "ipaddress": "10.2.2.8",
+ "isolationuri": "vlan://2950",
+ "broadcasturi": "vlan://2950",
+ "traffictype": "Guest",
+ "type": "Shared",
+ "isdefault": true,
+ "macaddress": "06:ef:30:00:04:22"
+ }
+ ],
+ "hypervisor": "VMware",
+ "tags": [],
+ "affinitygroup" : [{"id":"11112", "name": "MyAG", "type": "MyAGType"}],
+ "displayvm": true,
+ "isdynamicallyscalable": false,
+ "jobid": "e23b9f0c-b7ae-4ffe-aea0-c9cf436cc315",
+ "jobstatus": 0
+ }
+ },
+ "created": "2014-03-06T15:39:44-0600",
+ "jobid": "e23b9f5c-b7ae-4ffe-aea0-c9cf436dc315"
+} }
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/fixtures/cloudstack/updateVMAffinityGroup_default.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/cloudstack/updateVMAffinityGroup_default.json b/libcloud/test/compute/fixtures/cloudstack/updateVMAffinityGroup_default.json
new file mode 100644
index 0000000..825364b
--- /dev/null
+++ b/libcloud/test/compute/fixtures/cloudstack/updateVMAffinityGroup_default.json
@@ -0,0 +1 @@
+{"updatevmaffinitygroupresponse": { "jobid" : 1300006}}
http://git-wip-us.apache.org/repos/asf/libcloud/blob/52d96c7f/libcloud/test/compute/test_cloudstack.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_cloudstack.py b/libcloud/test/compute/test_cloudstack.py
index 5726a75..8eff8a9 100644
--- a/libcloud/test/compute/test_cloudstack.py
+++ b/libcloud/test/compute/test_cloudstack.py
@@ -26,7 +26,8 @@ except ImportError:
import json
from libcloud.common.types import ProviderError
-from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver
+from libcloud.compute.drivers.cloudstack import CloudStackNodeDriver, \
+ CloudStackAffinityGroupType
from libcloud.compute.types import LibcloudError, Provider, InvalidCredsError
from libcloud.compute.types import KeyPairDoesNotExistError
from libcloud.compute.types import NodeState
@@ -736,6 +737,46 @@ class CloudStackCommonTestCase(TestCaseMixin):
'0.0.0.0/0')
self.assertTrue(res)
+ def test_ex_create_affinity_group(self):
+ res = self.driver.ex_create_affinity_group('MyAG2',
+ CloudStackAffinityGroupType('MyAGType'))
+ self.assertEqual(res.name, 'MyAG2')
+ self.assertIsInstance(res.type, CloudStackAffinityGroupType)
+ self.assertEqual(res.type.type, 'MyAGType')
+
+ def test_ex_create_affinity_group_already_exists(self):
+ self.assertRaises(LibcloudError,
+ self.driver.ex_create_affinity_group,
+ 'MyAG', CloudStackAffinityGroupType('MyAGType'))
+
+ def test_delete_ex_affinity_group(self):
+ afg = self.driver.ex_create_affinity_group('MyAG3',
+ CloudStackAffinityGroupType('MyAGType'))
+ res = self.driver.ex_delete_affinity_group(afg)
+ self.assertTrue(res)
+
+ def test_ex_update_node_affinity_group(self):
+ affinity_group_list = self.driver.ex_list_affinity_groups()
+ nodes = self.driver.list_nodes()
+ node = self.driver.ex_update_node_affinity_group(nodes[0],
+ affinity_group_list)
+ self.assertEqual(node.extra['affinity_group'][0],
+ affinity_group_list[0].id)
+
+ def test_ex_list_affinity_groups(self):
+ res = self.driver.ex_list_affinity_groups()
+ self.assertEqual(len(res), 1)
+ self.assertEqual(res[0].id, '11112')
+ self.assertEqual(res[0].name, 'MyAG')
+ self.assertIsInstance(res[0].type, CloudStackAffinityGroupType)
+ self.assertEqual(res[0].type.type, 'MyAGType')
+
+ def test_ex_list_affinity_group_types(self):
+ res = self.driver.ex_list_affinity_group_types()
+ self.assertEqual(len(res), 1)
+ self.assertIsInstance(res[0], CloudStackAffinityGroupType)
+ self.assertEqual(res[0].type, 'MyAGType')
+
def test_ex_list_public_ips(self):
ips = self.driver.ex_list_public_ips()
self.assertEqual(ips[0].address, '1.1.1.116')