597 lines
24 KiB
Python
597 lines
24 KiB
Python
"""Strict loader for the local XGRIDS K1 compatibility profile.
|
|
|
|
The profile is descriptive and fail-closed. Loading it never performs device
|
|
I/O and never grants write authority to a transport implementation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
PROFILE_SCHEMA_VERSION = 1
|
|
DEFAULT_PROFILE_ID = "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1"
|
|
DEFAULT_PROFILE_PATH = Path(__file__).parent / "profiles" / "fw-3.0.2" / "direct-lan.v1.json"
|
|
EVIDENCE_FLAGS = (
|
|
"observed",
|
|
"decoded",
|
|
"replay_verified",
|
|
"physical_verified",
|
|
"write_enabled",
|
|
)
|
|
|
|
|
|
class CompatibilityProfileError(ValueError):
|
|
"""The compatibility profile is malformed or exceeds its reviewed scope."""
|
|
|
|
|
|
def _object(value: Any, path: str) -> dict[str, Any]:
|
|
if not isinstance(value, dict):
|
|
raise CompatibilityProfileError(f"{path} must be an object")
|
|
return value
|
|
|
|
|
|
def _array(value: Any, path: str) -> list[Any]:
|
|
if not isinstance(value, list):
|
|
raise CompatibilityProfileError(f"{path} must be an array")
|
|
return value
|
|
|
|
|
|
def _string(value: Any, path: str) -> str:
|
|
if not isinstance(value, str) or not value:
|
|
raise CompatibilityProfileError(f"{path} must be a non-empty string")
|
|
return value
|
|
|
|
|
|
def _unique_index(items: list[Any], path: str) -> dict[str, dict[str, Any]]:
|
|
index: dict[str, dict[str, Any]] = {}
|
|
for position, value in enumerate(items):
|
|
item_path = f"{path}[{position}]"
|
|
item = _object(value, item_path)
|
|
item_id = _string(item.get("id"), f"{item_path}.id")
|
|
if item_id in index:
|
|
raise CompatibilityProfileError(f"{path} contains duplicate id {item_id!r}")
|
|
index[item_id] = item
|
|
return index
|
|
|
|
|
|
def _validate_evidence(value: Any, path: str) -> dict[str, bool]:
|
|
evidence = _object(value, path)
|
|
if set(evidence) != set(EVIDENCE_FLAGS):
|
|
expected = ", ".join(EVIDENCE_FLAGS)
|
|
raise CompatibilityProfileError(f"{path} must contain exactly: {expected}")
|
|
validated: dict[str, bool] = {}
|
|
for flag in EVIDENCE_FLAGS:
|
|
flag_value = evidence[flag]
|
|
if not isinstance(flag_value, bool):
|
|
raise CompatibilityProfileError(f"{path}.{flag} must be a boolean")
|
|
validated[flag] = flag_value
|
|
if evidence["write_enabled"]:
|
|
raise CompatibilityProfileError(f"{path}.write_enabled must remain false in v1")
|
|
return validated
|
|
|
|
|
|
def _walk_and_validate_evidence(value: Any, path: str = "$") -> None:
|
|
if isinstance(value, dict):
|
|
if "evidence" in value:
|
|
_validate_evidence(value["evidence"], f"{path}.evidence")
|
|
for key, child in value.items():
|
|
child_path = f"{path}.{key}"
|
|
if key == "write_enabled" and path != "$.evidence_vocabulary":
|
|
if not isinstance(child, bool):
|
|
raise CompatibilityProfileError(f"{child_path} must be a boolean")
|
|
if child:
|
|
raise CompatibilityProfileError(f"{child_path} must remain false in v1")
|
|
_walk_and_validate_evidence(child, child_path)
|
|
elif isinstance(value, list):
|
|
for position, child in enumerate(value):
|
|
_walk_and_validate_evidence(child, f"{path}[{position}]")
|
|
|
|
|
|
def _expect_evidence(
|
|
item: dict[str, Any],
|
|
path: str,
|
|
*,
|
|
observed: bool,
|
|
decoded: bool,
|
|
replay_verified: bool,
|
|
physical_verified: bool,
|
|
) -> None:
|
|
actual = _validate_evidence(item.get("evidence"), f"{path}.evidence")
|
|
expected = {
|
|
"observed": observed,
|
|
"decoded": decoded,
|
|
"replay_verified": replay_verified,
|
|
"physical_verified": physical_verified,
|
|
"write_enabled": False,
|
|
}
|
|
if actual != expected:
|
|
raise CompatibilityProfileError(
|
|
f"{path}.evidence exceeds or contradicts the reviewed v1 evidence"
|
|
)
|
|
|
|
|
|
def _validate_sources(profile: dict[str, Any]) -> set[str]:
|
|
sources = _unique_index(
|
|
_array(profile.get("evidence_sources"), "$.evidence_sources"),
|
|
"$.evidence_sources",
|
|
)
|
|
for source_id, source in sources.items():
|
|
_string(source.get("kind"), f"$.evidence_sources[{source_id!r}].kind")
|
|
path = _string(source.get("path"), f"$.evidence_sources[{source_id!r}].path")
|
|
if path.startswith("/") or ".." in Path(path).parts:
|
|
raise CompatibilityProfileError(
|
|
f"$.evidence_sources[{source_id!r}].path must be repository-relative"
|
|
)
|
|
_string(source.get("scope"), f"$.evidence_sources[{source_id!r}].scope")
|
|
return set(sources)
|
|
|
|
|
|
def _validate_source_references(value: Any, source_ids: set[str], path: str = "$") -> None:
|
|
if isinstance(value, dict):
|
|
if "source_ids" in value:
|
|
references = _array(value["source_ids"], f"{path}.source_ids")
|
|
if len(references) != len(set(references)):
|
|
raise CompatibilityProfileError(f"{path}.source_ids contains duplicates")
|
|
for position, source_id in enumerate(references):
|
|
source_id = _string(source_id, f"{path}.source_ids[{position}]")
|
|
if source_id not in source_ids:
|
|
raise CompatibilityProfileError(
|
|
f"{path}.source_ids[{position}] references unknown evidence source"
|
|
)
|
|
for key, child in value.items():
|
|
_validate_source_references(child, source_ids, f"{path}.{key}")
|
|
elif isinstance(value, list):
|
|
for position, child in enumerate(value):
|
|
_validate_source_references(child, source_ids, f"{path}[{position}]")
|
|
|
|
|
|
def _validate_transports(profile: dict[str, Any]) -> None:
|
|
transports = _unique_index(
|
|
_array(profile.get("transports"), "$.transports"),
|
|
"$.transports",
|
|
)
|
|
if set(transports) != {
|
|
"ble.wifi-bootstrap.fw3.v1",
|
|
"mqtt.direct-lan.fw3.v1",
|
|
"rtsp.camera-preview.fw3.v1",
|
|
}:
|
|
raise CompatibilityProfileError("$.transports must contain only the reviewed v1 transports")
|
|
|
|
ble = transports["ble.wifi-bootstrap.fw3.v1"]
|
|
if ble.get("service_uuid") != "00007f00-0000-1000-8000-00805f9b34fb":
|
|
raise CompatibilityProfileError("BLE service UUID differs from reviewed evidence")
|
|
characteristics = _object(ble.get("characteristics"), "$.transports[ble].characteristics")
|
|
if characteristics != {
|
|
"wifi_request": "00007f01-0000-1000-8000-00805f9b34fb",
|
|
"wifi_status": "00007f02-0000-1000-8000-00805f9b34fb",
|
|
}:
|
|
raise CompatibilityProfileError("BLE characteristic UUIDs differ from reviewed evidence")
|
|
if ble.get("request_frame_bytes") != 99:
|
|
raise CompatibilityProfileError("BLE provisioning frame must remain exactly 99 bytes")
|
|
_expect_evidence(
|
|
ble,
|
|
"$.transports[ble.wifi-bootstrap.fw3.v1]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
mqtt = transports["mqtt.direct-lan.fw3.v1"]
|
|
if mqtt.get("protocol") != "MQTT 3.1.1":
|
|
raise CompatibilityProfileError("direct-LAN application protocol must remain MQTT 3.1.1")
|
|
network = _object(mqtt.get("network"), "$.transports[mqtt].network")
|
|
if network.get("transport") != "TCP" or network.get("port") != 1883:
|
|
raise CompatibilityProfileError("direct-LAN MQTT endpoint must remain TCP 1883")
|
|
if network.get("tls") is not False or network.get("authentication") != "none-observed":
|
|
raise CompatibilityProfileError("direct-LAN MQTT security claim differs from observation")
|
|
allowlist = _array(
|
|
mqtt.get("subscription_allowlist"),
|
|
"$.transports[mqtt].subscription_allowlist",
|
|
)
|
|
if "lixel/application/report/#" not in allowlist:
|
|
raise CompatibilityProfileError("MQTT report-topic allowlist is missing")
|
|
for topic in allowlist:
|
|
topic = _string(topic, "$.transports[mqtt].subscription_allowlist[]")
|
|
if "/request/" in topic:
|
|
raise CompatibilityProfileError(
|
|
"request topics cannot enter the subscribe-only profile"
|
|
)
|
|
_expect_evidence(
|
|
mqtt,
|
|
"$.transports[mqtt.direct-lan.fw3.v1]",
|
|
observed=True,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
rtsp = transports["rtsp.camera-preview.fw3.v1"]
|
|
if rtsp.get("protocol") != "RTSP 1.0 with interleaved RTP over TCP":
|
|
raise CompatibilityProfileError("camera-preview protocol differs from observation")
|
|
rtsp_network = _object(rtsp.get("network"), "$.transports[rtsp].network")
|
|
if rtsp_network.get("transport") != "TCP" or rtsp_network.get("port") != 8554:
|
|
raise CompatibilityProfileError("camera-preview endpoint must remain TCP 8554")
|
|
if (
|
|
rtsp_network.get("tls") is not False
|
|
or rtsp_network.get("authentication") != "none-observed"
|
|
):
|
|
raise CompatibilityProfileError("camera-preview security differs from observation")
|
|
media = _object(rtsp.get("media"), "$.transports[rtsp].media")
|
|
if media != {
|
|
"codec": "H.264",
|
|
"rtp_payload_type": 96,
|
|
"clock_hz": 90000,
|
|
"framing": "RTP/AVP/TCP interleaved channels 0-1",
|
|
}:
|
|
raise CompatibilityProfileError("camera-preview media contract differs from observation")
|
|
_expect_evidence(
|
|
rtsp,
|
|
"$.transports[rtsp.camera-preview.fw3.v1]",
|
|
observed=True,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
|
|
def _validate_channels(profile: dict[str, Any]) -> None:
|
|
channels = _unique_index(_array(profile.get("channels"), "$.channels"), "$.channels")
|
|
expected_ids = {
|
|
"spatial.point-cloud.live",
|
|
"spatial.pose.live",
|
|
"device.modeling.live",
|
|
"device.status.live",
|
|
"device.heartbeat.live",
|
|
"camera.preview.live",
|
|
}
|
|
if set(channels) != expected_ids:
|
|
raise CompatibilityProfileError("$.channels differs from the reviewed v1 channel set")
|
|
|
|
point = channels["spatial.point-cloud.live"]
|
|
if point.get("topic") != "lixel/application/report/lio_pcl":
|
|
raise CompatibilityProfileError("point-cloud topic differs from reviewed evidence")
|
|
_expect_evidence(
|
|
point,
|
|
"$.channels[spatial.point-cloud.live]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=True,
|
|
physical_verified=True,
|
|
)
|
|
|
|
pose = channels["spatial.pose.live"]
|
|
if pose.get("topic") != "lixel/application/report/lio_pose":
|
|
raise CompatibilityProfileError("pose topic differs from reviewed evidence")
|
|
_expect_evidence(
|
|
pose,
|
|
"$.channels[spatial.pose.live]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=True,
|
|
physical_verified=True,
|
|
)
|
|
|
|
modeling = channels["device.modeling.live"]
|
|
if (
|
|
modeling.get("topic") != "lixel/application/report/modeling"
|
|
or modeling.get("wire_format") != "protobuf ModelingReport acquisition telemetry subset"
|
|
or modeling.get("semantic_payload")
|
|
!= (
|
|
"nonnegative MoveDistance metres, MoveSpeed metres per second, "
|
|
"int64 ScanTime at two ticks per second, and int32 PgoProgress"
|
|
)
|
|
or modeling.get("bounds")
|
|
!= {
|
|
"max_mqtt_payload_bytes": 65_536,
|
|
"max_fields": 64,
|
|
"max_nested_fields": 16,
|
|
}
|
|
):
|
|
raise CompatibilityProfileError("modeling telemetry channel differs from evidence")
|
|
_expect_evidence(
|
|
modeling,
|
|
"$.channels[device.modeling.live]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
status = channels["device.status.live"]
|
|
if (
|
|
status.get("topic") != "lixel/application/report/device_status"
|
|
or status.get("wire_format") != "protobuf DeviceStatusReport acquisition lifecycle subset"
|
|
or status.get("semantic_payload")
|
|
!= (
|
|
"bounded modeling-state base-offset mapping, init-ready flag, "
|
|
"project presence and redacted identity fields"
|
|
)
|
|
):
|
|
raise CompatibilityProfileError("device-status channel differs from evidence")
|
|
_expect_evidence(
|
|
status,
|
|
"$.channels[device.status.live]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
heartbeat = channels["device.heartbeat.live"]
|
|
if (
|
|
heartbeat.get("topic") != "lixel/application/report/heartbeat"
|
|
or heartbeat.get("semantic_payload") is not None
|
|
):
|
|
raise CompatibilityProfileError("device.heartbeat.live must remain raw-only in v1")
|
|
_expect_evidence(
|
|
heartbeat,
|
|
"$.channels[device.heartbeat.live]",
|
|
observed=True,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
camera = channels["camera.preview.live"]
|
|
if camera.get("discovery_status") != "observed":
|
|
raise CompatibilityProfileError("camera discovery must match the owner-controlled run")
|
|
if camera.get("topic") is not None:
|
|
raise CompatibilityProfileError("camera preview is not an MQTT topic")
|
|
expected_endpoints = {
|
|
"rtsp://{confirmed-device-private-ipv4}:8554/live/chn_left_main",
|
|
"rtsp://{confirmed-device-private-ipv4}:8554/live/chn_right_main",
|
|
}
|
|
endpoints = _array(camera.get("endpoint_templates"), "$.channels[camera].endpoint_templates")
|
|
if set(endpoints) != expected_endpoints or len(endpoints) != len(expected_endpoints):
|
|
raise CompatibilityProfileError("camera endpoint templates differ from observation")
|
|
if camera.get("wire_format") != "RTSP 1.0, interleaved RTP/TCP, H.264 PT96 at 90000 Hz":
|
|
raise CompatibilityProfileError("camera wire format differs from observation")
|
|
_string(camera.get("semantic_payload"), "$.channels[camera].semantic_payload")
|
|
_expect_evidence(
|
|
camera,
|
|
"$.channels[camera.preview.live]",
|
|
observed=True,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
|
|
def _validate_acquisition_control(profile: dict[str, Any]) -> None:
|
|
control = _object(profile.get("acquisition_control"), "$.acquisition_control")
|
|
if control.get("mode") != "operator-manual" or control.get("write_enabled") is not False:
|
|
raise CompatibilityProfileError("acquisition control must remain operator-manual")
|
|
|
|
acceptance_transport = _object(
|
|
control.get("software_acceptance_transport"),
|
|
"$.acquisition_control.software_acceptance_transport",
|
|
)
|
|
if acceptance_transport != {
|
|
"status": "installed-operator-present",
|
|
"default_authority": "disabled",
|
|
"profile_gate": "live-device-info-exact-match",
|
|
"dialogue": "single-socket-canonical-start-to-stop",
|
|
"automatic_retry": False,
|
|
"supported_mount_type": "handheld",
|
|
"supported_gnss_mode": "none",
|
|
}:
|
|
raise CompatibilityProfileError(
|
|
"software acceptance transport differs from the reviewed operator-present contract"
|
|
)
|
|
|
|
device_control = _object(
|
|
control.get("verified_device_control"),
|
|
"$.acquisition_control.verified_device_control",
|
|
)
|
|
if device_control.get("gesture") != "physical-double-click":
|
|
raise CompatibilityProfileError("physical acquisition gesture differs from lab evidence")
|
|
_expect_evidence(
|
|
device_control,
|
|
"$.acquisition_control.verified_device_control",
|
|
observed=True,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
actions = _unique_index(
|
|
_array(control.get("semantic_actions"), "$.acquisition_control.semantic_actions"),
|
|
"$.acquisition_control.semantic_actions",
|
|
)
|
|
if set(actions) != {
|
|
"acquisition.start",
|
|
"acquisition.stop",
|
|
"calibration.device.start",
|
|
}:
|
|
raise CompatibilityProfileError("semantic action set differs from reviewed v1")
|
|
|
|
for action_id, action_value in (("acquisition.start", 1), ("acquisition.stop", 2)):
|
|
action = actions[action_id]
|
|
if action.get("execution") != "operator-manual":
|
|
raise CompatibilityProfileError(f"{action_id} must remain operator-manual")
|
|
mapping = _object(
|
|
action.get("vendor_request_mapping"),
|
|
f"$.acquisition_control.semantic_actions[{action_id}].vendor_request_mapping",
|
|
)
|
|
if mapping.get("evidence_kind") != "owner-controlled-wire-observation":
|
|
raise CompatibilityProfileError(f"{action_id} vendor mapping differs from evidence")
|
|
if mapping.get("transport") != "MQTT 3.1.1":
|
|
raise CompatibilityProfileError(f"{action_id} transport differs from evidence")
|
|
if mapping.get("message_type") != "ModelingRequest":
|
|
raise CompatibilityProfileError(f"{action_id} message type differs from evidence")
|
|
if mapping.get("write_enabled") is not False:
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} vendor mapping must explicitly remain write-disabled"
|
|
)
|
|
if mapping.get("topic") != "lixel/application/request/modeling":
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} vendor topic differs from static evidence"
|
|
)
|
|
if (
|
|
mapping.get("qos") != 2
|
|
or mapping.get("retain") is not False
|
|
or mapping.get("action_field_value") != action_value
|
|
):
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} vendor mapping differs from static evidence"
|
|
)
|
|
if mapping.get("header_contract") != {
|
|
"device_id": "explicit-observed-identity",
|
|
"session_id": "{device_id}:ModelingRequest",
|
|
"openapi_key": "private-application-level-runtime-authority",
|
|
}:
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} header contract differs from retained evidence"
|
|
)
|
|
expected_request_fields: dict[str, object] = (
|
|
{
|
|
"project_name": "required-operator-value",
|
|
"record_mode": 2,
|
|
"scan_mode": 1,
|
|
"mount_type": 0,
|
|
"pre_project_id": "omitted-in-retained-request",
|
|
}
|
|
if action_id == "acquisition.start"
|
|
else {}
|
|
)
|
|
if mapping.get("request_fields") != expected_request_fields:
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} request fields differ from retained evidence"
|
|
)
|
|
if mapping.get("success_result_code") != 302_252_033:
|
|
raise CompatibilityProfileError(
|
|
f"{action_id} success result differs from retained evidence"
|
|
)
|
|
if not _array(
|
|
mapping.get("required_unresolved_context"),
|
|
f"$.acquisition_control.semantic_actions[{action_id}].required_unresolved_context",
|
|
):
|
|
raise CompatibilityProfileError(f"{action_id} must declare unresolved request context")
|
|
_expect_evidence(
|
|
mapping,
|
|
f"$.acquisition_control.semantic_actions[{action_id}].vendor_request_mapping",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
_expect_evidence(
|
|
action,
|
|
f"$.acquisition_control.semantic_actions[{action_id}]",
|
|
observed=True,
|
|
decoded=True,
|
|
replay_verified=False,
|
|
physical_verified=True,
|
|
)
|
|
|
|
calibration = actions["calibration.device.start"]
|
|
if calibration.get("execution") != "unavailable":
|
|
raise CompatibilityProfileError("calibration must remain unavailable in v1")
|
|
if calibration.get("vendor_request_mapping") is not None:
|
|
raise CompatibilityProfileError("calibration vendor request is not evidenced")
|
|
_expect_evidence(
|
|
calibration,
|
|
"$.acquisition_control.semantic_actions[calibration.device.start]",
|
|
observed=False,
|
|
decoded=False,
|
|
replay_verified=False,
|
|
physical_verified=False,
|
|
)
|
|
|
|
|
|
def validate_compatibility_profile(profile: Any) -> dict[str, Any]:
|
|
"""Validate and return one read-only firmware-3/direct-LAN profile object."""
|
|
root = _object(profile, "$")
|
|
if root.get("schema_version") != PROFILE_SCHEMA_VERSION:
|
|
raise CompatibilityProfileError("unsupported compatibility profile schema_version")
|
|
if root.get("profile_id") != DEFAULT_PROFILE_ID:
|
|
raise CompatibilityProfileError("unexpected compatibility profile_id")
|
|
|
|
scope = _object(root.get("scope"), "$.scope")
|
|
if scope.get("vendor") != "XGRIDS" or scope.get("model") != "LixelKity K1":
|
|
raise CompatibilityProfileError("profile vendor/model must remain XGRIDS LixelKity K1")
|
|
if scope.get("platform_type") != "A4":
|
|
raise CompatibilityProfileError("profile platform type must remain the observed A4")
|
|
firmware = _object(scope.get("firmware"), "$.scope.firmware")
|
|
if firmware != {"match": "exact", "version": "3.0.2"}:
|
|
raise CompatibilityProfileError("profile must match firmware 3.0.2 exactly")
|
|
if scope.get("topology") != "direct-lan":
|
|
raise CompatibilityProfileError("profile topology must remain direct-lan")
|
|
|
|
vocabulary = _object(root.get("evidence_vocabulary"), "$.evidence_vocabulary")
|
|
if set(vocabulary) != set(EVIDENCE_FLAGS):
|
|
raise CompatibilityProfileError("evidence vocabulary differs from schema v1")
|
|
for flag in EVIDENCE_FLAGS:
|
|
_string(vocabulary[flag], f"$.evidence_vocabulary.{flag}")
|
|
|
|
safety = _object(root.get("safety"), "$.safety")
|
|
if safety.get("default_mode") != "read-only":
|
|
raise CompatibilityProfileError("profile default mode must remain read-only")
|
|
if safety.get("vendor_writes_enabled") is not False:
|
|
raise CompatibilityProfileError("profile must not enable vendor writes")
|
|
if safety.get("unknown_firmware_policy") != "reject-profile":
|
|
raise CompatibilityProfileError("unknown firmware must fail closed")
|
|
if safety.get("request_topic_subscription_enabled") is not False:
|
|
raise CompatibilityProfileError("request-topic subscription must remain disabled")
|
|
|
|
source_ids = _validate_sources(root)
|
|
_validate_source_references(root, source_ids)
|
|
_walk_and_validate_evidence(root)
|
|
_validate_transports(root)
|
|
_validate_channels(root)
|
|
_validate_acquisition_control(root)
|
|
return root
|
|
|
|
|
|
def load_compatibility_profile(path: Path | str = DEFAULT_PROFILE_PATH) -> dict[str, Any]:
|
|
"""Load a JSON profile, rejecting duplicate keys and unreviewed claims."""
|
|
resolved = Path(path).expanduser().resolve()
|
|
|
|
def reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
result: dict[str, Any] = {}
|
|
for key, value in pairs:
|
|
if key in result:
|
|
raise CompatibilityProfileError(f"duplicate JSON key {key!r}")
|
|
result[key] = value
|
|
return result
|
|
|
|
try:
|
|
raw = resolved.read_text(encoding="utf-8")
|
|
except OSError as exc:
|
|
raise CompatibilityProfileError(f"cannot read compatibility profile: {resolved}") from exc
|
|
try:
|
|
profile = json.loads(raw, object_pairs_hook=reject_duplicate_keys)
|
|
except json.JSONDecodeError as exc:
|
|
raise CompatibilityProfileError(f"invalid compatibility profile JSON: {exc}") from exc
|
|
return validate_compatibility_profile(profile)
|
|
|
|
|
|
def matches_target(
|
|
profile: dict[str, Any],
|
|
*,
|
|
firmware: str,
|
|
topology: str,
|
|
) -> bool:
|
|
"""Return whether an already validated exact-match profile covers the target."""
|
|
validated = validate_compatibility_profile(profile)
|
|
scope = validated["scope"]
|
|
firmware_scope = _object(scope.get("firmware"), "$.scope.firmware")
|
|
return firmware_scope.get("version") == firmware and scope.get("topology") == topology
|
|
|
|
|
|
def _main() -> int:
|
|
parser = argparse.ArgumentParser(description="Validate an XGRIDS K1 compatibility profile")
|
|
parser.add_argument("path", nargs="?", type=Path, default=DEFAULT_PROFILE_PATH)
|
|
args = parser.parse_args()
|
|
profile = load_compatibility_profile(args.path)
|
|
print(profile["profile_id"])
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(_main())
|