Skip to content

Commit c88a35e

Browse files
committed
hardware: Rework 'get_realtime_constraint'
Rework 'get_realtime_constraint' so that it can do the work previously split between this and 'vcpus_realtime_topology', eliminating the need for the latter. This provides a model for how we can parse the 'hw:cpu_dedicated_mask' extra spec in the future. Part of blueprint use-pcpu-and-vcpu-in-one-instance Change-Id: I3a2132db258202afe5cfa0eb95e6905030314f98 Signed-off-by: Stephen Finucane <[email protected]>
1 parent 184a2ca commit c88a35e

File tree

4 files changed

+80
-57
lines changed

4 files changed

+80
-57
lines changed

nova/compute/api.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,7 @@ def _validate_flavor_image_numa_pci(image, instance_type,
745745
# Only validate values of flavor/image so the return results of
746746
# following 'get' functions are not used.
747747
hardware.get_number_of_serial_ports(instance_type, image_meta)
748-
if hardware.is_realtime_enabled(instance_type):
749-
hardware.vcpus_realtime_topology(instance_type, image_meta)
748+
hardware.get_realtime_cpu_constraint(instance_type, image_meta)
750749
hardware.get_cpu_topology_constraints(instance_type, image_meta)
751750
if validate_numa:
752751
hardware.numa_get_constraints(instance_type, image_meta)

nova/tests/unit/virt/test_hardware.py

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,7 @@ def test_topology_constraints(self):
13071307
extra_specs={
13081308
"hw:cpu_policy": fields.CPUAllocationPolicy.SHARED,
13091309
"hw:cpu_realtime": "yes",
1310+
"hw:cpu_realtime_mask": "0-3,^0",
13101311
}),
13111312
"image": {
13121313
"properties": {}
@@ -3603,74 +3604,113 @@ def test_reserved_exceeded(self):
36033604

36043605
class CPURealtimeTestCase(test.NoDBTestCase):
36053606
def test_success_flavor(self):
3606-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3607-
extra_specs={"hw:cpu_realtime_mask": "^1"})
3607+
flavor = objects.Flavor(
3608+
vcpus=3, memory_mb=2048,
3609+
extra_specs={
3610+
'hw:cpu_realtime': 'true',
3611+
'hw:cpu_realtime_mask': '^1',
3612+
}
3613+
)
36083614
image = objects.ImageMeta.from_dict({})
3609-
rt = hw.vcpus_realtime_topology(flavor, image)
3615+
rt = hw.get_realtime_cpu_constraint(flavor, image)
36103616
self.assertEqual(set([0, 2]), rt)
36113617

36123618
def test_success_image(self):
3613-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3614-
extra_specs={"hw:cpu_realtime_mask": "^1"})
3619+
flavor = objects.Flavor(
3620+
vcpus=3, memory_mb=2048,
3621+
extra_specs={
3622+
'hw:cpu_realtime': 'true',
3623+
'hw:cpu_realtime_mask': '^1',
3624+
},
3625+
)
36153626
image = objects.ImageMeta.from_dict(
36163627
{"properties": {"hw_cpu_realtime_mask": "^0-1"}})
3617-
rt = hw.vcpus_realtime_topology(flavor, image)
3628+
rt = hw.get_realtime_cpu_constraint(flavor, image)
36183629
self.assertEqual(set([2]), rt)
36193630

36203631
def test_no_mask_configured(self):
3621-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3622-
extra_specs={})
3632+
flavor = objects.Flavor(
3633+
vcpus=3, memory_mb=2048,
3634+
extra_specs={
3635+
'hw:cpu_realtime': 'true',
3636+
},
3637+
)
36233638
image = objects.ImageMeta.from_dict({"properties": {}})
36243639
self.assertRaises(
36253640
exception.RealtimeMaskNotFoundOrInvalid,
3626-
hw.vcpus_realtime_topology, flavor, image)
3641+
hw.get_realtime_cpu_constraint, flavor, image)
36273642

36283643
def test_invalid_mask_no_rt_cpus(self):
36293644
# The mask excludes all vCPUs from being RT
3630-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3631-
extra_specs={"hw:cpu_realtime_mask": "^0-2"})
3645+
flavor = objects.Flavor(
3646+
vcpus=3, memory_mb=2048,
3647+
extra_specs={
3648+
'hw:cpu_realtime': 'true',
3649+
'hw:cpu_realtime_mask': '^0-2',
3650+
},
3651+
)
36323652
image = objects.ImageMeta.from_dict({"properties": {}})
36333653
self.assertRaises(
36343654
exception.RealtimeMaskNotFoundOrInvalid,
3635-
hw.vcpus_realtime_topology, flavor, image)
3655+
hw.get_realtime_cpu_constraint, flavor, image)
36363656

