From 630e95a4a11afa681fc95260da8d6a5637dd9e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Prchl=C3=ADk?= Date: Fri, 8 Mar 2024 09:47:52 +0100 Subject: [PATCH] Finish unit testing of HW transformations in mrack plugin --- tests/unit/provision/mrack/test_hw.py | 233 +++++++++++++++++++++++--- tmt/hardware.py | 13 -- tmt/steps/provision/mrack.py | 222 ++++++++++++++---------- 3 files changed, 343 insertions(+), 125 deletions(-) diff --git a/tests/unit/provision/mrack/test_hw.py b/tests/unit/provision/mrack/test_hw.py index fb601d330e..aaf416b909 100644 --- a/tests/unit/provision/mrack/test_hw.py +++ b/tests/unit/provision/mrack/test_hw.py @@ -3,7 +3,15 @@ import pytest import tmt.utils -from tmt.hardware import Hardware, Operator, _parse_hostname, _parse_memory +from tmt.hardware import ( + Hardware, + Operator, + _parse_cpu, + _parse_disk, + _parse_hostname, + _parse_memory, + _parse_virtualization, + ) from tmt.log import Logger from tmt.steps.provision.mrack import ( _CONSTRAINT_TRANSFORMERS, @@ -57,7 +65,9 @@ def test_maximal_constraint(root_logger: Logger) -> None: - "!= smep" disk: - size: 40 GiB + model-name: "PERC H310" - size: 120 GiB + driver: mpt3sas gpu: device-name: G86 [Quadro NVS 290] hostname: "~ .*.foo.redhat.com" @@ -111,15 +121,6 @@ def test_maximal_constraint(root_logger: Logger) -> None: } }, {'or': []}, - { - 'cpu': { - 'model': { - '_op': '==', - '_value': '62' - } - } - }, - {'or': []}, {'or': []}, {'or': []}, { @@ -163,21 +164,44 @@ def test_maximal_constraint(root_logger: Logger) -> None: { 'and': [ { - 'disk': { - 'size': { - '_op': '==', - '_value': '42949672960' + 'and': [ + { + 'disk': { + 'size': { + '_op': '==', + '_value': '42949672960' + } + } + }, + { + 'disk': { + 'model': { + '_op': '==', + '_value': 'PERC H310' + } + } } - } + ] }, { - 'disk': { - 'size': { - '_op': '==', - '_value': '128849018880' + 'and': [ + { + 'disk': { + 'size': { + '_op': '==', + '_value': '128849018880' + } + } + }, + { + 'key_value': { + '_key': 'BOOTDISK', + '_op': '==', + '_value': 'mpt3sas' + } } - } - } + ] + }, ] }, { @@ -211,6 +235,147 @@ def test_maximal_constraint(root_logger: Logger) -> None: } +def test_cpu_model(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['cpu.model'](_parse_cpu({'model': '79'}), root_logger) + + assert result.to_mrack() == { + 'cpu': { + 'model': { + '_op': '==', + '_value': '79' + } + } + } + + +def test_cpu_processors(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['cpu.processors']( + _parse_cpu({'processors': '79'}), root_logger) + + assert result.to_mrack() == { + 'cpu': { + 'cpu_count': { + '_op': '==', + '_value': '79' + } + } + } + + +def test_disk_driver(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['disk.driver']( + _parse_disk({'driver': 'mpt3sas'}, 1), root_logger) + + assert result.to_mrack() == { + 'key_value': { + '_key': 'BOOTDISK', + '_op': '==', + '_value': 'mpt3sas' + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.driver']( + _parse_disk({'driver': '!= mpt3sas'}, 1), root_logger) + + assert result.to_mrack() == { + 'key_value': { + '_key': 'BOOTDISK', + '_op': '!=', + '_value': 'mpt3sas' + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.driver']( + _parse_disk({'driver': '~ mpt3.*'}, 1), root_logger) + + assert result.to_mrack() == { + 'key_value': { + '_key': 'BOOTDISK', + '_op': 'like', + '_value': 'mpt3%' + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.driver']( + _parse_disk({'driver': '!~ mpt3.*'}, 1), root_logger) + + assert result.to_mrack() == { + 'not': { + 'key_value': { + '_key': 'BOOTDISK', + '_op': 'like', + '_value': 'mpt3%' + } + } + } + + +def test_disk_size(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['disk.size']( + _parse_disk({'size': '>= 40 GiB'}, 1), root_logger) + + assert result.to_mrack() == { + 'disk': { + 'size': { + '_op': '>=', + '_value': '42949672960' + } + } + } + + +def test_disk_model_name(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['disk.model_name']( + _parse_disk({'model-name': 'PERC H310'}, 1), root_logger) + + assert result.to_mrack() == { + 'disk': { + 'model': { + '_op': '==', + '_value': 'PERC H310' + } + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.model_name']( + _parse_disk({'model-name': '!= PERC H310'}, 1), root_logger) + + assert result.to_mrack() == { + 'disk': { + 'model': { + '_op': '!=', + '_value': 'PERC H310' + } + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.model_name']( + _parse_disk({'model-name': '~ PERC.*'}, 1), root_logger) + + assert result.to_mrack() == { + 'disk': { + 'model': { + '_op': 'like', + '_value': 'PERC%' + } + } + } + + result = _CONSTRAINT_TRANSFORMERS['disk.model_name']( + _parse_disk({'model-name': '!~ PERC.*'}, 1), root_logger) + + assert result.to_mrack() == { + 'not': { + 'disk': { + 'model': { + '_op': 'like', + '_value': 'PERC%' + } + } + } + } + + def test_memory(root_logger: Logger) -> None: result = _CONSTRAINT_TRANSFORMERS['memory'](_parse_memory({'memory': '>= 4 GiB'}), root_logger) @@ -256,3 +421,29 @@ def test_hostname(root_logger: Logger) -> None: } } } + + +def test_virtualization_is_virtualized(root_logger: Logger) -> None: + result = _CONSTRAINT_TRANSFORMERS['virtualization.is_virtualized']( + _parse_virtualization({'is-virtualized': True}), root_logger) + + assert result.to_mrack() == { + 'system': { + 'hypervisor': { + '_op': '!=', + '_value': '' + } + } + } + + result = _CONSTRAINT_TRANSFORMERS['virtualization.is_virtualized']( + _parse_virtualization({'is-virtualized': False}), root_logger) + + assert result.to_mrack() == { + 'system': { + 'hypervisor': { + '_op': '==', + '_value': '' + } + } + } diff --git a/tmt/hardware.py b/tmt/hardware.py index f44dea3ce5..eabd04ebf8 100644 --- a/tmt/hardware.py +++ b/tmt/hardware.py @@ -1010,19 +1010,6 @@ def _parse_cpu(spec: Spec) -> BaseConstraint: if constraint_name in spec ] - group.constraints += [ - TextConstraint.from_specification( - f'cpu.{constraint_name.replace("-", "_")}', - str(spec[constraint_name]), - allowed_operators=[ - Operator.EQ, Operator.NEQ, Operator.LT, Operator.LTE, Operator.GT, Operator.GTE]) - for constraint_name in ( - 'model', - 'family' - ) - if constraint_name in spec - ] - group.constraints += [ TextConstraint.from_specification( f'cpu.{constraint_name.replace("-", "_")}', diff --git a/tmt/steps/provision/mrack.py b/tmt/steps/provision/mrack.py index a69625c4c0..8f0e8deee0 100644 --- a/tmt/steps/provision/mrack.py +++ b/tmt/steps/provision/mrack.py @@ -205,6 +205,105 @@ class MrackHWNotGroup(MrackHWGroup): name: str = 'not' +def _transform_unsupported( + constraint: tmt.hardware.Constraint[Any], + logger: tmt.log.Logger) -> MrackBaseHWElement: + # Unsupported constraint has been already logged via report_support(). Make + # sure user is aware it would have no effect, and since we have to return + # something, return an empty `or` group - no harm done, composable with other + # elements. + logger.warn(f"Hardware requirement '{constraint.printable_name}' will have no effect.") + + return MrackHWOrGroup() + + +def _transform_cpu_flag( + constraint: tmt.hardware.TextConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator = OPERATOR_SIGN_TO_OPERATOR[tmt.hardware.Operator.EQ] \ + if constraint.operator is tmt.hardware.Operator.CONTAINS \ + else OPERATOR_SIGN_TO_OPERATOR[tmt.hardware.Operator.NEQ] + actual_value = str(constraint.value) + + return MrackHWGroup( + 'cpu', + children=[MrackHWBinOp('flag', beaker_operator, actual_value)] + ) + + +def _transform_cpu_model( + constraint: tmt.hardware.NumberConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, _ = operator_to_beaker_op( + constraint.operator, + str(constraint.value)) + + return MrackHWGroup( + 'cpu', + children=[MrackHWBinOp('model', beaker_operator, actual_value)]) + + +def _transform_cpu_processors( + constraint: tmt.hardware.NumberConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, _ = operator_to_beaker_op( + constraint.operator, + str(constraint.value)) + + return MrackHWGroup( + 'cpu', + children=[MrackHWBinOp('cpu_count', beaker_operator, actual_value)]) + + +def _transform_disk_driver( + constraint: tmt.hardware.TextConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, negate = operator_to_beaker_op( + constraint.operator, + constraint.value) + + if negate: + return MrackHWNotGroup(children=[ + MrackHWKeyValue('BOOTDISK', beaker_operator, actual_value) + ]) + + return MrackHWKeyValue( + 'BOOTDISK', + beaker_operator, + actual_value) + + +def _transform_disk_size( + constraint: tmt.hardware.SizeConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, _ = operator_to_beaker_op( + constraint.operator, + str(int(constraint.value.to('B').magnitude)) + ) + + return MrackHWGroup( + 'disk', + children=[MrackHWBinOp('size', beaker_operator, actual_value)]) + + +def _transform_disk_model_name( + constraint: tmt.hardware.TextConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, negate = operator_to_beaker_op( + constraint.operator, + constraint.value) + + if negate: + return MrackHWNotGroup(children=[ + MrackHWGroup( + 'disk', + children=[MrackHWBinOp('model', beaker_operator, actual_value)])]) + + return MrackHWGroup( + 'disk', + children=[MrackHWBinOp('model', beaker_operator, actual_value)]) + + def _transform_hostname( constraint: tmt.hardware.TextConstraint, logger: tmt.log.Logger) -> MrackBaseHWElement: @@ -236,12 +335,42 @@ def _transform_memory( children=[MrackHWBinOp('memory', beaker_operator, actual_value)]) +def _transform_virtualization_is_virtualized( + constraint: tmt.hardware.FlagConstraint, + logger: tmt.log.Logger) -> MrackBaseHWElement: + beaker_operator, actual_value, _ = operator_to_beaker_op( + constraint.operator, + str(constraint.value)) + + test = (constraint.operator, constraint.value) + + if test in [(tmt.hardware.Operator.EQ, True), (tmt.hardware.Operator.NEQ, False)]: + return MrackHWGroup( + 'system', + children=[MrackHWBinOp('hypervisor', '!=', '')]) + + if test in [(tmt.hardware.Operator.EQ, False), (tmt.hardware.Operator.NEQ, True)]: + return MrackHWGroup( + 'system', + children=[MrackHWBinOp('hypervisor', '==', '')]) + + return _transform_unsupported(constraint, logger) + + ConstraintTransformer = Callable[[ tmt.hardware.Constraint[Any], tmt.log.Logger], MrackBaseHWElement] _CONSTRAINT_TRANSFORMERS: Mapping[str, ConstraintTransformer] = { + 'cpu.flag': _transform_cpu_flag, # type: ignore[dict-item] + 'cpu.model': _transform_cpu_model, # type: ignore[dict-item] + 'cpu.processors': _transform_cpu_processors, # type: ignore[dict-item] + 'disk.driver': _transform_disk_driver, # type: ignore[dict-item] + 'disk.model_name': _transform_disk_model_name, # type: ignore[dict-item] + 'disk.size': _transform_disk_size, # type: ignore[dict-item] 'hostname': _transform_hostname, # type: ignore[dict-item] - 'memory': _transform_memory # type: ignore[dict-item] + 'memory': _transform_memory, # type: ignore[dict-item] + 'virtualization.is_virtualized': \ + _transform_virtualization_is_virtualized # type: ignore[dict-item] } @@ -279,96 +408,7 @@ def constraint_to_beaker_filter( if transformer: return transformer(constraint, logger) - if name == "disk" and child_name == 'size': - beaker_operator, actual_value, _ = operator_to_beaker_op( - constraint.operator, - str(int(cast('tmt.hardware.Size', constraint.value).to('B').magnitude)) - ) - - return MrackHWGroup( - 'disk', - children=[MrackHWBinOp('size', beaker_operator, actual_value)]) - - if name == "disk" and child_name == 'model_name': - beaker_operator, actual_value, negate = operator_to_beaker_op( - constraint.operator, - constraint.value) - - if negate: - return MrackHWNotGroup(children=[ - MrackHWBinOp('model', beaker_operator, actual_value) - ]) - - return MrackHWGroup( - 'disk', - children=[MrackHWBinOp('model', beaker_operator, actual_value)]) - - if name == "disk" and child_name == 'driver': - beaker_operator, actual_value, negate = operator_to_beaker_op( - constraint.operator, - constraint.value) - - if negate: - return MrackHWNotGroup(children=[ - MrackHWKeyValue('BOOTDISK', beaker_operator, actual_value) - ]) - - return MrackHWKeyValue( - 'BOOTDISK', - beaker_operator, - actual_value) - - if name == "cpu": - if child_name == 'flag': - beaker_operator = OPERATOR_SIGN_TO_OPERATOR[tmt.hardware.Operator.EQ] \ - if constraint.operator is tmt.hardware.Operator.CONTAINS \ - else OPERATOR_SIGN_TO_OPERATOR[tmt.hardware.Operator.NEQ] - actual_value = str(constraint.value) - - return MrackHWGroup( - 'cpu', - children=[MrackHWBinOp('flag', beaker_operator, actual_value)] - ) - - beaker_operator, actual_value, _ = operator_to_beaker_op( - constraint.operator, - str(constraint.value)) - - if child_name == 'processors': - return MrackHWGroup( - 'cpu', - children=[MrackHWBinOp('cpu_count', beaker_operator, actual_value)]) - - if child_name == 'model': - return MrackHWGroup( - 'cpu', - children=[MrackHWBinOp('model', beaker_operator, actual_value)]) - - if name == 'virtualization': - beaker_operator, actual_value, _ = operator_to_beaker_op( - constraint.operator, - str(constraint.value)) - - if child_name == 'is_virtualized': - test = (constraint.operator, constraint.value) - - if test in [(tmt.hardware.Operator.EQ, True), (tmt.hardware.Operator.NEQ, False)]: - return MrackHWGroup( - 'system', - children=[MrackHWBinOp('hypervisor', '!=', '')]) - - if test in [(tmt.hardware.Operator.EQ, False), (tmt.hardware.Operator.NEQ, True)]: - return MrackHWGroup( - 'system', - children=[MrackHWBinOp('hypervisor', '==', '')]) - - # Unsupported constraint has been already logged via report_support(). Make - # sure user is aware it would have no effect, and since we have to return - # something, return an empty `or` group - no harm done, composable with other - # elements. - logger.warn(f"Hardware requirement '{constraint.printable_name}' will have no effect.") - - return MrackHWOrGroup() + return _transform_unsupported(constraint, logger) def import_and_load_mrack_deps(workdir: Any, name: str, logger: tmt.log.Logger) -> None: