From c5dfd2c3ef773b29666fae3fe75bf7548044dbf5 Mon Sep 17 00:00:00 2001 From: Andrew Boik Date: Sun, 29 Mar 2015 10:18:48 -0400 Subject: [PATCH] Associate floating IPs with first v4 fixed IP if none specified In the absence of a specified fixed address with which to associate a floating IP, the first IPv4 address on the port should be associated. Without the check for IPv4, IPv6 ports can be associated with a (IPv4) floating IP, which is not supported. Change-Id: Ib66a9109cc1c7999474daca5970d0af1f70886e4 Closes-Bug: 1437855 --- .../openstack/compute/contrib/floating_ips.py | 16 +++++++++++--- .../compute/plugins/v3/floating_ips.py | 16 +++++++++++--- .../compute/contrib/test_floating_ips.py | 22 ++++++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 74bed5aae8..0be23ebfc2 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import netaddr from oslo_log import log as logging from oslo_utils import uuidutils import webob @@ -200,10 +201,19 @@ class FloatingIPActionController(wsgi.Controller): raise webob.exc.HTTPBadRequest(explanation=msg) if not fixed_address: - fixed_address = fixed_ips[0]['address'] + try: + fixed_address = (ip['address'] for ip in fixed_ips if + netaddr.valid_ipv4(ip['address'])).next() + except StopIteration: + msg = _('Unable to associate floating ip %(address)s ' + 'to any fixed IPs for instance %(id)s. ' + 'Instance has no fixed IPv4 addresses to ' + 'associate.') % ( + {'address': address, 'id': id}) + raise webob.exc.HTTPBadRequest(explanation=msg) if len(fixed_ips) > 1: - LOG.warning(_LW('multiple fixed_ips exist, using the first: ' - '%s'), fixed_address) + LOG.warning(_LW('multiple fixed_ips exist, using the first ' + 'IPv4 fixed_ip: %s'), fixed_address) try: self.network_api.associate_floating_ip(context, instance, diff --git a/nova/api/openstack/compute/plugins/v3/floating_ips.py b/nova/api/openstack/compute/plugins/v3/floating_ips.py index 5e7e8ed332..11a0e8d0aa 100644 --- a/nova/api/openstack/compute/plugins/v3/floating_ips.py +++ b/nova/api/openstack/compute/plugins/v3/floating_ips.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import netaddr from oslo_log import log as logging from oslo_utils import uuidutils import webob @@ -212,10 +213,19 @@ class FloatingIPActionController(wsgi.Controller): raise webob.exc.HTTPBadRequest(explanation=msg) if not fixed_address: - fixed_address = fixed_ips[0]['address'] + try: + fixed_address = (ip['address'] for ip in fixed_ips if + netaddr.valid_ipv4(ip['address'])).next() + except StopIteration: + msg = _('Unable to associate floating ip %(address)s ' + 'to any fixed IPs for instance %(id)s. ' + 'Instance has no fixed IPv4 addresses to ' + 'associate.') % ( + {'address': address, 'id': id}) + raise webob.exc.HTTPBadRequest(explanation=msg) if len(fixed_ips) > 1: - LOG.warning(_LW('multiple fixed_ips exist, using the first: ' - '%s'), fixed_address) + LOG.warning(_LW('multiple fixed_ips exist, using the first ' + 'IPv4 fixed_ip: %s'), fixed_address) try: self.network_api.associate_floating_ip(context, instance, diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_floating_ips.py b/nova/tests/unit/api/openstack/compute/contrib/test_floating_ips.py index b5f7514fdb..110b3b0ebc 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_floating_ips.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_floating_ips.py @@ -481,9 +481,7 @@ class FloatingIpTestV21(test.TestCase): def test_floating_ip_release(self): self.controller.delete(self.fake_req, 1) - def test_floating_ip_associate(self): - fixed_address = '192.168.1.100' - + def _test_floating_ip_associate(self, fixed_address): def fake_associate_floating_ip(*args, **kwargs): self.assertEqual(fixed_address, kwargs['fixed_address']) @@ -495,6 +493,24 @@ class FloatingIpTestV21(test.TestCase): body=body) self.assertEqual(202, rsp.status_int) + def test_floating_ip_associate(self): + self._test_floating_ip_associate(fixed_address='192.168.1.100') + + @mock.patch.object(network.model.NetworkInfo, 'fixed_ips') + def test_associate_floating_ip_v4v6_fixed_ip(self, fixed_ips_mock): + fixed_address = '192.168.1.100' + fixed_ips_mock.return_value = [{'address': 'fc00:2001:db8::100'}, + {'address': fixed_address}] + self._test_floating_ip_associate(fixed_address=fixed_address) + + @mock.patch.object(network.model.NetworkInfo, 'fixed_ips', + return_value=[{'address': 'fc00:2001:db8::100'}]) + def test_associate_floating_ip_v6_fixed_ip(self, fixed_ips_mock): + body = dict(addFloatingIp=dict(address=self.floating_ip)) + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._add_floating_ip, self.fake_req, + TEST_INST, body=body) + def test_floating_ip_associate_invalid_instance(self): def fake_get(self, context, id, expected_attrs=None,