diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5a796fe2d41a..57c64309c3d4 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1026,6 +1026,7 @@ def floating_ip_update(context, address, values): float_ip_ref.save(session=session) except db_exc.DBDuplicateEntry: raise exception.FloatingIpExists(address=values['address']) + return float_ip_ref def _dnsdomain_get(context, session, fqdomain): diff --git a/nova/objects/floating_ip.py b/nova/objects/floating_ip.py index 1bd2c2ea0132..9b811fc92b89 100644 --- a/nova/objects/floating_ip.py +++ b/nova/objects/floating_ip.py @@ -124,6 +124,14 @@ class FloatingIP(obj_base.NovaPersistentObject, obj_base.NovaObject): if 'address' in updates: raise exception.ObjectActionError(action='save', reason='address is not mutable') + if 'fixed_ip_id' in updates: + reason = 'fixed_ip_id is not mutable' + raise exception.ObjectActionError(action='save', reason=reason) + + # NOTE(danms): Make sure we don't pass the calculated fixed_ip + # relationship to the DB update method + updates.pop('fixed_ip', None) + db_floatingip = db.floating_ip_update(context, str(self.address), updates) self._from_db_object(context, self, db_floatingip) diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 5d5d1d945b01..a5737533a2b1 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -4075,7 +4075,9 @@ class FloatingIpTestCase(test.TestCase, ModelsObjectComparatorMixin): 'interface': 'some_interface', 'pool': 'some_pool' } - db.floating_ip_update(self.ctxt, float_ip['address'], values) + floating_ref = db.floating_ip_update(self.ctxt, float_ip['address'], + values) + self.assertIsNot(floating_ref, None) updated_float_ip = db.floating_ip_get(self.ctxt, float_ip['id']) self._assertEqualObjects(updated_float_ip, values, ignored_keys=['id', 'address', 'updated_at', diff --git a/nova/tests/objects/test_floating_ip.py b/nova/tests/objects/test_floating_ip.py index 41c9cb038cdd..a4b84050105f 100644 --- a/nova/tests/objects/test_floating_ip.py +++ b/nova/tests/objects/test_floating_ip.py @@ -16,6 +16,7 @@ import mock import netaddr from nova import exception +from nova.objects import fixed_ip from nova.objects import floating_ip from nova.tests.objects import test_fixed_ip from nova.tests.objects import test_network @@ -129,13 +130,32 @@ class _TestFloatingIPObject(object): floatingip = floating_ip.FloatingIP(context=self.context, id=123, address='1.2.3.4', host='foo') - self.assertRaises(exception.ObjectActionError, floatingip.save) floatingip.obj_reset_changes(['address', 'id']) floatingip.save() self.assertEqual(set(), floatingip.obj_what_changed()) update.assert_called_with(self.context, '1.2.3.4', {'host': 'foo'}) + def test_save_errors(self): + floatingip = floating_ip.FloatingIP(context=self.context, + id=123, host='foo') + floatingip.obj_reset_changes() + floating_ip.address = '1.2.3.4' + self.assertRaises(exception.ObjectActionError, floatingip.save) + + floatingip.obj_reset_changes() + floatingip.fixed_ip_id = 1 + self.assertRaises(exception.ObjectActionError, floatingip.save) + + @mock.patch('nova.db.floating_ip_update') + def test_save_no_fixedip(self, update): + update.return_value = fake_floating_ip + floatingip = floating_ip.FloatingIP(context=self.context, + id=123) + floatingip.fixed_ip = fixed_ip.FixedIP(context=self.context, + id=456) + self.assertNotIn('fixed_ip', update.calls[1]) + @mock.patch('nova.db.floating_ip_get_all') def test_get_all(self, get): get.return_value = [fake_floating_ip]