diff --git a/nova/api/openstack/compute/floating_ips.py b/nova/api/openstack/compute/floating_ips.py index e79bb46221bc..a5862e2b9d9d 100644 --- a/nova/api/openstack/compute/floating_ips.py +++ b/nova/api/openstack/compute/floating_ips.py @@ -267,6 +267,8 @@ class FloatingIPActionController(wsgi.Controller): except exception.FloatingIpAssociated: msg = _('floating IP is already associated') raise webob.exc.HTTPBadRequest(explanation=msg) + except exception.FloatingIpAssociateFailed as e: + raise webob.exc.HTTPBadRequest(explanation=e.format_message()) except exception.NoFloatingIpInterface: msg = _('l3driver call to add floating IP failed') raise webob.exc.HTTPBadRequest(explanation=msg) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 5984806c5afe..42d916a2bc49 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -1730,7 +1730,10 @@ class API(base_api.NetworkAPI): fip = self._get_floating_ip_by_address(client, floating_address) param = {'port_id': port_id, 'fixed_ip_address': fixed_address} - client.update_floatingip(fip['id'], {'floatingip': param}) + try: + client.update_floatingip(fip['id'], {'floatingip': param}) + except neutron_client_exc.Conflict as e: + raise exception.FloatingIpAssociateFailed(six.text_type(e)) if fip['port_id']: port = self._show_port(context, fip['port_id'], diff --git a/nova/tests/unit/api/openstack/compute/test_floating_ips.py b/nova/tests/unit/api/openstack/compute/test_floating_ips.py index 1cb214142dd2..0fc45b6f7287 100644 --- a/nova/tests/unit/api/openstack/compute/test_floating_ips.py +++ b/nova/tests/unit/api/openstack/compute/test_floating_ips.py @@ -634,6 +634,15 @@ class FloatingIpTestV21(test.TestCase): self.manager._add_floating_ip, self.fake_req, TEST_INST, body=body) + @mock.patch.object(network.api.API, 'associate_floating_ip', + side_effect=exception.FloatingIpAssociateFailed( + address='10.10.10.11')) + def test_associate_floating_ip_failed(self, associate_mock): + body = dict(addFloatingIp=dict(address='10.10.10.11')) + self.assertRaises(webob.exc.HTTPBadRequest, + self.manager._add_floating_ip, self.fake_req, + TEST_INST, body=body) + def test_associate_floating_ip_bad_address_key(self): body = dict(addFloatingIp=dict(bad_address='10.10.10.11')) req = fakes.HTTPRequest.blank('/v2/fake/servers/test_inst/action') diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index 18bb9c0aa224..9f99638bd8ab 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -4783,6 +4783,36 @@ class TestNeutronv2WithMock(test.TestCase): self.context, pci_requests, requested_networks) self.assertFalse(getclient.called) + @mock.patch.object(neutronapi, 'get_client') + def test_associate_floating_ip_conflict(self, mock_get_client): + """Tests that if Neutron raises a Conflict we handle it and re-raise + as a nova-specific exception. + """ + mock_get_client.return_value.update_floatingip.side_effect = ( + exceptions.Conflict( + "Cannot associate floating IP 172.24.5.15 " + "(60a8f00b-4404-4518-ad66-00448a155904) with port " + "95ee1ffb-6d41-447d-a90e-b6ce5d9c92fa using fixed IP " + "10.1.0.9, as that fixed IP already has a floating IP on " + "external network bdcda645-f612-40ab-a956-0d95af42cf7c.") + ) + with test.nested( + mock.patch.object( + self.api, '_get_port_id_by_fixed_address', + return_value='95ee1ffb-6d41-447d-a90e-b6ce5d9c92fa'), + mock.patch.object( + self.api, '_get_floating_ip_by_address', + return_value={'id': uuids.floating_ip_id}) + ) as ( + _get_floating_ip_by_address, _get_port_id_by_fixed_address + ): + instance = fake_instance.fake_instance_obj( + self.context, uuid='2a2200ec-02fe-484e-885b-9bae7b21ecba') + self.assertRaises(exception.FloatingIpAssociateFailed, + self.api.associate_floating_ip, + self.context, instance, + '172.24.5.15', '10.1.0.9') + class TestNeutronv2ModuleMethods(test.NoDBTestCase):