Skip to content

Commit

Permalink
Translate cpu.frequency hardware requirement (#3296)
Browse files Browse the repository at this point in the history
  • Loading branch information
skycastlelily authored Oct 25, 2024
1 parent 05a66e2 commit 5815197
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 19 deletions.
4 changes: 4 additions & 0 deletions spec/hardware/cpu.fmf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ description: |
# Number or string, an ID of CPU vendor.
vendor: 1234|"> 1234"

# Float or string, CPU frequency.
# MHz are assumed when no unit is specified.
frequency: 2300.0|">= 2300.0"

# Integer or string, CPU stepping.
stepping: 10|">= 10"

Expand Down
1 change: 1 addition & 0 deletions tests/unit/provision/mrack/test_hw.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def test_maximal_constraint(root_logger: Logger) -> None:
},
},
{'or': []},
{'or': []},
{
'not':
{
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/test_hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def test_normalize_invalid_hardware(
- "= avx2"
- "!= smep"
hyper-threading: true
frequency: ">= 2300.0"
device:
device-name: '~ .*Thunderbolt.*'
device: 79
Expand Down Expand Up @@ -260,6 +261,7 @@ def test_parse_maximal_constraint() -> None:
- cpu.family: < 6
- cpu.vendor: == 32902
- cpu.stepping: '!= 10'
- cpu.frequency: '>= 2300.0 megahertz'
- cpu.family-name: == Skylake
- cpu.model-name: '!~ Haswell'
- cpu.vendor-name: ~ Intel.*
Expand Down
88 changes: 75 additions & 13 deletions tmt/hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ class Operator(enum.Enum):
Spec = Any

#: A type of constraint values.
ConstraintValue = Union[int, 'Size', str, bool]
ConstraintValueT = TypeVar('ConstraintValueT', int, 'Size', str, bool)
ConstraintValue = Union[int, 'Size', str, bool, float]
ConstraintValueT = TypeVar('ConstraintValueT', int, 'Size', str, bool, float)

# TODO: this was ported from Artemis but it's not used as of now. That should
# change with future support for flavors aka instance types.
Expand Down Expand Up @@ -701,8 +701,8 @@ def from_specification(
)


class NumberConstraint(Constraint[int]):
""" A constraint representing a dimension-less number """
class IntegerConstraint(Constraint[int]):
""" A constraint representing a dimension-less int number """

@classmethod
def from_specification(
Expand All @@ -713,7 +713,7 @@ def from_specification(
allowed_operators: Optional[list[Operator]] = None
) -> T:

def _cast_number(raw_value: Any) -> int:
def _cast_int(raw_value: Any) -> int:
if isinstance(raw_value, int):
return raw_value

Expand All @@ -731,12 +731,46 @@ def _cast_number(raw_value: Any) -> int:
name,
raw_value,
as_quantity=False,
as_cast=_cast_number,
as_cast=_cast_int,
original_constraint=original_constraint,
allowed_operators=allowed_operators
)


class NumberConstraint(Constraint[float]):
""" A constraint representing a float number """

@classmethod
def from_specification(
cls: type[T],
name: str,
raw_value: str,
original_constraint: Optional['Constraint[Any]'] = None,
allowed_operators: Optional[list[Operator]] = None,
default_unit: Optional[Any] = None
) -> T:

def _cast_number(raw_value: Any) -> float:
if isinstance(raw_value, float):
return raw_value

if isinstance(raw_value, str):
raw_value = raw_value.strip()
return float(raw_value)

raise SpecificationError(f"Could not convert '{raw_value}' to a number.")

return cls._from_specification(
name,
raw_value,
as_quantity=True,
as_cast=_cast_number,
original_constraint=original_constraint,
allowed_operators=allowed_operators,
default_unit=default_unit
)


class TextConstraint(Constraint[str]):
""" A constraint representing a string, e.g. a name """

Expand Down Expand Up @@ -904,14 +938,14 @@ def wrapper(spec: Spec, index: int) -> BaseConstraint:
return wrapper


def _parse_number_constraints(
def _parse_int_constraints(
spec: Spec,
prefix: str,
constraint_keys: tuple[str, ...]) -> list[BaseConstraint]:
""" Parse number-like constraints defined by a given set of keys """
""" Parse number-like constraints defined by a given set of keys, to int """

return [
NumberConstraint.from_specification(
IntegerConstraint.from_specification(
f'{prefix}.{constraint_name.replace("-", "_")}',
str(spec[constraint_name]),
allowed_operators=[
Expand All @@ -921,6 +955,25 @@ def _parse_number_constraints(
]


def _parse_number_constraints(
spec: Spec,
prefix: str,
constraint_keys: tuple[str, ...],
default_unit: Optional[Any] = None) -> list[BaseConstraint]:
""" Parse number-like constraints defined by a given set of keys, to float """

return [
NumberConstraint.from_specification(
f'{prefix}.{constraint_name.replace("-", "_")}',
str(spec[constraint_name]),
allowed_operators=[
Operator.EQ, Operator.NEQ, Operator.LT, Operator.LTE, Operator.GT, Operator.GTE],
default_unit=default_unit)
for constraint_name in constraint_keys
if constraint_name in spec
]


def _parse_size_constraints(
spec: Spec,
prefix: str,
Expand Down Expand Up @@ -998,7 +1051,7 @@ def _parse_device_core(
if include_driver:
text_constraints = (*text_constraints, 'driver')

group.constraints += _parse_number_constraints(spec, device_prefix, number_constraints)
group.constraints += _parse_int_constraints(spec, device_prefix, number_constraints)
group.constraints += _parse_text_constraints(spec, device_prefix, text_constraints)

return group
Expand Down Expand Up @@ -1084,7 +1137,7 @@ def _parse_cpu(spec: Spec) -> BaseConstraint:

group = And()

group.constraints += _parse_number_constraints(
group.constraints += _parse_int_constraints(
spec,
'cpu',
(
Expand All @@ -1097,10 +1150,19 @@ def _parse_cpu(spec: Spec) -> BaseConstraint:
'model',
'family',
'vendor',
'stepping'
'stepping',
)
)

group.constraints += _parse_number_constraints(
spec,
'cpu',
(
'frequency',
),
default_unit='MHz'
)

group.constraints += _parse_text_constraints(
spec,
'cpu',
Expand Down Expand Up @@ -1258,7 +1320,7 @@ def _parse_system(spec: Spec) -> BaseConstraint:
include_driver=False,
include_device=False)

group.constraints += _parse_number_constraints(spec, 'system', ('model', 'numa-nodes'))
group.constraints += _parse_int_constraints(spec, 'system', ('model', 'numa-nodes'))
group.constraints += _parse_text_constraints(spec, 'system', ('model-name',))

return group
Expand Down
4 changes: 4 additions & 0 deletions tmt/schemas/provision/hardware.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ definitions:
- type: integer
model-name:
type: string
frequency:
anyOf:
- type: string
- type: number
stepping:
anyOf:
- type: string
Expand Down
10 changes: 5 additions & 5 deletions tmt/steps/provision/mrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def _transform_cpu_flag(


def _transform_cpu_model(
constraint: tmt.hardware.NumberConstraint,
constraint: tmt.hardware.IntegerConstraint,
logger: tmt.log.Logger) -> MrackBaseHWElement:
beaker_operator, actual_value, _ = operator_to_beaker_op(
constraint.operator,
Expand All @@ -256,7 +256,7 @@ def _transform_cpu_model(


def _transform_cpu_processors(
constraint: tmt.hardware.NumberConstraint,
constraint: tmt.hardware.IntegerConstraint,
logger: tmt.log.Logger) -> MrackBaseHWElement:
beaker_operator, actual_value, _ = operator_to_beaker_op(
constraint.operator,
Expand All @@ -268,7 +268,7 @@ def _transform_cpu_processors(


def _transform_cpu_cores(
constraint: tmt.hardware.NumberConstraint,
constraint: tmt.hardware.IntegerConstraint,
logger: tmt.log.Logger) -> MrackBaseHWElement:
beaker_operator, actual_value, _ = operator_to_beaker_op(
constraint.operator,
Expand Down Expand Up @@ -298,7 +298,7 @@ def _transform_cpu_model_name(


def _transform_cpu_stepping(
constraint: tmt.hardware.NumberConstraint,
constraint: tmt.hardware.IntegerConstraint,
logger: tmt.log.Logger) -> MrackBaseHWElement:
beaker_operator, actual_value, _ = operator_to_beaker_op(
constraint.operator,
Expand Down Expand Up @@ -568,7 +568,7 @@ def _transform_location_lab_controller(


def _transform_system_numa_nodes(
constraint: tmt.hardware.NumberConstraint,
constraint: tmt.hardware.IntegerConstraint,
logger: tmt.log.Logger) -> MrackBaseHWElement:
beaker_operator, actual_value, _ = operator_to_beaker_op(
constraint.operator,
Expand Down
2 changes: 1 addition & 1 deletion tmt/steps/provision/testcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def _apply_hw_cpu_processors(
cpu_processors_constraints = [
constraint
for constraint in variant
if isinstance(constraint, tmt.hardware.NumberConstraint)
if isinstance(constraint, tmt.hardware.IntegerConstraint)
and constraint.expand_name().name == 'cpu'
and constraint.expand_name().child_name == 'processors']

Expand Down

0 comments on commit 5815197

Please sign in to comment.