From bd96a1352f42fbdb2ab2489b499d58f7f85454bd Mon Sep 17 00:00:00 2001 From: Han Han Date: Fri, 19 Jul 2024 18:21:54 +0800 Subject: [PATCH 1/3] Add the `iommu` specification and support in `beaker` plugin Fixes #2392 Signed-off-by: Han Han --- spec/hardware/iommu.fmf | 32 +++++++++++++++++++++++++++ tests/unit/provision/mrack/test_hw.py | 30 +++++++++++++++++++++++++ tmt/hardware.py | 22 ++++++++++++++++++ tmt/schemas/provision/hardware.yaml | 17 ++++++++++++++ tmt/steps/provision/mrack.py | 16 ++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 spec/hardware/iommu.fmf diff --git a/spec/hardware/iommu.fmf b/spec/hardware/iommu.fmf new file mode 100644 index 0000000000..49903026fb --- /dev/null +++ b/spec/hardware/iommu.fmf @@ -0,0 +1,32 @@ +summary: | + Select or provision a guest with the `Input–output memory management unit`. + +description: | + .. code-block:: + + iommu: + # Boolean, whether the guest supports IOMMU + is-supported: true|false + # String, the model name for IOMMU + model-name: "virtio"|"intel"|"smmuv3"|... + + .. versionadded:: 1.37 + + .. versionchanged:: 1.37 + ``beaker`` plugin supports ``iommu`` + +example: + - | + # Require a guest that supports IOMMU + iommu: + is-supported: true + + - | + # Require a guest with the IOMMU of virtio model + iommu: + is-supported: true + model-name: virtio + +link: + - implemented-by: /tmt/steps/provision/mrack.py + note: "``iommu.is-supported`` only" diff --git a/tests/unit/provision/mrack/test_hw.py b/tests/unit/provision/mrack/test_hw.py index 958614744f..47ef7dac34 100644 --- a/tests/unit/provision/mrack/test_hw.py +++ b/tests/unit/provision/mrack/test_hw.py @@ -10,6 +10,7 @@ _parse_cpu, _parse_disk, _parse_hostname, + _parse_iommu, _parse_location, _parse_memory, _parse_system, @@ -788,6 +789,35 @@ def test_zcrypt_mode(root_logger: Logger) -> None: } +def test_iommu_is_supported(root_logger: Logger) -> None: + + result = _CONSTRAINT_TRANSFORMERS['iommu.is_supported']( + _parse_iommu({"is-supported": True}), root_logger) + + assert result.to_mrack() == { + 'system': { + 'key_value': { + '_key': 'VIRT_IOMMU', + '_op': '==', + '_value': '1' + } + } + } + + result = _CONSTRAINT_TRANSFORMERS['iommu.is_supported']( + _parse_iommu({"is-supported": False}), root_logger) + + assert result.to_mrack() == { + 'system': { + 'key_value': { + '_key': 'VIRT_IOMMU', + '_op': '==', + '_value': '0' + } + } + } + + def test_location_lab_controller(root_logger: Logger) -> None: result = _CONSTRAINT_TRANSFORMERS['location.lab_controller']( diff --git a/tmt/hardware.py b/tmt/hardware.py index f79d1b63d3..bf0010015f 100644 --- a/tmt/hardware.py +++ b/tmt/hardware.py @@ -1333,6 +1333,25 @@ def _parse_zcrypt(spec: Spec) -> BaseConstraint: return group +@ungroupify +def _parse_iommu(spec: Spec) -> BaseConstraint: + """ + Parse constraints related to the ``iommu`` HW requirement. + + :param spec: raw constraint block specification. + :returns: block representation as :py:class:`BaseConstraint` or one of its subclasses. + """ + + group = And() + + group.constraints += _parse_flag_constraints(spec, + 'iommu', + ('is-supported',)) + group.constraints += _parse_text_constraints(spec, 'iommu', ('model-name',)) + + return group + + @ungroupify def _parse_location(spec: Spec) -> BaseConstraint: """ @@ -1428,6 +1447,9 @@ def _parse_generic_spec(spec: Spec) -> BaseConstraint: if 'zcrypt' in spec: group.constraints += [_parse_zcrypt(spec['zcrypt'])] + if 'iommu' in spec: + group.constraints += [_parse_iommu(spec['iommu'])] + return group diff --git a/tmt/schemas/provision/hardware.yaml b/tmt/schemas/provision/hardware.yaml index 7d1a871380..53ce43a47e 100644 --- a/tmt/schemas/provision/hardware.yaml +++ b/tmt/schemas/provision/hardware.yaml @@ -355,6 +355,20 @@ definitions: # empty `boot`. minProperties: 1 + # Hw requirements `iommu` block + iommu: + type: object + + properties: + is-supported: + type: boolean + + model-name: + type: string + + additionalProperties: false + minProperties: 1 + # HW requirements: basic block block: type: object @@ -405,6 +419,9 @@ definitions: virtualization: "$ref": "#/definitions/virtualization" + iommu: + "$ref": "#/definitions/iommu" + additionalProperties: false # enforce at least one property - we don't care which one, but we don't want diff --git a/tmt/steps/provision/mrack.py b/tmt/steps/provision/mrack.py index 2e833f4951..be94845153 100644 --- a/tmt/steps/provision/mrack.py +++ b/tmt/steps/provision/mrack.py @@ -512,6 +512,21 @@ def _transform_zcrypt_mode( children=[MrackHWKeyValue('ZCRYPT_MODE', beaker_operator, actual_value)]) +def _transform_iommu_is_supported( + constraint: tmt.hardware.FlagConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + + test = (constraint.operator, constraint.value) + + if test in [(tmt.hardware.Operator.EQ, True), (tmt.hardware.Operator.NEQ, False)]: + return MrackHWKeyValue('VIRT_IOMMU', '==', '1') + + if test in [(tmt.hardware.Operator.EQ, False), (tmt.hardware.Operator.NEQ, True)]: + return MrackHWKeyValue('VIRT_IOMMU', '==', '0') + + return _transform_unsupported(constraint, logger) + + def _transform_location_lab_controller( constraint: tmt.hardware.TextConstraint, logger: tmt.log.Logger) -> MrackBaseHWElement: @@ -573,6 +588,7 @@ def _transform_system_numa_nodes( 'zcrypt.adapter': _transform_zcrypt_adapter, # type: ignore[dict-item] 'zcrypt.mode': _transform_zcrypt_mode, # type: ignore[dict-item] 'system.numa_nodes': _transform_system_numa_nodes, # type: ignore[dict-item] + 'iommu.is_supported': _transform_iommu_is_supported, # type: ignore[dict-item] } From 573a86b803d9ae1f07df0d46dc03f871a4fd037b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0pl=C3=ADchal?= Date: Tue, 1 Oct 2024 16:17:38 +0200 Subject: [PATCH 2/3] Add a short release note, fix & simplify the test --- docs/releases.rst | 5 +++++ tests/unit/provision/mrack/test_hw.py | 23 +++++------------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/docs/releases.rst b/docs/releases.rst index eb873c1a2d..b994400ab1 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -57,6 +57,11 @@ The ``tmt try`` command now supports the new new :ref:`/stories/cli/try/option/install` option backed by the :ref:`prepare/feature` plugin. +The new key :ref:`/spec/hardware/iommu` allowing to provision a +guest with the `Input–output memory management unit` has been +added into the :ref:`/spec/hardware` specification and implemented +in the :ref:`/plugins/provision/beaker` provision plugin. + tmt-1.36.1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/unit/provision/mrack/test_hw.py b/tests/unit/provision/mrack/test_hw.py index 47ef7dac34..e0d0827511 100644 --- a/tests/unit/provision/mrack/test_hw.py +++ b/tests/unit/provision/mrack/test_hw.py @@ -791,31 +791,18 @@ def test_zcrypt_mode(root_logger: Logger) -> None: def test_iommu_is_supported(root_logger: Logger) -> None: - result = _CONSTRAINT_TRANSFORMERS['iommu.is_supported']( - _parse_iommu({"is-supported": True}), root_logger) + for value in True, False: - assert result.to_mrack() == { - 'system': { - 'key_value': { - '_key': 'VIRT_IOMMU', - '_op': '==', - '_value': '1' - } - } - } - - result = _CONSTRAINT_TRANSFORMERS['iommu.is_supported']( - _parse_iommu({"is-supported": False}), root_logger) + result = _CONSTRAINT_TRANSFORMERS['iommu.is_supported']( + _parse_iommu({"is-supported": value}), root_logger) - assert result.to_mrack() == { - 'system': { + assert result.to_mrack() == { 'key_value': { '_key': 'VIRT_IOMMU', '_op': '==', - '_value': '0' + '_value': str(int(value)) } } - } def test_location_lab_controller(root_logger: Logger) -> None: From 98b52b0596e641c13eac3ffaff46878fe5de8c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0pl=C3=ADchal?= Date: Tue, 1 Oct 2024 19:46:25 +0200 Subject: [PATCH 3/3] Adjust test to work for pull requests from forks as well --- tests/unit/test_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index ac057542dc..13330c18ce 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1742,7 +1742,7 @@ def test_jira_link_test_only(self, mock_config_tree, mock_add_simple_link) -> No logger=self.logger) result = mock_add_simple_link.call_args.args[1] assert 'https://tmt.testing-farm.io/?' in result['url'] - assert 'test-url=https%3A%2F%2Fgithub.com%2Fteemtee%2Ftmt' in result['url'] + assert 'test-url=https%3A%2F%2Fgithub.com%2F' in result['url'] assert '&test-name=%2Ftmp%2Ftest' in result['url'] assert '&test-path=%2Ftests%2Funit%2Ftmp' in result['url'] @@ -1760,15 +1760,15 @@ def test_jira_link_test_plan_story(self, mock_config_tree, mock_add_simple_link) result = mock_add_simple_link.call_args.args[1] assert 'https://tmt.testing-farm.io/?' in result['url'] - assert 'test-url=https%3A%2F%2Fgithub.com%2Fteemtee%2Ftmt' in result['url'] + assert 'test-url=https%3A%2F%2Fgithub.com%2F' in result['url'] assert '&test-name=%2Ftmp%2Ftest' in result['url'] assert '&test-path=%2Ftests%2Funit%2Ftmp' in result['url'] - assert '&plan-url=https%3A%2F%2Fgithub.com%2Fteemtee%2Ftmt' in result['url'] + assert '&plan-url=https%3A%2F%2Fgithub.com%2F' in result['url'] assert '&plan-name=%2Ftmp%2Fplan' in result['url'] assert '&plan-path=%2Ftests%2Funit%2Ftmp' in result['url'] - assert '&story-url=https%3A%2F%2Fgithub.com%2Fteemtee%2Ftmt' in result['url'] + assert '&story-url=https%3A%2F%2Fgithub.com%2F' in result['url'] assert '&story-name=%2Ftmp%2Fstory' in result['url'] assert '&story-path=%2Ftests%2Funit%2Ftmp' in result['url']