diff --git a/doc/source/user/flavors.rst b/doc/source/user/flavors.rst index 5d964493c6a2..30704be4a024 100644 --- a/doc/source/user/flavors.rst +++ b/doc/source/user/flavors.rst @@ -625,9 +625,11 @@ CPU real-time policy - ``yes``: The guest vCPUs will have a real-time policy CPU-REALTIME-MASK (coremask): - A coremask indicating which vCPUs **will not** have a real-time policy. This - should start with a ``^``. For example, a value of ``^0-1`` indicates that - all vCPUs *except* vCPUs ``0`` and ``1`` will have a real-time policy. + A coremask indicating which vCPUs **will** or, if starting with a ``^``, + **will not** have a real-time policy. For example, a value of ``0-5`` + indicates that vCPUs ``0`` to ``5`` will have a real-time policy. + Conversely, a value of ``^0-1`` indicates that all vCPUs *except* vCPUs + ``0`` and ``1`` will have a real-time policy. .. note:: @@ -641,6 +643,12 @@ CPU real-time policy to omit this when an emulator thread policy is configured using the ``hw:emulator_threads_policy`` extra spec. + .. versionchanged:: 22.0.0 (Victoria) + + Previously, the leading carat was necessary and omitting it would be + equivalent to not setting the mask, resulting in a failure to spawn + the instance. + .. _extra-specs-emulator-threads-policy: Emulator threads policy diff --git a/nova/api/validation/extra_specs/hw.py b/nova/api/validation/extra_specs/hw.py index b9d4def4a982..fd4aae4cb4cf 100644 --- a/nova/api/validation/extra_specs/hw.py +++ b/nova/api/validation/extra_specs/hw.py @@ -36,8 +36,7 @@ realtime_validators = [ ), value={ 'type': str, - # NOTE(stephenfin): Yes, these things *have* to start with '^' - 'pattern': r'\^\d+((-\d+)?(,\^?\d+(-\d+)?)?)*', + 'pattern': r'(\^)?\d+((-\d+)?(,\^?\d+(-\d+)?)?)*', }, ), ] diff --git a/nova/tests/unit/api/validation/extra_specs/test_validators.py b/nova/tests/unit/api/validation/extra_specs/test_validators.py index 2670c5c5acfb..6f11673fd034 100644 --- a/nova/tests/unit/api/validation/extra_specs/test_validators.py +++ b/nova/tests/unit/api/validation/extra_specs/test_validators.py @@ -59,6 +59,7 @@ class TestValidators(test.NoDBTestCase): def test_value__str(self): valid_specs = ( # patterns + ('hw:cpu_realtime_mask', '0'), ('hw:cpu_realtime_mask', '^0'), ('hw:cpu_realtime_mask', '^0,2-3,1'), ('hw:mem_page_size', 'large'), @@ -74,7 +75,7 @@ class TestValidators(test.NoDBTestCase): invalid_specs = ( # patterns - ('hw:cpu_realtime_mask', '0'), + ('hw:cpu_realtime_mask', 'a'), ('hw:cpu_realtime_mask', '^0,2-3,b'), ('hw:mem_page_size', 'largest'), ('hw:mem_page_size', '2kbits'), diff --git a/nova/tests/unit/virt/test_hardware.py b/nova/tests/unit/virt/test_hardware.py index 8888157db7ee..1c0da0860c9d 100644 --- a/nova/tests/unit/virt/test_hardware.py +++ b/nova/tests/unit/virt/test_hardware.py @@ -3818,6 +3818,42 @@ class CPURealtimeTestCase(test.NoDBTestCase): rt = hw.get_realtime_cpu_constraint(flavor, image) self.assertEqual(set([2]), rt) + def test_success_image_no_exclusion(self): + flavor = objects.Flavor( + vcpus=3, memory_mb=2048, + extra_specs={ + 'hw:cpu_realtime': 'true', + }, + ) + image = objects.ImageMeta.from_dict( + {"properties": {"hw_cpu_realtime_mask": "1-2"}}) + rt = hw.get_realtime_cpu_constraint(flavor, image) + self.assertEqual(set([1, 2]), rt) + + def test_success_image_leading_space(self): + flavor = objects.Flavor( + vcpus=3, memory_mb=2048, + extra_specs={ + 'hw:cpu_realtime': 'true', + }, + ) + image = objects.ImageMeta.from_dict( + {"properties": {"hw_cpu_realtime_mask": " ^1"}}) + rt = hw.get_realtime_cpu_constraint(flavor, image) + self.assertEqual(set([0, 2]), rt) + + def test_success_image_no_implicit_exclusion(self): + flavor = objects.Flavor( + vcpus=3, memory_mb=2048, + extra_specs={ + 'hw:cpu_realtime': 'true', + }, + ) + image = objects.ImageMeta.from_dict( + {"properties": {"hw_cpu_realtime_mask": "1-2,^1"}}) + rt = hw.get_realtime_cpu_constraint(flavor, image) + self.assertEqual(set([2]), rt) + def test_no_mask_configured(self): flavor = objects.Flavor( vcpus=3, memory_mb=2048, diff --git a/nova/virt/hardware.py b/nova/virt/hardware.py index b8af9553eb6e..64c9bf0bab62 100644 --- a/nova/virt/hardware.py +++ b/nova/virt/hardware.py @@ -1708,7 +1708,10 @@ def get_realtime_cpu_constraint( vcpus_set = set(range(flavor.vcpus)) if mask: - vcpus_rt = parse_cpu_spec("0-%d,%s" % (flavor.vcpus - 1, mask)) + if mask.strip().startswith('^'): + vcpus_rt = parse_cpu_spec("0-%d,%s" % (flavor.vcpus - 1, mask)) + else: + vcpus_rt = parse_cpu_spec("%s" % (mask)) else: vcpus_rt = set(range(flavor.vcpus)) diff --git a/releasenotes/notes/bug-1884231-16acf297d88b122e.yaml b/releasenotes/notes/bug-1884231-16acf297d88b122e.yaml index 4f18a2fcb8b2..7ed64a0c1637 100644 --- a/releasenotes/notes/bug-1884231-16acf297d88b122e.yaml +++ b/releasenotes/notes/bug-1884231-16acf297d88b122e.yaml @@ -9,3 +9,8 @@ features: It is now possible to allocate all cores in an instance to realtime and omit the ``hw:cpu_realtime_mask`` extra spec. This requires specifying the ``hw:emulator_threads_policy`` extra spec. + - | + It is now possible to specify a mask in ``hw:cpu_realtime_mask`` without a + leading ``^``. When this is ommitted, the value will specify the cores that + should be included in the set of realtime cores, as opposed to those that + should be excluded.