mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
make autoformat reflowed a resource_filter call, a generator expression and five test files it touched along the way. The new architecture test also carried the two findings the first gate run stopped on: three RUF012, where a TestCase class attribute needs ClassVar, and two PERF401, where an append loop becomes list.extend over a generator. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
80 lines
2.8 KiB
Python
80 lines
2.8 KiB
Python
"""Whether a service gets a GPU: it asked for one AND the host carries one.
|
|
|
|
Two facts have to agree and used to be restated at every call site. The
|
|
declaration lives in ``applications`` under ``services.<service>.gpu``; the
|
|
hardware is ``sys-svc-container``'s stat of ``/dev/nvidia0`` on the target
|
|
host, registered as ``sys_svc_container_nvidia_device``. Asking only the
|
|
declaration selects a ``-cuda`` image on a host that has no device, and that
|
|
tag has no arm64 manifest at all, so the build fails with ``no match for
|
|
platform in manifest``.
|
|
|
|
The stat is a target-host fact, so this reads the register out of the play's
|
|
variables rather than inspecting the controller it runs on.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ansible.errors import AnsibleError
|
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
from ansible.plugins.loader import lookup_loader
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
from plugins.filter.resource_filter import resource_filter
|
|
|
|
DEVICE_FACT = "sys_svc_container_nvidia_device"
|
|
|
|
|
|
def _device_present(variables: dict) -> bool:
|
|
"""Whether the target host registered an NVIDIA device node.
|
|
|
|
Args:
|
|
variables: the play's variables, carrying the register.
|
|
|
|
Returns:
|
|
False while the register is absent, which is also the answer before
|
|
``sys-svc-container`` has run.
|
|
"""
|
|
register = variables.get(DEVICE_FACT) or {}
|
|
if not isinstance(register, dict):
|
|
return False
|
|
return bool((register.get("stat") or {}).get("exists"))
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
def run(
|
|
self,
|
|
terms: list[Any],
|
|
variables: dict | None = None,
|
|
**kwargs: Any,
|
|
) -> list[bool]:
|
|
"""Return ``[bool]``: the service is GPU-accelerated on this host.
|
|
|
|
Args:
|
|
terms: ``application_id`` and optionally the service name; the
|
|
service defaults to the application's own entity.
|
|
variables: the play's variables.
|
|
|
|
Raises:
|
|
AnsibleError: wrong number of terms.
|
|
"""
|
|
if not 1 <= len(terms) <= 2:
|
|
raise AnsibleError(
|
|
"gpu: expected 1 or 2 terms: lookup('gpu', application_id[, service])"
|
|
)
|
|
application_id = str(terms[0])
|
|
service_name = str(terms[1]) if len(terms) == 2 else ""
|
|
|
|
templar = getattr(self, "_templar", None)
|
|
variables = variables or getattr(templar, "available_variables", {}) or {}
|
|
|
|
applications = lookup_loader.get(
|
|
"applications", loader=self._loader, templar=templar
|
|
).run([], variables=variables)[0]
|
|
|
|
wanted = resource_filter(
|
|
applications, application_id, "gpu", service_name, False
|
|
)
|
|
return [boolean(wanted, strict=False) and _device_present(variables)]
|