Skip to content

Commit 176eebc

Browse files
authored
Merge pull request #42 from stackhpc/upstream/xena-2023-04-10
Synchronise xena with upstream
2 parents 4511d5d + 706ee60 commit 176eebc

File tree

10 files changed

+225
-19
lines changed

10 files changed

+225
-19
lines changed

nova/db/main/api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4332,6 +4332,12 @@ def _get_fk_stmts(metadata, conn, table, column, records):
43324332
fk_column = fk_table.c.id
43334333

43344334
for fk in fk_table.foreign_keys:
4335+
if table != fk.column.table:
4336+
# if the foreign key doesn't actually point to the table we're
4337+
# archiving entries from then it's not relevant; trying to
4338+
# resolve this would result in a cartesian product
4339+
continue
4340+
43354341
# We need to find the records in the referring (child) table that
43364342
# correspond to the records in our (parent) table so we can archive
43374343
# them.
@@ -4378,6 +4384,7 @@ def _get_fk_stmts(metadata, conn, table, column, records):
43784384
# deque.
43794385
fk_delete = fk_table.delete().where(fk_column.in_(fk_records))
43804386
deletes.appendleft(fk_delete)
4387+
43814388
# Repeat for any possible nested child tables.
43824389
i, d = _get_fk_stmts(metadata, conn, fk_table, fk_column, fk_records)
43834390
inserts.extendleft(i)

nova/objects/cell_mapping.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,15 @@ def _get_by_project_id_from_db(context, project_id):
279279
# SELECT DISTINCT cell_id FROM instance_mappings \
280280
# WHERE project_id = $project_id;
281281
cell_ids = context.session.query(
282-
api_db_models.InstanceMapping.cell_id).filter_by(
283-
project_id=project_id).distinct().subquery()
282+
api_db_models.InstanceMapping.cell_id
283+
).filter_by(
284+
project_id=project_id
285+
).distinct()
284286
# SELECT cell_mappings WHERE cell_id IN ($cell_ids);
285-
return context.session.query(api_db_models.CellMapping).filter(
286-
api_db_models.CellMapping.id.in_(cell_ids)).all()
287+
return context.session.query(
288+
api_db_models.CellMapping).filter(
289+
api_db_models.CellMapping.id.in_(cell_ids)
290+
).all()
287291

288292
@classmethod
289293
def get_by_project_id(cls, context, project_id):

nova/tests/fixtures/nova.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,16 @@ def setUp(self):
833833

834834
self.addCleanup(warnings.resetwarnings)
835835

836+
# Enable general SQLAlchemy warnings also to ensure we're not doing
837+
# silly stuff. It's possible that we'll need to filter things out here
838+
# with future SQLAlchemy versions, but that's a good thing
839+
840+
warnings.filterwarnings(
841+
'error',
842+
module='nova',
843+
category=sqla_exc.SAWarning,
844+
)
845+
836846