36373657
def test_invalid_mask_exclude_out_of_range(self):
36383658
# The mask excludes an invalidly high vCPU number.
3639-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3640-
extra_specs={"hw:cpu_realtime_mask": "^3"})
3659+
flavor = objects.Flavor(
3660+
vcpus=3, memory_mb=2048,
3661+
extra_specs={
3662+
'hw:cpu_realtime': 'true',
3663+
'hw:cpu_realtime_mask': '^3',
3664+
},
3665+
)
36413666
image = objects.ImageMeta.from_dict({"properties": {}})
36423667
self.assertRaises(
36433668
exception.RealtimeMaskNotFoundOrInvalid,
3644-
hw.vcpus_realtime_topology, flavor, image)
3669+
hw.get_realtime_cpu_constraint, flavor, image)
36453670

36463671
def test_explicit_range(self):
36473672
# The mask is not just an exclusion mask. This is unexpected but
36483673
# the code doesn't prevent it.
3649-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3650-
extra_specs={"hw:cpu_realtime_mask": "0-2,^0"})
3674+
flavor = objects.Flavor(
3675+
vcpus=3, memory_mb=2048,
3676+
extra_specs={
3677+
'hw:cpu_realtime': 'true',
3678+
'hw:cpu_realtime_mask': '0-2,^0',
3679+
},
3680+
)
36513681
image = objects.ImageMeta.from_dict({"properties": {}})
3652-
rt = hw.vcpus_realtime_topology(flavor, image)
3682+
rt = hw.get_realtime_cpu_constraint(flavor, image)
36533683
self.assertEqual({1, 2}, rt)
36543684

36553685
def test_invalid_mask_no_exclusion_wo_emulator_policy(self):
36563686
# The mask has no exclusion and there's no emulator thread policy
36573687
# configured
3658-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3659-
extra_specs={"hw:cpu_realtime_mask": "0-2"})
3688+
flavor = objects.Flavor(
3689+
vcpus=3, memory_mb=2048,
3690+
extra_specs={
3691+
'hw:cpu_realtime': 'true',
3692+
'hw:cpu_realtime_mask': '0-2',
3693+
},
3694+
)
36603695
image = objects.ImageMeta.from_dict({"properties": {}})
36613696
self.assertRaises(
36623697
exception.RealtimeMaskNotFoundOrInvalid,
3663-
hw.vcpus_realtime_topology, flavor, image)
3698+
hw.get_realtime_cpu_constraint, flavor, image)
36643699

36653700
def test_invalid_mask_rt_cpus_out_of_range(self):
36663701
# The mask is not just an exclusion mask, and the RT range specifies
36673702
# an invalid vCPU number.
3668-
flavor = objects.Flavor(vcpus=3, memory_mb=2048,
3669-
extra_specs={"hw:cpu_realtime_mask": "0-3,^0"})
3703+
flavor = objects.Flavor(
3704+
vcpus=3, memory_mb=2048,
3705+
extra_specs={
3706+
'hw:cpu_realtime': 'true',
3707+
'hw:cpu_realtime_mask': '0-3,^0',
3708+
},
3709+
)
36703710
image = objects.ImageMeta.from_dict({"properties": {}})
36713711
self.assertRaises(
36723712
exception.RealtimeMaskNotFoundOrInvalid,
3673-
hw.vcpus_realtime_topology, flavor, image)
3713+
hw.get_realtime_cpu_constraint, flavor, image)
36743714

36753715

36763716
class EmulatorThreadsTestCase(test.NoDBTestCase):

