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')

Reply via email to