837847
class ConfPatcher(fixtures.Fixture):
838848
"""Fixture to patch and restore global CONF.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
from oslo_utils import uuidutils
15+
16+
17+
from nova.tests.fixtures import libvirt as fakelibvirt
18+
from nova.tests.functional.libvirt import test_vgpu
19+
from nova.virt.libvirt import utils as libvirt_utils
20+
21+
22+
class VGPUTestsLibvirt7_7(test_vgpu.VGPUTestBase):
23+
24+
def _create_mdev(self, physical_device, mdev_type, uuid=None):
25+
# We need to fake the newly created sysfs object by adding a new
26+
# FakeMdevDevice in the existing persisted Connection object so
27+
# when asking to get the existing mdevs, we would see it.
28+
if not uuid:
29+
uuid = uuidutils.generate_uuid()
30+
mdev_name = libvirt_utils.mdev_uuid2name(uuid)
31+
libvirt_parent = self.pci2libvirt_address(physical_device)
32+
33+
# Libvirt 7.7 now creates mdevs with a parent_addr suffix.
34+
new_mdev_name = '_'.join([mdev_name, libvirt_parent])
35+
36+
# Here, we get the right compute thanks by the self.current_host that
37+
# was modified just before
38+
connection = self.computes[
39+
self._current_host].driver._host.get_connection()
40+
connection.mdev_info.devices.update(
41+
{mdev_name: fakelibvirt.FakeMdevDevice(dev_name=new_mdev_name,
42+
type_id=mdev_type,
43+
parent=libvirt_parent)})
44+
return uuid
45+
46+
def setUp(self):
47+
super(VGPUTestsLibvirt7_7, self).setUp()
48+
extra_spec = {"resources:VGPU": "1"}
49+
self.flavor = self._create_flavor(extra_spec=extra_spec)
50+
51+
# Start compute1 supporting only nvidia-11
52+
self.flags(
53+
enabled_mdev_types=fakelibvirt.NVIDIA_11_VGPU_TYPE,
54+
group='devices')
55+
56+
self.compute1 = self.start_compute_with_vgpu('host1')
57+
58+
def test_create_servers_with_vgpu(self):
59+
60+
# Create a single instance against a specific compute node.
61+
self._create_server(
62+
image_uuid='155d900f-4e14-4e4c-a73d-069cbf4541e6',
63+
flavor_id=self.flavor, host=self.compute1.host,
64+
networks='auto', expected_state='ACTIVE')
65+
66+
self.assert_mdev_usage(self.compute1, expected_amount=1)
67+
68+
self._create_server(
69+
image_uuid='155d900f-4e14-4e4c-a73d-069cbf4541e6',
70+
flavor_id=self.flavor, host=self.compute1.host,
71+
networks='auto', expected_state='ACTIVE')
72+
73+
self.assert_mdev_usage(self.compute1, expected_amount=2)

nova/tests/unit/virt/libvirt/test_config.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3135,6 +3135,32 @@ def test_config_mdev_device(self):
31353135
config.LibvirtConfigNodeDeviceMdevInformation)
31363136
self.assertEqual("nvidia-11", obj.mdev_information.type)
31373137
self.assertEqual(12, obj.mdev_information.iommu_group)
3138+
self.assertIsNone(obj.mdev_information.uuid)
3139+
3140+
def test_config_mdev_device_uuid(self):
3141+
xmlin = """
3142+
<device>
3143+
<name>mdev_b2107403_110c_45b0_af87_32cc91597b8a_0000_41_00_0</name>
3144+
<path>/sys/devices/pci0000:40/0000:40:03.1/0000:41:00.0/b2107403-110c-45b0-af87-32cc91597b8a</path>
3145+
<parent>pci_0000_41_00_0</parent>
3146+
<driver>
3147+
<name>vfio_mdev</name>
3148+
</driver>
3149+
<capability type='mdev'>
3150+
<type id='nvidia-442'/>
3151+
<uuid>b2107403-110c-45b0-af87-32cc91597b8a</uuid>
3152+
<iommuGroup number='57'/>
3153+
</capability>
3154+
</device>"""
3155+
3156+
obj = config.LibvirtConfigNodeDevice()
3157+
obj.parse_str(xmlin)
3158+
self.assertIsInstance(obj.mdev_information,
3159+
config.LibvirtConfigNodeDeviceMdevInformation)
3160+
self.assertEqual("nvidia-442", obj.mdev_information.type)
3161+
self.assertEqual(57, obj.mdev_information.iommu_group)
3162+
self.assertEqual("b2107403-110c-45b0-af87-32cc91597b8a",
3163+
obj.mdev_information.uuid)
31383164

31393165
def test_config_vdpa_device(self):
31403166
xmlin = """

nova/virt/libvirt/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3289,6 +3289,7 @@ def __init__(self, **kwargs):
32893289
root_name="capability", **kwargs)
32903290
self.type = None
32913291
self.iommu_group = None
3292+
self.uuid = None
32923293

32933294
def parse_dom(self, xmldoc):
32943295
super(LibvirtConfigNodeDeviceMdevInformation,
@@ -3298,6 +3299,8 @@ def parse_dom(self, xmldoc):
32983299
self.type = c.get('id')
32993300
if c.tag == "iommuGroup":
33003301
self.iommu_group = int(c.get('number'))
3302+
if c.tag == "uuid":
3303+
self.uuid = c.text
33013304

33023305

33033306
class LibvirtConfigGuestRng(LibvirtConfigGuestDevice):

nova/virt/libvirt/driver.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7790,15 +7790,52 @@ def _get_mdev_capable_devices(self, types=None):
77907790

77917791
def _get_mediated_device_information(self, devname):
77927792
"""Returns a dict of a mediated device."""
7793-
virtdev = self._host.device_lookup_by_name(devname)
7793+
# LP #1951656 - In Libvirt 7.7, the mdev name now includes the PCI
7794+
# address of the parent device (e.g. mdev_<uuid>_<pci_address>) due to
7795+
# the mdevctl allowing for multiple mediated devs having the same UUID
7796+
# defined (only one can be active at a time). Since the guest
7797+
# information doesn't have the parent ID, try to lookup which
7798+
# mediated device is available that matches the UUID. If multiple
7799+
# devices are found that match the UUID, then this is an error
7800+
# condition.
7801+
try:
7802+
virtdev = self._host.device_lookup_by_name(devname)
7803+
except libvirt.libvirtError as ex:
7804+
if ex.get_error_code() != libvirt.VIR_ERR_NO_NODE_DEVICE:
7805+
raise
7806+
mdevs = [dev for dev in self._host.list_mediated_devices()
7807+
if dev.startswith(devname)]
7808+
# If no matching devices are found, simply raise the original
7809+
# exception indicating that no devices are found.
7810+
if not mdevs:
7811+
raise
7812+
elif len(mdevs) > 1:
7813+
msg = ("The mediated device name %(devname)s refers to a UUID "
7814+
"that is present in multiple libvirt mediated devices. "
7815+
"Matching libvirt mediated devices are %(devices)s. "
7816+
"Mediated device UUIDs must be unique for Nova." %
7817+
{'devname': devname,
7818+
'devices': ', '.join(mdevs)})
7819+
raise exception.InvalidLibvirtMdevConfig(reason=msg)
7820+
7821+
LOG.debug('Found requested device %s as %s. Using that.',
7822+
devname, mdevs[0])
7823+
virtdev = self._host.device_lookup_by_name(mdevs[0])
77947824
xmlstr = virtdev.XMLDesc(0)
77957825
cfgdev = vconfig.LibvirtConfigNodeDevice()
77967826
cfgdev.parse_str(xmlstr)
7827+
# Starting with Libvirt 7.3, the uuid information is available in the
7828+
# node device information. If its there, use that. Otherwise,
7829+
# fall back to the previous behavior of parsing the uuid from the
7830+
# devname.
7831+
if cfgdev.mdev_information.uuid:
7832+
mdev_uuid = cfgdev.mdev_information.uuid
7833+
else:
7834+
mdev_uuid = libvirt_utils.mdev_name2uuid(cfgdev.name)
77977835

77987836
device = {
77997837
"dev_id": cfgdev.name,
7800-
# name is like mdev_00ead764_fdc0_46b6_8db9_2963f5c815b4
7801-
"uuid": libvirt_utils.mdev_name2uuid(cfgdev.name),
7838+
"uuid": mdev_uuid,
78027839
# the physical GPU PCI device
78037840
"parent": cfgdev.parent,
78047841
"type": cfgdev.mdev_information.type,

nova/virt/libvirt/host.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,7 @@ def list_mdev_capable_devices(self, flags=0):
14171417
def list_mediated_devices(self, flags=0):
14181418
"""Lookup mediated devices.
14191419
1420-
:returns: a list of virNodeDevice instance
1420+
:returns: a list of strings with the name of the instance
14211421
"""
14221422
return self._list_devices("mdev", flags=flags)
14231423

nova/virt/libvirt/utils.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -577,17 +577,31 @@ def get_default_machine_type(arch: str) -> ty.Optional[str]:
577577

578578

579579
def mdev_name2uuid(mdev_name: str) -> str:
580-
"""Convert an mdev name (of the form mdev_<uuid_with_underscores>) to a
581-
uuid (of the form 8-4-4-4-12).
580+
"""Convert an mdev name (of the form mdev_<uuid_with_underscores> or
581+
mdev_<uuid_with_underscores>_<pciaddress>) to a uuid
582+
(of the form 8-4-4-4-12).
583+
584+
:param mdev_name: the name of the mdev to parse the UUID from
585+
:returns: string containing the uuid
582586
"""
583-
return str(uuid.UUID(mdev_name[5:].replace('_', '-')))
587+
mdev_uuid = mdev_name[5:].replace('_', '-')
588+
# Unconditionnally remove the PCI address from the name
589+
mdev_uuid = mdev_uuid[:36]
590+
return str(uuid.UUID(mdev_uuid))
591+
584592

593+
def mdev_uuid2name(mdev_uuid: str, parent: str = None) -> str:
594+
"""Convert an mdev uuid (of the form 8-4-4-4-12) and optionally its parent
595+
device to a name (of the form mdev_<uuid_with_underscores>[_<pciid>]).
585596
586-
def mdev_uuid2name(mdev_uuid: str) -> str:
587-
"""Convert an mdev uuid (of the form 8-4-4-4-12) to a name (of the form
588-
mdev_<uuid_with_underscores>).
597+
:param mdev_uuid: the uuid of the mediated device
598+
:param parent: the parent device id for the mediated device
599+
:returns: name of the mdev to reference in libvirt
589600
"""
590-
return "mdev_" + mdev_uuid.replace('-', '_')
601+
name = "mdev_" + mdev_uuid.replace('-', '_')
602+
if parent and parent.startswith('pci_'):
603+
name = name + parent[4:]
604+
return name
591605

592606

593607
def get_flags_by_flavor_specs(flavor: 'objects.Flavor') -> ty.Set[str]:

nova/weights.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919

2020
import abc
2121

22+
from oslo_log import log as logging
23+
2224
from nova import loadables
2325

2426

27+
LOG = logging.getLogger(__name__)
28+
29+
2530
def normalize(weight_list, minval=None, maxval=None):
2631
"""Normalize the values in a list between 0 and 1.0.
2732
@@ -127,13 +132,40 @@ def get_weighed_objects(self, weighers, obj_list, weighing_properties):
127132
for weigher in weighers:
128133
weights = weigher.weigh_objects(weighed_objs, weighing_properties)
129134

135+
LOG.debug(
136+
"%s: raw weights %s",
137+
weigher.__class__.__name__,
138+
{(obj.obj.host, obj.obj.nodename): weight
139+
for obj, weight in zip(weighed_objs, weights)}
140+
)
141+
130142
# Normalize the weights
131-
weights = normalize(weights,
132-
minval=weigher.minval,
133-
maxval=weigher.maxval)
143+
weights = list(
144+
normalize(
145+
weights, minval=weigher.minval, maxval=weigher.maxval))
146+
147+
LOG.debug(
148+
"%s: normalized weights %s",
149+
weigher.__class__.__name__,
150+
{(obj.obj.host, obj.obj.nodename): weight
151+
for obj, weight in zip(weighed_objs, weights)}
152+
)
153+
154+
log_data = {}
134155

135156
for i, weight in enumerate(weights):
136157
obj = weighed_objs[i]
137-
obj.weight += weigher.weight_multiplier(obj.obj) * weight
158+
multiplier = weigher.weight_multiplier(obj.obj)
159+
weigher_score = multiplier * weight
160+
obj.weight += weigher_score
161+
162+
log_data[(obj.obj.host, obj.obj.nodename)] = (
163+
f"{multiplier} * {weight}")
164+
165+
LOG.debug(
166+
"%s: score (multiplier * weight) %s",
167+
weigher.__class__.__name__,
168+
{name: log for name, log in log_data.items()}
169+
)
138170

139171
return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)

0 commit comments

Comments
 (0)