nova/virt/hardware.py

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,37 +1674,26 @@ def _get_hyperthreading_trait(
16741674
return None
16751675

16761676

1677-
def _get_realtime_constraint(
1677+
# NOTE(stephenfin): This must be public as it's used elsewhere
1678+
def get_realtime_cpu_constraint(
16781679
flavor: 'objects.Flavor',
16791680
image_meta: 'objects.ImageMeta',
1680-
) -> ty.Optional[str]:
1681+
) -> ty.Optional[ty.Set[int]]:
16811682
"""Validate and return the requested realtime CPU mask.
16821683
16831684
:param flavor: ``nova.objects.Flavor`` instance
16841685
:param image_meta: ``nova.objects.ImageMeta`` instance
1685-
:returns: The realtime CPU mask requested, else None.
1686+
:returns: The realtime CPU set requested, else None.
16861687
"""
1688+
if not is_realtime_enabled(flavor):
1689+
return None
1690+
16871691
flavor_mask, image_mask = _get_flavor_image_meta(
16881692
'cpu_realtime_mask', flavor, image_meta)
16891693

16901694
# Image masks are used ahead of flavor masks as they will have more
16911695
# specific requirements
1692-
return image_mask or flavor_mask
1693-
1694-
1695-
def vcpus_realtime_topology(
1696-
flavor: 'objects.Flavor',
1697-
image_meta: 'objects.ImageMeta',
1698-
) -> ty.Set[int]:
1699-
"""Determines instance vCPUs used as RT for a given spec.
1700-
1701-
:param flavor: ``nova.objects.Flavor`` instance
1702-
:param image_meta: ``nova.objects.ImageMeta`` instance
1703-
:raises: exception.RealtimeMaskNotFoundOrInvalid if mask was not found or
1704-
is invalid.
1705-
:returns: The realtime CPU mask requested.
1706-
"""
1707-
mask = _get_realtime_constraint(flavor, image_meta)
1696+
mask = image_mask or flavor_mask
17081697
if not mask:
17091698
raise exception.RealtimeMaskNotFoundOrInvalid()
17101699

@@ -1854,7 +1843,7 @@ def numa_get_constraints(flavor, image_meta):
18541843

18551844
cpu_policy = get_cpu_policy_constraint(flavor, image_meta)
18561845
cpu_thread_policy = get_cpu_thread_policy_constraint(flavor, image_meta)
1857-
rt_mask = _get_realtime_constraint(flavor, image_meta)
1846+
rt_mask = get_realtime_cpu_constraint(flavor, image_meta)
18581847
emu_threads_policy = get_emulator_thread_policy_constraint(flavor)
18591848

18601849
# handle explicit VCPU/PCPU resource requests and the HW_CPU_HYPERTHREADING
@@ -1920,14 +1909,11 @@ def numa_get_constraints(flavor, image_meta):
19201909
if emu_threads_policy == fields.CPUEmulatorThreadsPolicy.ISOLATE:
19211910
raise exception.BadRequirementEmulatorThreadsPolicy()
19221911

1923-
if is_realtime_enabled(flavor):
1912+
if rt_mask:
19241913
raise exception.RealtimeConfigurationInvalid()
19251914

19261915
return numa_topology
19271916

1928-
if is_realtime_enabled(flavor) and not rt_mask:
1929-
raise exception.RealtimeMaskNotFoundOrInvalid()
1930-
19311917
if numa_topology:
19321918
for cell in numa_topology.cells:
19331919
cell.cpu_policy = cpu_policy

nova/virt/libvirt/driver.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4990,7 +4990,7 @@ def _get_pin_cpuset(self, vcpu, object_numa_cell, host_cell):
49904990
return pin_cpuset
49914991

49924992
def _get_emulatorpin_cpuset(self, vcpu, object_numa_cell, vcpus_rt,
4993-
emulator_threads_policy, wants_realtime,
4993+
emulator_threads_policy,
49944994
pin_cpuset):
49954995
"""Returns a set of cpu_ids to add to the cpuset for emulator threads
49964996
with the following caveats:
@@ -5028,7 +5028,7 @@ def _get_emulatorpin_cpuset(self, vcpu, object_numa_cell, vcpus_rt,
50285028
'req': sorted(shared_ids)})
50295029
raise exception.Invalid(msg)
50305030
emulatorpin_cpuset = cpuset
5031-
elif not wants_realtime or vcpu not in vcpus_rt:
5031+
elif not vcpus_rt or vcpu not in vcpus_rt:
50325032
emulatorpin_cpuset = pin_cpuset.cpuset
50335033

50345034
return emulatorpin_cpuset
@@ -5115,10 +5115,8 @@ def _get_guest_numa_config(self, instance_numa_topology, flavor,
51155115
instance_numa_topology.emulator_threads_policy)
51165116

51175117
# Set realtime scheduler for CPUTune
5118-
vcpus_rt = set([])
5119-
wants_realtime = hardware.is_realtime_enabled(flavor)
5120-
if wants_realtime:
5121-
vcpus_rt = hardware.vcpus_realtime_topology(flavor, image_meta)
5118+
vcpus_rt = hardware.get_realtime_cpu_constraint(flavor, image_meta)
5119+
if vcpus_rt:
51225120
vcpusched = vconfig.LibvirtConfigGuestCPUTuneVCPUSched()
51235121
designer.set_vcpu_realtime_scheduler(
51245122
vcpusched, vcpus_rt, CONF.libvirt.realtime_scheduler_priority)
@@ -5142,7 +5140,7 @@ def _get_guest_numa_config(self, instance_numa_topology, flavor,
51425140

51435141
emu_pin_cpuset = self._get_emulatorpin_cpuset(
51445142
cpu, object_numa_cell, vcpus_rt,
5145-
emulator_threads_policy, wants_realtime, pin_cpuset)
5143+
emulator_threads_policy, pin_cpuset)
51465144
guest_cpu_tune.emulatorpin.cpuset.update(emu_pin_cpuset)
51475145

51485146
# TODO(berrange) When the guest has >1 NUMA node, it will

0 commit comments

Comments
 (0)