From a59e1a9c7e54efaadc39d366772972463855dfc7 Mon Sep 17 00:00:00 2001 From: Nikola Dipanov Date: Tue, 18 Nov 2014 20:45:28 +0100 Subject: [PATCH] Make Instance.save() update numa_topology This is needed so that we can actually update the given topology with the updated data after a successful claim. Deleting it will also be needed when we actually make the resize work properly for instances with NUMA topology, so we add it here as well. We do not expose the new InstanceNUMATopology methods as @remotable to avoid having to bump the object version thus making this an easier backport target. This is OK since they are only called from Instance.save() which is @remotable, and can be trivially made remotable should this be needed later (causing a version bump that need not be backported). Change-Id: I64ff2d00ca20bd065bb17ebaa9c40b64b8cbb817 Partial-bug: #1386236 --- nova/objects/instance.py | 8 ++++++-- nova/objects/instance_numa_topology.py | 22 ++++++++++++++++++++++ nova/tests/unit/objects/test_instance.py | 23 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 3ac84c8f44c2..7b65ebc23b9d 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -424,8 +424,12 @@ class Instance(base.NovaPersistentObject, base.NovaObject): pass def _save_numa_topology(self, context): - # NOTE(ndipanov): No need for this yet. - pass + if self.numa_topology: + self.numa_topology.instance_uuid = self.uuid + self.numa_topology._save(context) + else: + objects.InstanceNUMATopology.delete_by_instance_uuid( + context, self.uuid) def _save_pci_requests(self, context): # NOTE(danms): No need for this yet. diff --git a/nova/objects/instance_numa_topology.py b/nova/objects/instance_numa_topology.py index 62f8091db969..5c12b1b943db 100644 --- a/nova/objects/instance_numa_topology.py +++ b/nova/objects/instance_numa_topology.py @@ -85,6 +85,7 @@ class InstanceNUMATopology(base.NovaObject): cells.append(cell) return hardware.VirtNUMAInstanceTopology(cells=cells) + # TODO(ndipanov) Remove this method on the major version bump to 2.0 @base.remotable def create(self, context): topology = self.topology_from_obj() @@ -95,6 +96,27 @@ class InstanceNUMATopology(base.NovaObject): values) self.obj_reset_changes() + # 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()} + db.instance_extra_update_by_uuid(context, self.instance_uuid, + values) + self.obj_reset_changes() + + # NOTE(ndipanov): We 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. + @classmethod + def delete_by_instance_uuid(cls, context, instance_uuid): + values = {'numa_topology': None} + db.instance_extra_update_by_uuid(context, instance_uuid, + values) + @base.remotable_classmethod def get_by_instance_uuid(cls, context, instance_uuid): db_extra = db.instance_extra_get_by_instance_uuid( diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 44d7d31f9f6c..0e26681925bd 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -30,6 +30,7 @@ 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 @@ -413,6 +414,28 @@ class _TestInstanceObject(object): self.assertNotIn('pci_devices', mock_fdo.call_args_list[0][1]['expected_attrs']) + @mock.patch('nova.db.instance_extra_update_by_uuid') + @mock.patch('nova.db.instance_update_and_get_original') + @mock.patch('nova.objects.Instance._from_db_object') + def test_save_updates_numa_topology(self, mock_fdo, mock_update, + mock_extra_update): + 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.save() + mock_extra_update.assert_called_once_with( + self.context, inst.uuid, + {'numa_topology': + test_instance_numa_topology.fake_numa_topology.to_json()}) + mock_extra_update.reset_mock() + inst.numa_topology = None + inst.save() + mock_extra_update.assert_called_once_with( + self.context, inst.uuid, {'numa_topology': None}) + def test_get_deleted(self): fake_inst = dict(self.fake_instance, id=123, deleted=123) fake_uuid = fake_inst['uuid']