meta-digi-containers: generate DCP with DRM policy block
Align the policy DRM block with the latest changes done in the cc-container-mng tool. https://onedigi.atlassian.net/browse/DEL-10075 Signed-off-by: Isaac Hermida <isaac.hermida@digi.com>
This commit is contained in:
parent
408a8bec60
commit
2fa5427622
|
|
@ -98,24 +98,36 @@ This generates a bundle named like:
|
||||||
|
|
||||||
`dey-image-container-manager` includes `cc-container-mng` that has the capability to
|
`dey-image-container-manager` includes `cc-container-mng` that has the capability to
|
||||||
publish container statistics through the local CCCS Python API.
|
publish container statistics through the local CCCS Python API.
|
||||||
For generated DCPs, per-container DRM sampling is enabled
|
For generated DCPs, DRM behavior is controlled through `registration_defaults.drm`
|
||||||
through `registration_defaults.stats_publish` in the artifact manifest.
|
in the artifact manifest.
|
||||||
Those manifest defaults establish the initial runtime policy on the target.
|
Those manifest defaults establish the initial runtime policy on the target.
|
||||||
After installation, mutable policy such as `autostart`, `monitor`, `restart`, and
|
After installation, mutable policy such as `autostart`, `monitor`, `restart`, and
|
||||||
`stats_publish` can be inspected or updated through the container manager `config`
|
`drm` can be inspected or updated through the container manager `config`
|
||||||
`get` and `set` operations without regenerating the DCP.
|
`get` and `set` operations without regenerating the DCP.
|
||||||
The image recipe generates the DCP automatically from the following variables:
|
The image recipe generates the DCP automatically from the following variables:
|
||||||
|
|
||||||
- `CONTAINER_STATS_PUBLISH_ENABLED`
|
- `CONTAINER_DRM_ENABLED`
|
||||||
- `CONTAINER_STATS_PUBLISH_SAMPLE_INTERVAL`
|
- `CONTAINER_DRM_STATS_SAMPLE_INTERVAL`
|
||||||
|
- `CONTAINER_DRM_STATS_LIST_OF_METRICS`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
CONTAINER_STATS_PUBLISH_ENABLED = "true"
|
CONTAINER_DRM_ENABLED = "true"
|
||||||
CONTAINER_STATS_PUBLISH_SAMPLE_INTERVAL = "30"
|
CONTAINER_DRM_STATS_SAMPLE_INTERVAL = "30"
|
||||||
|
CONTAINER_DRM_STATS_LIST_OF_METRICS = "[\"cpu\", \"mem\"]"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`CONTAINER_DRM_STATS_LIST_OF_METRICS` follows the same semantics as the target
|
||||||
|
manager:
|
||||||
|
|
||||||
|
- `["all"]` is accepted as a shorthand input for all periodic metrics.
|
||||||
|
- `[]` publishes no periodic metrics.
|
||||||
|
- any other list publishes only the selected metrics.
|
||||||
|
|
||||||
|
Generated manifests and target-side effective configuration use the explicit
|
||||||
|
metric list.
|
||||||
|
|
||||||
## Layer Scope
|
## Layer Scope
|
||||||
|
|
||||||
Main recipes:
|
Main recipes:
|
||||||
|
|
@ -294,8 +306,9 @@ Relevant variables:
|
||||||
- `CONTAINER_PACKAGE_ID`
|
- `CONTAINER_PACKAGE_ID`
|
||||||
- `CONTAINER_ARTIFACT_VERSION`
|
- `CONTAINER_ARTIFACT_VERSION`
|
||||||
- `CONTAINER_CREATE_ARGS_PODMAN`
|
- `CONTAINER_CREATE_ARGS_PODMAN`
|
||||||
- `CONTAINER_STATS_PUBLISH_ENABLED`
|
- `CONTAINER_DRM_ENABLED`
|
||||||
- `CONTAINER_STATS_PUBLISH_SAMPLE_INTERVAL`
|
- `CONTAINER_DRM_STATS_SAMPLE_INTERVAL`
|
||||||
|
- `CONTAINER_DRM_STATS_LIST_OF_METRICS`
|
||||||
- `CONTAINER_FIRMWARE_VERSIONS`
|
- `CONTAINER_FIRMWARE_VERSIONS`
|
||||||
- `CONTAINER_DEVICE_TYPES_JSON`
|
- `CONTAINER_DEVICE_TYPES_JSON`
|
||||||
- `CONTAINER_ARTIFACT_DESCRIPTION`
|
- `CONTAINER_ARTIFACT_DESCRIPTION`
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,19 @@
|
||||||
"registration_defaults": {
|
"registration_defaults": {
|
||||||
"autostart": true,
|
"autostart": true,
|
||||||
"monitor": true,
|
"monitor": true,
|
||||||
|
"drm": {
|
||||||
|
"enabled": true,
|
||||||
|
"stats_sample_interval_s": 30,
|
||||||
|
"stats_list_of_metrics": [
|
||||||
|
"uptime",
|
||||||
|
"cpu",
|
||||||
|
"mem",
|
||||||
|
"net/rx",
|
||||||
|
"net/tx",
|
||||||
|
"disk/read",
|
||||||
|
"disk/write"
|
||||||
|
]
|
||||||
|
},
|
||||||
"restart": {
|
"restart": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"max_retries": 3,
|
"max_retries": 3,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,19 @@
|
||||||
"registration_defaults": {
|
"registration_defaults": {
|
||||||
"autostart": true,
|
"autostart": true,
|
||||||
"monitor": true,
|
"monitor": true,
|
||||||
|
"drm": {
|
||||||
|
"enabled": true,
|
||||||
|
"stats_sample_interval_s": 30,
|
||||||
|
"stats_list_of_metrics": [
|
||||||
|
"uptime",
|
||||||
|
"cpu",
|
||||||
|
"mem",
|
||||||
|
"net/rx",
|
||||||
|
"net/tx",
|
||||||
|
"disk/read",
|
||||||
|
"disk/write"
|
||||||
|
]
|
||||||
|
},
|
||||||
"restart": {
|
"restart": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"max_retries": 3,
|
"max_retries": 3,
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,9 @@ do_image_container_artifacts() {
|
||||||
CREATE_ARGS="${create_args}" \
|
CREATE_ARGS="${create_args}" \
|
||||||
AUTOSTART="${CONTAINER_AUTOSTART}" \
|
AUTOSTART="${CONTAINER_AUTOSTART}" \
|
||||||
MONITOR="${CONTAINER_MONITOR}" \
|
MONITOR="${CONTAINER_MONITOR}" \
|
||||||
STATS_PUBLISH_ENABLED="${CONTAINER_STATS_PUBLISH_ENABLED}" \
|
DRM_ENABLED="${CONTAINER_DRM_ENABLED}" \
|
||||||
STATS_PUBLISH_SAMPLE_INTERVAL="${CONTAINER_STATS_PUBLISH_SAMPLE_INTERVAL}" \
|
DRM_STATS_SAMPLE_INTERVAL="${CONTAINER_DRM_STATS_SAMPLE_INTERVAL}" \
|
||||||
|
DRM_STATS_LIST_OF_METRICS="${CONTAINER_DRM_STATS_LIST_OF_METRICS}" \
|
||||||
RESTART_ENABLED="${CONTAINER_RESTART_ENABLED}" \
|
RESTART_ENABLED="${CONTAINER_RESTART_ENABLED}" \
|
||||||
RESTART_MAX_RETRIES="${CONTAINER_RESTART_MAX_RETRIES}" \
|
RESTART_MAX_RETRIES="${CONTAINER_RESTART_MAX_RETRIES}" \
|
||||||
RESTART_WINDOW="${CONTAINER_RESTART_WINDOW}" \
|
RESTART_WINDOW="${CONTAINER_RESTART_WINDOW}" \
|
||||||
|
|
@ -66,7 +67,7 @@ do_image_container_artifacts() {
|
||||||
LABELS_JSON="${CONTAINER_ARTIFACT_LABELS_JSON}" \
|
LABELS_JSON="${CONTAINER_ARTIFACT_LABELS_JSON}" \
|
||||||
python3 -c 'import json, os, sys; \
|
python3 -c 'import json, os, sys; \
|
||||||
parse_bool = lambda name: os.environ[name].strip().lower() == "true"; \
|
parse_bool = lambda name: os.environ[name].strip().lower() == "true"; \
|
||||||
payload = {"package_id": os.environ["PACKAGE_ID"], "version": os.environ["VERSION"], "runtime": os.environ["RUNTIME"], "registration_defaults": {"autostart": parse_bool("AUTOSTART"), "monitor": parse_bool("MONITOR"), "stats_publish": {"enabled": parse_bool("STATS_PUBLISH_ENABLED"), "sample_interval_s": int(os.environ["STATS_PUBLISH_SAMPLE_INTERVAL"])}, "restart": {"enabled": parse_bool("RESTART_ENABLED"), "max_retries": int(os.environ["RESTART_MAX_RETRIES"]), "window": int(os.environ["RESTART_WINDOW"]), "retry_delay": int(os.environ["RESTART_RETRY_DELAY"]) } }, "device_types": json.loads(os.environ["DEVICE_TYPES_JSON"]), "firmware_versions": os.environ["FIRMWARE_VERSIONS"], "build_id": os.environ["BUILD_ID"], "description": os.environ["DESCRIPTION"], "labels": json.loads(os.environ["LABELS_JSON"])}; \
|
payload = {"package_id": os.environ["PACKAGE_ID"], "version": os.environ["VERSION"], "runtime": os.environ["RUNTIME"], "registration_defaults": {"autostart": parse_bool("AUTOSTART"), "monitor": parse_bool("MONITOR"), "drm": {"enabled": parse_bool("DRM_ENABLED"), "stats_sample_interval_s": int(os.environ["DRM_STATS_SAMPLE_INTERVAL"]), "stats_list_of_metrics": json.loads(os.environ["DRM_STATS_LIST_OF_METRICS"])}, "restart": {"enabled": parse_bool("RESTART_ENABLED"), "max_retries": int(os.environ["RESTART_MAX_RETRIES"]), "window": int(os.environ["RESTART_WINDOW"]), "retry_delay": int(os.environ["RESTART_RETRY_DELAY"]) } }, "device_types": json.loads(os.environ["DEVICE_TYPES_JSON"]), "firmware_versions": os.environ["FIRMWARE_VERSIONS"], "build_id": os.environ["BUILD_ID"], "description": os.environ["DESCRIPTION"], "labels": json.loads(os.environ["LABELS_JSON"])}; \
|
||||||
create_args = os.environ["CREATE_ARGS"].strip(); \
|
create_args = os.environ["CREATE_ARGS"].strip(); \
|
||||||
payload.update({"create_args": create_args} if create_args else {}); \
|
payload.update({"create_args": create_args} if create_args else {}); \
|
||||||
open(sys.argv[1], "w", encoding="utf-8").write(json.dumps(payload, indent=2) + "\n")' \
|
open(sys.argv[1], "w", encoding="utf-8").write(json.dumps(payload, indent=2) + "\n")' \
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,9 @@ CONTAINER_ARTIFACT_LABELS_JSON ?= "{}"
|
||||||
# monitor and (auto/re)start configuration
|
# monitor and (auto/re)start configuration
|
||||||
CONTAINER_AUTOSTART ?= "true"
|
CONTAINER_AUTOSTART ?= "true"
|
||||||
CONTAINER_MONITOR ?= "true"
|
CONTAINER_MONITOR ?= "true"
|
||||||
CONTAINER_STATS_PUBLISH_ENABLED ?= "true"
|
CONTAINER_DRM_ENABLED ?= "true"
|
||||||
CONTAINER_STATS_PUBLISH_SAMPLE_INTERVAL ?= "30"
|
CONTAINER_DRM_STATS_SAMPLE_INTERVAL ?= "30"
|
||||||
|
CONTAINER_DRM_STATS_LIST_OF_METRICS ?= "[\"uptime\", \"cpu\", \"mem\", \"net/rx\", \"net/tx\", \"disk/read\", \"disk/write\"]"
|
||||||
CONTAINER_RESTART_ENABLED ?= "true"
|
CONTAINER_RESTART_ENABLED ?= "true"
|
||||||
CONTAINER_RESTART_MAX_RETRIES ?= "3"
|
CONTAINER_RESTART_MAX_RETRIES ?= "3"
|
||||||
CONTAINER_RESTART_WINDOW ?= "60"
|
CONTAINER_RESTART_WINDOW ?= "60"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,16 @@ import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
|
BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||||
|
SUPPORTED_STATS_METRICS = (
|
||||||
|
"uptime",
|
||||||
|
"cpu",
|
||||||
|
"mem",
|
||||||
|
"net/rx",
|
||||||
|
"net/tx",
|
||||||
|
"disk/read",
|
||||||
|
"disk/write",
|
||||||
|
)
|
||||||
|
SUPPORTED_STATS_METRICS_SET = set(SUPPORTED_STATS_METRICS)
|
||||||
|
|
||||||
|
|
||||||
def fail(message: str) -> "NoReturn":
|
def fail(message: str) -> "NoReturn":
|
||||||
|
|
@ -100,6 +110,34 @@ def validate_labels(value: object) -> dict:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_string_list(value: object, *, path: str, allowed: set[str]) -> list[str]:
|
||||||
|
if value is None:
|
||||||
|
return []
|
||||||
|
if not isinstance(value, list):
|
||||||
|
fail(f"invalid manifest: {path} must be an array")
|
||||||
|
result: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for item in value:
|
||||||
|
if not isinstance(item, str) or not item.strip():
|
||||||
|
fail(f"invalid manifest: {path} entries must be non-empty strings")
|
||||||
|
metric = item.strip()
|
||||||
|
if metric not in allowed:
|
||||||
|
fail(f"invalid manifest: {path} contains unsupported metric {metric}")
|
||||||
|
if metric in seen:
|
||||||
|
continue
|
||||||
|
seen.add(metric)
|
||||||
|
result.append(metric)
|
||||||
|
if "all" in result and len(result) > 1:
|
||||||
|
fail(f"invalid manifest: {path} cannot combine all with specific metrics")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_drm_stats_metrics(metrics: list[str]) -> list[str]:
|
||||||
|
if "all" in metrics:
|
||||||
|
return list(SUPPORTED_STATS_METRICS)
|
||||||
|
return metrics
|
||||||
|
|
||||||
|
|
||||||
def validate_manifest(data: dict) -> dict:
|
def validate_manifest(data: dict) -> dict:
|
||||||
package_id = validate_string(data, "package_id", path="manifest")
|
package_id = validate_string(data, "package_id", path="manifest")
|
||||||
version = validate_string(data, "version", path="manifest")
|
version = validate_string(data, "version", path="manifest")
|
||||||
|
|
@ -114,6 +152,9 @@ def validate_manifest(data: dict) -> dict:
|
||||||
restart = registration_defaults.get("restart")
|
restart = registration_defaults.get("restart")
|
||||||
if not isinstance(restart, dict):
|
if not isinstance(restart, dict):
|
||||||
fail("invalid manifest: registration_defaults.restart must be an object")
|
fail("invalid manifest: registration_defaults.restart must be an object")
|
||||||
|
drm = registration_defaults.get("drm", {})
|
||||||
|
if not isinstance(drm, dict):
|
||||||
|
fail("invalid manifest: registration_defaults.drm must be an object")
|
||||||
|
|
||||||
create_args = data.get("create_args")
|
create_args = data.get("create_args")
|
||||||
if runtime == "podman":
|
if runtime == "podman":
|
||||||
|
|
@ -135,7 +176,7 @@ def validate_manifest(data: dict) -> dict:
|
||||||
if description is not None and not isinstance(description, str):
|
if description is not None and not isinstance(description, str):
|
||||||
fail("invalid manifest: description must be a string")
|
fail("invalid manifest: description must be a string")
|
||||||
|
|
||||||
return {
|
validated = {
|
||||||
"package_id": package_id,
|
"package_id": package_id,
|
||||||
"version": version,
|
"version": version,
|
||||||
"runtime": runtime,
|
"runtime": runtime,
|
||||||
|
|
@ -143,6 +184,20 @@ def validate_manifest(data: dict) -> dict:
|
||||||
"registration_defaults": {
|
"registration_defaults": {
|
||||||
"autostart": validate_bool(registration_defaults, "autostart", path="registration_defaults"),
|
"autostart": validate_bool(registration_defaults, "autostart", path="registration_defaults"),
|
||||||
"monitor": validate_bool(registration_defaults, "monitor", path="registration_defaults"),
|
"monitor": validate_bool(registration_defaults, "monitor", path="registration_defaults"),
|
||||||
|
"drm": {
|
||||||
|
"enabled": validate_bool(drm, "enabled", path="registration_defaults.drm"),
|
||||||
|
"stats_sample_interval_s": validate_int(
|
||||||
|
drm,
|
||||||
|
"stats_sample_interval_s",
|
||||||
|
path="registration_defaults.drm",
|
||||||
|
minimum=1,
|
||||||
|
),
|
||||||
|
"stats_list_of_metrics": validate_string_list(
|
||||||
|
drm.get("stats_list_of_metrics"),
|
||||||
|
path="registration_defaults.drm.stats_list_of_metrics",
|
||||||
|
allowed=SUPPORTED_STATS_METRICS_SET | {"all"},
|
||||||
|
),
|
||||||
|
},
|
||||||
"restart": {
|
"restart": {
|
||||||
"enabled": validate_bool(restart, "enabled", path="registration_defaults.restart"),
|
"enabled": validate_bool(restart, "enabled", path="registration_defaults.restart"),
|
||||||
"max_retries": validate_int(restart, "max_retries", path="registration_defaults.restart", minimum=0),
|
"max_retries": validate_int(restart, "max_retries", path="registration_defaults.restart", minimum=0),
|
||||||
|
|
@ -156,6 +211,10 @@ def validate_manifest(data: dict) -> dict:
|
||||||
"description": description or "",
|
"description": description or "",
|
||||||
"labels": validate_labels(data.get("labels")),
|
"labels": validate_labels(data.get("labels")),
|
||||||
}
|
}
|
||||||
|
validated["registration_defaults"]["drm"]["stats_list_of_metrics"] = normalize_drm_stats_metrics(
|
||||||
|
validated["registration_defaults"]["drm"]["stats_list_of_metrics"]
|
||||||
|
)
|
||||||
|
return validated
|
||||||
|
|
||||||
|
|
||||||
def validate_payload(path: Path, runtime: str) -> None:
|
def validate_payload(path: Path, runtime: str) -> None:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue