Repository: libcloud Updated Branches: refs/heads/trunk d3b039795 -> 43d8cd680
CLOUDSTACK: Add ex_list_nics, ex_attach_nic_to_vm, ex_detach_nic_from_vm + tests Signed-off-by: Sebastien Goasguen <[email protected]> This closes #369 Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/43d8cd68 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/43d8cd68 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/43d8cd68 Branch: refs/heads/trunk Commit: 43d8cd680b9f09615e61506d18eecec12bccbee1 Parents: d3b0397 Author: Roeland Kuipers <[email protected]> Authored: Fri Oct 3 19:15:26 2014 +0200 Committer: Sebastien Goasguen <[email protected]> Committed: Wed Nov 5 14:22:36 2014 +0100 ---------------------------------------------------------------------- CHANGES.rst | 4 + libcloud/compute/drivers/cloudstack.py | 125 ++++++++++++++++++- .../addNicToVirtualMachine_default.json | 1 + .../fixtures/cloudstack/listNics_default.json | 1 + .../queryAsyncJobResult_addnictovm.json | 37 ++++++ .../queryAsyncJobResult_removenic.json | 37 ++++++ .../removeNicFromVirtualMachine_default.json | 1 + libcloud/test/compute/test_cloudstack.py | 40 ++++++ 8 files changed, 244 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index da45e6c..9026450 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -171,6 +171,10 @@ Compute (GITHUB-325) [Michael Bennett] +- Add methods in CloudStack driver to manage mutiple nics per vm. + (GITHUB-369) + [Roeland Kuipers] + Storage ~~~~~~~ http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/compute/drivers/cloudstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/cloudstack.py b/libcloud/compute/drivers/cloudstack.py index fa10fb1..454f1e7 100644 --- a/libcloud/compute/drivers/cloudstack.py +++ b/libcloud/compute/drivers/cloudstack.py @@ -181,6 +181,10 @@ RESOURCE_EXTRA_ATTRIBUTES_MAP = { 'project_id': { 'key_name': 'projectid', 'transform_func': str + }, + 'nics:': { + 'key_name': 'nic', + 'transform_func': list } }, 'volume': { @@ -302,6 +306,12 @@ RESOURCE_EXTRA_ATTRIBUTES_MAP = { 'vpcavailable': {'key_name': 'vpcavailable', 'transform_func': int}, 'vpclimit': {'key_name': 'vpclimit', 'transform_func': int}, 'vpctotal': {'key_name': 'vpctotal', 'transform_func': int} + }, + 'nic': { + 'secondary_ip': { + 'key_name': 'secondaryip', + 'transform_func': list + } } } @@ -654,6 +664,35 @@ class CloudStackNetworkOffering(object): self.driver.name)) +class CloudStackNic(object): + """ + Class representing a CloudStack Network Interface. + """ + + def __init__(self, id, network_id, net_mask, gateway, ip_address, + is_default, mac_address, driver, extra=None): + self.id = id + self.network_id = network_id + self.net_mask = net_mask + self.gateway = gateway + self.ip_address = ip_address + self.is_default = is_default + self.mac_address = mac_address + self.driver = driver + self.extra = extra or {} + + def __repr__(self): + return (('<CloudStackNic: id=%s, network_id=%s, ' + 'net_mask=%s, gateway=%s, ip_address=%s, ' + 'is_default=%s, mac_address=%s, driver%s>') + % (self.id, self.network_id, self.net_mask, + self.gateway, self.ip_address, self.is_default, + self.mac_address, self.driver.name)) + + def __eq__(self, other): + return self.__class__ is other.__class__ and self.id == other.id + + class CloudStackVPC(object): """ Class representing a CloudStack VPC. @@ -1189,10 +1228,10 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): :param name: the name of the network :type name: ``str`` - :param network_offering: the network offering id + :param network_offering: NetworkOffering object :type network_offering: :class:'CloudStackNetworkOffering` - :param location: Zone + :param location: Zone object :type location: :class:`NodeLocation` :param gateway: Optional, the Gateway of this network @@ -2714,6 +2753,88 @@ class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver): ostypes = self._sync_request('listOsTypes') return ostypes['ostype'] + def ex_list_nics(self, node): + """ + List the available networks + + :param vm: Node Object + :type vm: :class:`CloudStackNode + + :rtype ``list`` of :class:`CloudStackNic` + """ + + res = self._sync_request(command='listNics', + params={'virtualmachineid': node.id}, + method='GET') + items = res.get('nic', []) + + nics = [] + extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['nic'] + for item in items: + extra = self._get_extra_dict(item, extra_map) + + nics.append(CloudStackNic( + id=item['id'], + network_id=item['networkid'], + net_mask=item['netmask'], + gateway=item['gateway'], + ip_address=item['ipaddress'], + is_default=item['isdefault'], + mac_address=item['macaddress'], + driver=self, + extra=extra)) + + return nics + + def ex_attach_nic_to_node(self, node, network, ip_address=None): + """ + Add an extra Nic to a VM + + :param network: NetworkOffering object + :type network: :class:'CloudStackNetwork` + + :param node: Node Object + :type node: :class:'CloudStackNode` + + :param ip_address: Optional, specific IP for this Nic + :type ip_address: ``str`` + + + :rtype: ``bool`` + """ + + args = { + 'virtualmachineid': node.id, + 'networkid': network.id + } + + if ip_address is not None: + args['ipaddress'] = ip_address + + self._async_request(command='addNicToVirtualMachine', + params=args) + return True + + def ex_detach_nic_from_node(self, nic, node): + + """ + Remove Nic from a VM + + :param nic: Nic object + :type nic: :class:'CloudStackNetwork` + + :param node: Node Object + :type node: :class:'CloudStackNode` + + :rtype: ``bool`` + """ + + self._async_request(command='removeNicFromVirtualMachine', + params={'nicid': nic.id, + 'virtualmachineid': node.id}) + + return True + def _to_snapshot(self, data): """ Create snapshot object from data http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/fixtures/cloudstack/addNicToVirtualMachine_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/addNicToVirtualMachine_default.json b/libcloud/test/compute/fixtures/cloudstack/addNicToVirtualMachine_default.json new file mode 100644 index 0000000..3816acb --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/addNicToVirtualMachine_default.json @@ -0,0 +1 @@ +{ "addnictovirtualmachineresponse" : {"jobid":"addnictovm"} } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/fixtures/cloudstack/listNics_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/listNics_default.json b/libcloud/test/compute/fixtures/cloudstack/listNics_default.json new file mode 100644 index 0000000..4c8b18e --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/listNics_default.json @@ -0,0 +1 @@ +{ "listnicsresponse" : { "count":1 ,"nic" : [ {"id":"15418e74-25e8-42d3-9bd7-eb55e57825fe","networkid":"de45b0ed-b5ae-4374-ac7c-aff3fb2aefa2","netmask":"255.255.255.0","gateway":"10.1.1.1","ipaddress":"10.1.1.136","isdefault":true,"macaddress":"02:00:00:b9:01:1a"} ] } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_addnictovm.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_addnictovm.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_addnictovm.json new file mode 100644 index 0000000..12b01cd --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_addnictovm.json @@ -0,0 +1,37 @@ +{ + "queryasyncjobresultresponse" : { + "accountid" : "02c9bf08-6f36-44b1-a57f-df0708f90de4", "userid" : "6ef2b921-4ecf-4651-8188-f9868db73e73", "cmd" : "org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd", "jobstatus" : 1, "jobprocstatus" : 0, "jobresultcode" : 0, "jobresulttype" : "object", "jobresult" : { + "virtualmachine" : { + "id" : "903897b7-9241-4f93-bd08-3453c36a3c99", "name" : "test", "account" : "rkuipers_admin", "domainid" : "4b6e626c-9d50-4480-bf77-daae632c7ffd", "domain" : "rkuipers", "created" : "2014-10-03T17:24:37+0200", "state" : "Stopped", "haenable" : false, "zoneid" : "2", "zonename" : "BETA-SBP-DC-1", "guestosid" : "278699da-edfc-11e2-a249-005056ba4c5e", "securitygroup" : [], "nic" : [{ + "id": "15418e74-25e8-42d3-9bd7-eb55e57825fe", + "networkid": "de45b0ed-b5ae-4374-ac7c-aff3fb2aefa2", + "networkname": "rkuipers-default", + "netmask": "255.255.255.0", + "gateway": "10.1.1.1", + "ipaddress": "10.1.1.136", + "isolationuri": "lswitch:d3eaa05c-4392-4918-93ab-c4a979a7988a", + "broadcasturi": "lswitch:d3eaa05c-4392-4918-93ab-c4a979a7988a", + "traffictype": "Guest", + "type": "Isolated", + "isdefault": true, + "macaddress": "02:00:00:b9:01:1a" + }, { + "id": "f71e48da-fe40-458d-9033-ea27a3a92de1", + "networkid": "f59ee08a-66b5-40c6-baa7-392394c6cea9", + "networkname": "node_cellar_network", + "netmask": "255.255.255.0", + "gateway": "10.1.1.1", + "ipaddress": "10.1.1.253", + "isolationuri": "lswitch:bad1ca9f-b039-4ad1-b5fc-1f3147fad7fa", + "broadcasturi": "lswitch:bad1ca9f-b039-4ad1-b5fc-1f3147fad7fa", + "traffictype": "Guest", + "type": "Isolated", + "isdefault": false, + "macaddress": "02:00:4f:84:00:1d" + } + ], "hypervisor" : "XenServer", "tags" : [], "affinitygroup" : [], "displayvm" : true, "isdynamicallyscalable" : false + } + }, "created" : "2014-10-03T18:30:59+0200", "jobid" : "e521e748-1271-4587-8433-2d094704bfe9" + } +} + http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_removenic.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_removenic.json b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_removenic.json new file mode 100644 index 0000000..3f0d556 --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/queryAsyncJobResult_removenic.json @@ -0,0 +1,37 @@ +{ + "queryasyncjobresultresponse" : { + "accountid" : "02c9bf08-6f36-44b1-a57f-df0708f90de4", "userid" : "6ef2b921-4ecf-4651-8188-f9868db73e73", "cmd" : "org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd", "jobstatus" : 1, "jobprocstatus" : 0, "jobresultcode" : 0, "jobresulttype" : "object", "jobresult" : { + "virtualmachine" : { + "id" : "903897b7-9241-4f93-bd08-3453c36a3c99", "name" : "test", "account" : "rkuipers_admin", "domainid" : "4b6e626c-9d50-4480-bf77-daae632c7ffd", "domain" : "rkuipers", "created" : "2014-10-03T17:24:37+0200", "state" : "Stopped", "haenable" : false, "zoneid" : "2", "zonename" : "BETA-SBP-DC-1", "guestosid" : "278699da-edfc-11e2-a249-005056ba4c5e", "securitygroup" : [], "nic" : [{ + "id": "15418e74-25e8-42d3-9bd7-eb55e57825fe", + "networkid": "de45b0ed-b5ae-4374-ac7c-aff3fb2aefa2", + "networkname": "rkuipers-default", + "netmask": "255.255.255.0", + "gateway": "10.1.1.1", + "ipaddress": "10.1.1.136", + "isolationuri": "lswitch:d3eaa05c-4392-4918-93ab-c4a979a7988a", + "broadcasturi": "lswitch:d3eaa05c-4392-4918-93ab-c4a979a7988a", + "traffictype": "Guest", + "type": "Isolated", + "isdefault": true, + "macaddress": "02:00:00:b9:01:1a" + }, { + "id": "f71e48da-fe40-458d-9033-ea27a3a92de1", + "networkid": "f59ee08a-66b5-40c6-baa7-392394c6cea9", + "networkname": "node_cellar_network", + "netmask": "255.255.255.0", + "gateway": "10.1.1.1", + "ipaddress": "10.1.1.253", + "isolationuri": "lswitch:bad1ca9f-b039-4ad1-b5fc-1f3147fad7fa", + "broadcasturi": "lswitch:bad1ca9f-b039-4ad1-b5fc-1f3147fad7fa", + "traffictype": "Guest", + "type": "Isolated", + "isdefault": false, + "macaddress": "02:00:4f:84:00:1d" + } + ], "hypervisor" : "XenServer", "tags" : [], "affinitygroup" : [], "displayvm" : true, "isdynamicallyscalable" : false + } + }, "created" : "2014-10-03T18:40:30+0200", "jobid" : "23fc6bfc-e354-41c8-bf8b-c0db8227feb4" + } +} + http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/fixtures/cloudstack/removeNicFromVirtualMachine_default.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/cloudstack/removeNicFromVirtualMachine_default.json b/libcloud/test/compute/fixtures/cloudstack/removeNicFromVirtualMachine_default.json new file mode 100644 index 0000000..ccada15 --- /dev/null +++ b/libcloud/test/compute/fixtures/cloudstack/removeNicFromVirtualMachine_default.json @@ -0,0 +1 @@ +{ "removenicfromvirtualmachineresponse" : {"jobid":"removenic"} } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/43d8cd68/libcloud/test/compute/test_cloudstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_cloudstack.py b/libcloud/test/compute/test_cloudstack.py index 387695f..2164323 100644 --- a/libcloud/test/compute/test_cloudstack.py +++ b/libcloud/test/compute/test_cloudstack.py @@ -318,6 +318,46 @@ class CloudStackCommonTestCase(TestCaseMixin): result = self.driver.ex_delete_network(network=network) self.assertTrue(result) + def test_ex_list_nics(self): + _, fixture = CloudStackMockHttp()._load_fixture( + 'listNics_default.json') + + fixture_nic = fixture['listnicsresponse']['nic'] + vm = self.driver.list_nodes()[0] + nics = self.driver.ex_list_nics(vm) + + for i, nic in enumerate(nics): + self.assertEqual(nic.id, fixture_nic[i]['id']) + self.assertEqual(nic.network_id, + fixture_nic[i]['networkid']) + self.assertEqual(nic.net_mask, + fixture_nic[i]['netmask']) + self.assertEqual(nic.gateway, + fixture_nic[i]['gateway']) + self.assertEqual(nic.ip_address, + fixture_nic[i]['ipaddress']) + self.assertEqual(nic.is_default, + fixture_nic[i]['isdefault']) + self.assertEqual(nic.mac_address, + fixture_nic[i]['macaddress']) + + def test_ex_add_nic_to_node(self): + + vm = self.driver.list_nodes()[0] + network = self.driver.ex_list_networks()[0] + ip = "10.1.4.123" + + result = self.driver.ex_attach_nic_to_node(node=vm, network=network, ip_address=ip) + self.assertTrue(result) + + def test_ex_remove_nic_from_node(self): + + vm = self.driver.list_nodes()[0] + nic = self.driver.ex_list_nics(node=vm)[0] + + result = self.driver.ex_detach_nic_from_node(node=vm, nic=nic) + self.assertTrue(result) + def test_ex_list_vpc_offerings(self): _, fixture = CloudStackMockHttp()._load_fixture( 'listVPCOfferings_default.json')
