summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nova/db/sqlalchemy/api.py17
-rw-r--r--nova/pci/manager.py1
-rw-r--r--nova/pci/stats.py26
-rw-r--r--nova/tests/unit/db/test_db_api.py8
-rw-r--r--nova/tests/unit/pci/test_stats.py24
-rw-r--r--releasenotes/notes/bug-1892361-pci-deivce-type-update-c407a66fd37f6405.yaml12
6 files changed, 87 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):
diff --git a/releasenotes/notes/bug-1892361-pci-deivce-type-update-c407a66fd37f6405.yaml b/releasenotes/notes/bug-1892361-pci-deivce-type-update-c407a66fd37f6405.yaml
new file mode 100644
index 0000000000..ba35c25b02
--- /dev/null
+++ b/releasenotes/notes/bug-1892361-pci-deivce-type-update-c407a66fd37f6405.yaml
@@ -0,0 +1,12 @@
+---
+fixes:
+ - |
+ Fixes `bug 1892361`_ in which the pci stat pools are not updated when an
+ existing device is enabled with SRIOV capability. Restart of nova-compute
+ service updates the pci device type from type-PCI to type-PF but the pools
+ still maintain the device type as type-PCI. And so the PF is considered for
+ allocation to instance that requests vnic_type=direct. With this fix, the
+ pci device type updates are detected and the pci stat pools are updated
+ properly.
+
+ .. _bug 1892361: https://bugs.launchpad.net/nova/+bug/1892361