diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 9fe4f7cbcc..72d085d8c9 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -376,8 +376,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject): numa_topology = updates.pop('numa_topology', None) if numa_topology: expected_attrs.append('numa_topology') - updates['extra']['numa_topology'] = ( - numa_topology.topology_from_obj().to_json()) + updates['extra']['numa_topology'] = numa_topology._to_json() pci_requests = updates.pop('pci_requests', None) if pci_requests: expected_attrs.append('pci_requests') diff --git a/nova/objects/instance_numa_topology.py b/nova/objects/instance_numa_topology.py index 422169d1af..324b4394f7 100644 --- a/nova/objects/instance_numa_topology.py +++ b/nova/objects/instance_numa_topology.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.serialization import jsonutils + from nova import db from nova import exception from nova.objects import base @@ -47,13 +49,19 @@ class InstanceNUMATopology(base.NovaObject): @classmethod def obj_from_db_obj(cls, instance_uuid, db_obj): - topo = hardware.VirtNUMAInstanceTopology.from_json(db_obj) - obj_topology = cls.obj_from_topology(topo) - obj_topology.instance_uuid = instance_uuid - # NOTE (ndipanov) not really needed as we never save, but left for - # consistency - obj_topology.id = 0 - obj_topology.obj_reset_changes() + if 'nova_object.name' in db_obj: + obj_topology = cls.obj_from_primitive( + jsonutils.loads(db_obj)) + else: + # NOTE(sahid): This compatibility code needs to stay until we can + # guarantee that there are no cases of the old format stored in + # the database (or forever, if we can never guarantee that). + topo = hardware.VirtNUMAInstanceTopology.from_json(db_obj) + obj_topology = cls.obj_from_topology(topo) + obj_topology.instance_uuid = instance_uuid + + # No benefit to store a list of changed fields + obj_topology.obj_reset_changes() return obj_topology @classmethod @@ -81,22 +89,13 @@ class InstanceNUMATopology(base.NovaObject): # TODO(ndipanov) Remove this method on the major version bump to 2.0 @base.remotable def create(self, context): - topology = self.topology_from_obj() - if not topology: - return - values = {'numa_topology': topology.to_json()} - db.instance_extra_update_by_uuid(context, self.instance_uuid, - values) - self.obj_reset_changes() + self._save(context) # NOTE(ndipanov): We can't rename create and want to avoid version bump # as this needs to be backported to stable so this is not a @remotable # That's OK since we only call it from inside Instance.save() which is. def _save(self, context): - topology = self.topology_from_obj() - if not topology: - return - values = {'numa_topology': topology.to_json()} + values = {'numa_topology': self._to_json()} db.instance_extra_update_by_uuid(context, self.instance_uuid, values) self.obj_reset_changes() @@ -121,3 +120,6 @@ class InstanceNUMATopology(base.NovaObject): return None return cls.obj_from_db_obj(instance_uuid, db_extra['numa_topology']) + + def _to_json(self): + return jsonutils.dumps(self.obj_to_primitive()) diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 0e26681925..e5acd44449 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -30,7 +30,6 @@ from nova import notifications from nova import objects from nova.objects import instance from nova.objects import instance_info_cache -from nova.objects import instance_numa_topology from nova.objects import pci_device from nova.objects import security_group from nova import test @@ -419,17 +418,19 @@ class _TestInstanceObject(object): @mock.patch('nova.objects.Instance._from_db_object') def test_save_updates_numa_topology(self, mock_fdo, mock_update, mock_extra_update): + fake_obj_numa_topology = objects.InstanceNUMATopology(cells=[ + objects.InstanceNUMACell(id=0, cpuset=set([0]), memory=128), + objects.InstanceNUMACell(id=1, cpuset=set([1]), memory=128)]) + fake_obj_numa_topology.instance_uuid = 'fake-uuid' + jsonified = fake_obj_numa_topology._to_json() + mock_update.return_value = None, None inst = instance.Instance( context=self.context, id=123, uuid='fake-uuid') - inst.numa_topology = ( - instance_numa_topology.InstanceNUMATopology.obj_from_topology( - test_instance_numa_topology.fake_numa_topology)) + inst.numa_topology = fake_obj_numa_topology inst.save() mock_extra_update.assert_called_once_with( - self.context, inst.uuid, - {'numa_topology': - test_instance_numa_topology.fake_numa_topology.to_json()}) + self.context, inst.uuid, {'numa_topology': jsonified}) mock_extra_update.reset_mock() inst.numa_topology = None inst.save() @@ -707,19 +708,18 @@ class _TestInstanceObject(object): def test_create_with_extras(self): inst = instance.Instance(uuid=self.fake_instance['uuid'], - numa_topology=objects.InstanceNUMATopology.obj_from_topology( - test_instance_numa_topology.fake_numa_topology), - pci_requests=objects.InstancePCIRequests( - requests=[ - objects.InstancePCIRequest(count=123, - spec=[])])) - + numa_topology=test_instance_numa_topology.fake_obj_numa_topology, + pci_requests=objects.InstancePCIRequests( + requests=[ + objects.InstancePCIRequest(count=123, + spec=[])])) inst.create(self.context) self.assertIsNotNone(inst.numa_topology) self.assertIsNotNone(inst.pci_requests) got_numa_topo = objects.InstanceNUMATopology.get_by_instance_uuid( self.context, inst.uuid) - self.assertEqual(inst.numa_topology.id, got_numa_topo.id) + self.assertEqual(inst.numa_topology.instance_uuid, + got_numa_topo.instance_uuid) got_pci_requests = objects.InstancePCIRequests.get_by_instance_uuid( self.context, inst.uuid) self.assertEqual(123, got_pci_requests.requests[0].count) diff --git a/nova/tests/unit/objects/test_instance_numa_topology.py b/nova/tests/unit/objects/test_instance_numa_topology.py index afb9872ad7..26aa71e1ab 100644 --- a/nova/tests/unit/objects/test_instance_numa_topology.py +++ b/nova/tests/unit/objects/test_instance_numa_topology.py @@ -19,11 +19,16 @@ from nova import objects from nova.tests.unit.objects import test_objects from nova.virt import hardware +fake_instance_uuid = str(uuid.uuid4()) fake_numa_topology = hardware.VirtNUMAInstanceTopology( - cells=[hardware.VirtNUMATopologyCellInstance( - 0, set([1, 2]), 512, 2048), - hardware.VirtNUMATopologyCellInstance( - 1, set([3, 4]), 512, 2048)]) + cells=[hardware.VirtNUMATopologyCellInstance( + 0, set([1, 2]), 512, 2048), + hardware.VirtNUMATopologyCellInstance( + 1, set([3, 4]), 512, 2048)]) + +fake_obj_numa_topology = objects.InstanceNUMATopology.obj_from_topology( + fake_numa_topology) +fake_obj_numa_topology.instance_uuid = fake_instance_uuid fake_db_topology = { 'created_at': None, @@ -31,10 +36,13 @@ fake_db_topology = { 'deleted_at': None, 'deleted': 0, 'id': 1, - 'instance_uuid': str(uuid.uuid4()), - 'numa_topology': fake_numa_topology.to_json() + 'instance_uuid': fake_instance_uuid, + 'numa_topology': fake_obj_numa_topology._to_json() } +fake_old_db_topology = dict(fake_db_topology) # copy +fake_old_db_topology['numa_topology'] = fake_numa_topology.to_json() + class _TestInstanceNUMATopology(object): @mock.patch('nova.db.instance_extra_update_by_uuid') @@ -45,9 +53,15 @@ class _TestInstanceNUMATopology(object): topo_obj.create(self.context) self.assertEqual(1, len(mock_update.call_args_list)) - @mock.patch('nova.db.instance_extra_get_by_instance_uuid') - def test_get_by_instance_uuid(self, mock_get): - mock_get.return_value = fake_db_topology + @mock.patch('nova.db.instance_extra_update_by_uuid') + def test_save(self, mock_update): + topo_obj = objects.InstanceNUMATopology.obj_from_topology( + fake_numa_topology) + topo_obj.instance_uuid = fake_db_topology['instance_uuid'] + topo_obj._save(self.context) + self.assertEqual(1, len(mock_update.call_args_list)) + + def _test_get_by_instance_uuid(self): numa_topology = objects.InstanceNUMATopology.get_by_instance_uuid( self.context, fake_db_topology['instance_uuid']) self.assertEqual(fake_db_topology['instance_uuid'], @@ -59,6 +73,16 @@ class _TestInstanceNUMATopology(object): self.assertEqual(topo_cell.memory, obj_cell.memory) self.assertEqual(topo_cell.pagesize, obj_cell.pagesize) + @mock.patch('nova.db.instance_extra_get_by_instance_uuid') + def test_get_by_instance_uuid(self, mock_get): + mock_get.return_value = fake_db_topology + self._test_get_by_instance_uuid() + + @mock.patch('nova.db.instance_extra_get_by_instance_uuid') + def test_get_by_instance_uuid_old(self, mock_get): + mock_get.return_value = fake_old_db_topology + self._test_get_by_instance_uuid() + @mock.patch('nova.db.instance_extra_get_by_instance_uuid') def test_get_by_instance_uuid_missing(self, mock_get): mock_get.return_value = None