diff options
Diffstat (limited to 'nova')
-rw-r--r-- | nova/db/sqlalchemy/api.py | 17 | ||||
-rw-r--r-- | nova/pci/manager.py | 1 | ||||
-rw-r--r-- | nova/pci/stats.py | 26 | ||||
-rw-r--r-- | nova/tests/unit/db/test_db_api.py | 8 | ||||
-rw-r--r-- | nova/tests/unit/pci/test_stats.py | 24 |
5 files changed, 75 insertions, 1 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ac577b873e..c571128591 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -49,6 +49,7 @@ from sqlalchemy.orm import contains_eager from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload_all from sqlalchemy.orm import noload +from sqlalchemy.orm import subqueryload from sqlalchemy.orm import undefer from sqlalchemy.schema import Table from sqlalchemy import sql @@ -1919,13 +1920,27 @@ def _build_instance_get(context, columns_to_join=None): continue if 'extra.' in column: query = query.options(undefer(column)) + elif column in ['metadata', 'system_metadata']: + # NOTE(melwitt): We use subqueryload() instead of joinedload() for + # metadata and system_metadata because of the one-to-many + # relationship of the data. Directly joining these columns can + # result in a large number of additional rows being queried if an + # instance has a large number of (system_)metadata items, resulting + # in a large data transfer. Instead, the subqueryload() will + # perform additional queries to obtain metadata and system_metadata + # for the instance. + query = query.options(subqueryload(column)) else: query = query.options(joinedload(column)) # NOTE(alaski) Stop lazy loading of columns not needed. for col in ['metadata', 'system_metadata']: if col not in columns_to_join: query = query.options(noload(col)) - return query + # NOTE(melwitt): We need to use order_by(<unique column>) so that the + # additional queries emitted by subqueryload() include the same ordering as + # used by the parent query. + # https://docs.sqlalchemy.org/en/13/orm/loading_relationships.html#the-importance-of-ordering + return query.order_by(models.Instance.id) def _instances_fill_metadata(context, instances, manual_joins=None): diff --git a/nova/pci/manager.py b/nova/pci/manager.py index ff161e38ca..3e8ed3ee25 100644 --- a/nova/pci/manager.py +++ b/nova/pci/manager.py @@ -225,6 +225,7 @@ class PciDevTracker(object): self.stale[new_value['address']] = new_value else: existed.update_device(new_value) + self.stats.update_device(existed) # Track newly discovered devices. for dev in [dev for dev in devices if diff --git a/nova/pci/stats.py b/nova/pci/stats.py index 40736ef726..9f8d249050 100644 --- a/nova/pci/stats.py +++ b/nova/pci/stats.py @@ -97,6 +97,32 @@ class PciDeviceStats(object): pool.update(tags) return pool + def _get_pool_with_device_type_mismatch(self, dev): + """Check for device type mismatch in the pools for a given device. + + Return (pool, device) if device type does not match or a single None + if the device type matches. + """ + for pool in self.pools: + for device in pool['devices']: + if device.address == dev.address: + if dev.dev_type != pool["dev_type"]: + return pool, device + return None + + return None + + def update_device(self, dev): + """Update a device to its matching pool.""" + pool_device_info = self._get_pool_with_device_type_mismatch(dev) + if pool_device_info is None: + return + + pool, device = pool_device_info + pool['devices'].remove(device) + self._decrease_pool_count(self.pools, pool) + self.add_device(dev) + def add_device(self, dev): """Add a device to its matching pool.""" dev_pool = self._create_pool_keys_from_dev(dev) diff --git a/nova/tests/unit/db/test_db_api.py b/nova/tests/unit/db/test_db_api.py index 37d11880ac..fe4452f835 100644 --- a/nova/tests/unit/db/test_db_api.py +++ b/nova/tests/unit/db/test_db_api.py @@ -2372,6 +2372,14 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin): sys_meta = utils.metadata_to_dict(inst['system_metadata']) self.assertEqual(sys_meta, self.sample_data['system_metadata']) + def test_instance_get_with_meta(self): + inst_id = self.create_instance_with_args().id + inst = db.instance_get(self.ctxt, inst_id) + meta = utils.metadata_to_dict(inst['metadata']) + self.assertEqual(meta, self.sample_data['metadata']) + sys_meta = utils.metadata_to_dict(inst['system_metadata']) + self.assertEqual(sys_meta, self.sample_data['system_metadata']) + def test_instance_update(self): instance = self.create_instance_with_args() metadata = {'host': 'bar', 'key2': 'wuff'} diff --git a/nova/tests/unit/pci/test_stats.py b/nova/tests/unit/pci/test_stats.py index be867783fb..0375ccc63f 100644 --- a/nova/tests/unit/pci/test_stats.py +++ b/nova/tests/unit/pci/test_stats.py @@ -533,6 +533,30 @@ class PciDeviceStatsWithTagsTestCase(test.NoDBTestCase): self.pci_stats.remove_device(dev2) self._assertPools() + def test_update_device(self): + # Update device type of one of the device from type-PCI to + # type-PF. Verify if the existing pool is updated and a new + # pool is created with dev_type type-PF. + self._create_pci_devices() + dev1 = self.pci_tagged_devices.pop() + dev1.dev_type = 'type-PF' + self.pci_stats.update_device(dev1) + self.assertEqual(3, len(self.pci_stats.pools)) + self._assertPoolContent(self.pci_stats.pools[0], '1137', '0072', + len(self.pci_untagged_devices)) + self.assertEqual(self.pci_untagged_devices, + self.pci_stats.pools[0]['devices']) + self._assertPoolContent(self.pci_stats.pools[1], '1137', '0071', + len(self.pci_tagged_devices), + physical_network='physnet1') + self.assertEqual(self.pci_tagged_devices, + self.pci_stats.pools[1]['devices']) + self._assertPoolContent(self.pci_stats.pools[2], '1137', '0071', + 1, + physical_network='physnet1') + self.assertEqual(dev1, + self.pci_stats.pools[2]['devices'][0]) + class PciDeviceVFPFStatsTestCase(test.NoDBTestCase): |