Merge pull request #1569 from godaddy/allowed-address-pairs

Support updating allowed_address_pairs on OpenStack ports
diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py
index a4cc7cc..1116c0b 100644
--- a/libcloud/compute/drivers/openstack.py
+++ b/libcloud/compute/drivers/openstack.py
@@ -3408,7 +3408,8 @@
     def ex_update_port(self, port, description=None,
                        admin_state_up=None, name=None,
                        port_security_enabled=None,
-                       qos_policy_id=None, security_groups=None):
+                       qos_policy_id=None, security_groups=None,
+                       allowed_address_pairs=None):
         """
         Update a OpenStack_2_PortInterface
 
@@ -3434,6 +3435,13 @@
         :param      security_groups: The IDs of security groups applied
         :type       security_groups: ``list`` of ``str``
 
+        :param      allowed_address_pairs: IP and MAC address that the port
+                    can use when sending packets if port_security_enabled is
+                    true
+        :type       allowed_address_pairs: ``list`` of ``dict`` containing
+                    ip_address and mac_address; mac_address is optional, taken
+                    from the port if not specified
+
         :rtype: :class:`OpenStack_2_PortInterface`
         """
         data = {'port': {}}
@@ -3449,6 +3457,8 @@
             data['port']['qos_policy_id'] = qos_policy_id
         if security_groups is not None:
             data['port']['security_groups'] = security_groups
+        if allowed_address_pairs is not None:
+            data['port']['allowed_address_pairs'] = allowed_address_pairs
         response = self.network_connection.request(
             '/v2.0/ports/{}'.format(port.id), method='PUT', data=data
         )
diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_port_v2.json b/libcloud/test/compute/fixtures/openstack_v1.1/_port_v2.json
index a86a8e6..52cae05 100644
--- a/libcloud/test/compute/fixtures/openstack_v1.1/_port_v2.json
+++ b/libcloud/test/compute/fixtures/openstack_v1.1/_port_v2.json
@@ -3,7 +3,7 @@
     "status": "BUILD",
     "extra_dhcp_opts": [],
     "description": "Some port description",
-    "allowed_address_pairs": [],
+    "allowed_address_pairs": [{"ip_address": "1.2.3.4"}, {"ip_address": "2.3.4.5"}],
     "tags": [],
     "network_id": "123c8a8c-6427-4e8f-a805-2035365f4d43",
     "tenant_id": "abcdec85bee34bb0a44ab8255eb36abc",
diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py
index d86ca70..fe6f298 100644
--- a/libcloud/test/compute/test_openstack.py
+++ b/libcloud/test/compute/test_openstack.py
@@ -1979,6 +1979,14 @@
         ret = self.driver.ex_update_port(port, port_security_enabled=False)
         self.assertEqual(ret.extra['name'], 'Some port name')
 
+    def test_ex_update_port_allowed_address_pairs(self):
+        allowed_address_pairs = [{'ip_address': '1.2.3.4'},
+                                 {'ip_address': '2.3.4.5'}]
+        port = self.driver.ex_get_port('126da55e-cfcb-41c8-ae39-a26cb8a7e723')
+        ret = self.driver.ex_update_port(
+            port, allowed_address_pairs=allowed_address_pairs)
+        self.assertEqual(ret.extra['allowed_address_pairs'], allowed_address_pairs)
+
     def test_detach_port_interface(self):
         node = Node(id='1c01300f-ef97-4937-8f03-ac676d6234be', name=None,
                     state=None, public_ips=None, private_ips=None,