diff --git a/nova/objects/virt_device_metadata.py b/nova/objects/virt_device_metadata.py index 3fb17e52501d..5437ce6b65b6 100644 --- a/nova/objects/virt_device_metadata.py +++ b/nova/objects/virt_device_metadata.py @@ -12,12 +12,14 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_serialization import jsonutils +from nova import db from nova.objects import base from nova.objects import fields -@base.NovaObjectRegistry.register_if(False) +@base.NovaObjectRegistry.register class DeviceBus(base.NovaObject): VERSION = '1.0' @@ -88,9 +90,29 @@ class DiskMetadata(DeviceMetadata): @base.NovaObjectRegistry.register -class DeviceMetadataList(base.ObjectListBase, base.NovaObject): +class InstanceDeviceMetadata(base.NovaObject): VERSION = '1.0' fields = { - 'objects': fields.ListOfObjectsField('DeviceMetadata', - subclasses=True), + 'devices': fields.ListOfObjectsField('DeviceMetadata', + subclasses=True), } + + @classmethod + def obj_from_db(cls, context, db_requests): + primitive = jsonutils.loads(db_requests) + device_metadata = cls.obj_from_primitive(primitive) + return device_metadata + + @base.remotable_classmethod + def get_by_instance_uuid(cls, context, instance_uuid): + db_extra = db.instance_extra_get_by_instance_uuid( + context, instance_uuid, columns=['device_metadata']) + if not db_extra or db_extra['device_metadata'] is None: + return None + + primitive = jsonutils.loads(db_extra['device_metadata']) + device_metadata = cls.obj_from_primitive(primitive) + return device_metadata + + def _to_json(self): + return jsonutils.dumps(self.obj_to_primitive()) diff --git a/nova/tests/unit/objects/test_instance_device_metadata.py b/nova/tests/unit/objects/test_instance_device_metadata.py new file mode 100644 index 000000000000..a4ac411e037c --- /dev/null +++ b/nova/tests/unit/objects/test_instance_device_metadata.py @@ -0,0 +1,91 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from oslo_serialization import jsonutils + +from nova import objects +from nova.tests.unit.objects import test_objects + +fake_net_interface_meta = objects.NetworkInterfaceMetadata( + mac='52:54:00:f6:35:8f', + tags=['mytag1'], + bus=objects.PCIDeviceBus(address='0000:00:03.0')) +fake_pci_disk_meta = objects.DiskMetadata( + bus=objects.PCIDeviceBus(address='0000:00:09.0'), + tags=['nfvfunc3']) +fake_obj_devices_metadata = objects.InstanceDeviceMetadata( + devices=[fake_net_interface_meta, fake_pci_disk_meta]) + +fake_devices_metadata = fake_obj_devices_metadata._to_json() + +fake_db_metadata = { + 'created_at': None, + 'updated_at': None, + 'deleted_at': None, + 'deleted': 0, + 'id': 1, + 'device_metadata': fake_obj_devices_metadata._to_json() + } + +fake_old_db_metadata = dict(fake_db_metadata) # copy +fake_old_db_metadata['device_metadata'] = jsonutils.dumps( + fake_devices_metadata) + + +def get_fake_obj_device_metadata(context): + fake_obj_devices_metadata_cpy = fake_obj_devices_metadata.obj_clone() + fake_obj_devices_metadata_cpy._context = context + return fake_obj_devices_metadata_cpy + + +class _TestInstanceDeviceMetadata(object): + def _check_object(self, obj_meta): + self.assertTrue(isinstance(obj_meta, + objects.NetworkInterfaceMetadata) or + isinstance(obj_meta, objects.DiskMetadata)) + if isinstance(obj_meta, objects.NetworkInterfaceMetadata): + self.assertEqual(obj_meta.mac, '52:54:00:f6:35:8f') + self.assertEqual(obj_meta.tags, ['mytag1']) + self.assertTrue(obj_meta.bus, objects.PCIDeviceBus) + self.assertEqual(obj_meta.bus.address, '0000:00:03.0') + elif isinstance(obj_meta, objects.DiskMetadata): + self.assertTrue(obj_meta.bus, objects.PCIDeviceBus) + self.assertEqual(obj_meta.bus.address, '0000:00:09.0') + self.assertEqual(obj_meta.tags, ['nfvfunc3']) + + @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_metadata + inst_meta = objects.InstanceDeviceMetadata + dev_meta = inst_meta.get_by_instance_uuid( + self.context, 'fake_uuid') + for obj_meta, fake_meta in zip( + dev_meta.devices, + fake_obj_devices_metadata.devices): + self._check_object(obj_meta) + + def test_obj_from_db(self): + db_meta = fake_db_metadata['device_metadata'] + metadata = objects.InstanceDeviceMetadata.obj_from_db(None, db_meta) + for obj_meta in metadata.devices: + self._check_object(obj_meta) + + +class TestInstanceDeviceMetadata(test_objects._LocalTest, + _TestInstanceDeviceMetadata): + pass + + +class TestInstanceDeviceMetadataRemote(test_objects._RemoteTest, + _TestInstanceDeviceMetadata): + pass diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 94640ffed5f9..d73fc7e0f90a 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1111,8 +1111,8 @@ object_data = { 'DNSDomain': '1.0-7b0b2dab778454b6a7b6c66afe163a1a', 'DNSDomainList': '1.0-4ee0d9efdfd681fed822da88376e04d2', 'Destination': '1.0-4c59dd1288b2e7adbda6051a2de59183', + 'DeviceBus': '1.0-77509ea1ea0dd750d5864b9bd87d3f9d', 'DeviceMetadata': '1.0-04eb8fd218a49cbc3b1e54b774d179f7', - 'DeviceMetadataList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e', 'DiskMetadata': '1.0-e7a0f1ccccf10d26a76b28e7492f3788', 'EC2Ids': '1.0-474ee1094c7ec16f8ce657595d8c49d9', 'EC2InstanceMapping': '1.0-a4556eb5c5e94c045fe84f49cf71644f', @@ -1135,6 +1135,7 @@ object_data = { 'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33', 'InstanceActionEventList': '1.1-13d92fb953030cdbfee56481756e02be', 'InstanceActionList': '1.0-4a53826625cc280e15fae64a575e0879', + 'InstanceDeviceMetadata': '1.0-74d78dd36aa32d26d2769a1b57caf186', 'InstanceExternalEvent': '1.1-6e446ceaae5f475ead255946dd443417', 'InstanceFault': '1.2-7ef01f16f1084ad1304a513d6d410a38', 'InstanceFaultList': '1.1-f8ec07cbe3b60f5f07a8b7a06311ac0d',