feat(device-plane): accept core edge transport ADR

This commit is contained in:
Codex
2026-08-10 16:40:48 +03:00
parent 07224c6f0d
commit bf0bc50abb
27 changed files with 5034 additions and 26 deletions
+410 -26
View File
@@ -132,6 +132,27 @@ DEVICE_PLANE_BACKHAUL_ENROLLMENT_DIR = DEVICE_PLANE_ROOT / "enrollment"
DEVICE_PLANE_BACKHAUL_ENROLLMENT_PUBLIC_KEY_FILE = (
DEVICE_PLANE_BACKHAUL_ENROLLMENT_DIR / "device-edge-backhaul.pub"
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PUBLIC_KEY_FILE = (
DEVICE_PLANE_BACKHAUL_ENROLLMENT_DIR / "device-edge-vps-backhaul.pub"
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_REL = (
"deployment/device-plane-backhaul-vps-enrollment-v1.json"
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_ENTRIES = (
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_REL,
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_PATCH_ID = (
"device-plane-backhaul-target-tailnet-serve-20260804-002"
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_ARTIFACT_SHA256 = (
"219408705dd4d80a962ed00eeb53a69df0b9ab6458443734d5c9cd1d1f795eba"
)
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_FINGERPRINT = (
"SHA256:HHTiDYiCRxSiKjBLCip6JMSzGfLGrDz5g8SIkosJcVw"
)
DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_BACKUP = (
"device-plane-backhaul-authorized-keys-before"
)
DEVICE_PLANE_BACKHAUL_SECRET_DIR = DEVICE_PLANE_SECRET_DIR / "backhaul-target"
DEVICE_PLANE_BACKHAUL_HOST_KEY_FILE = (
DEVICE_PLANE_BACKHAUL_SECRET_DIR / "ssh_host_ed25519_key"
@@ -8053,6 +8074,12 @@ def load_artifact(artifact, work_dir):
manifest = parse_manifest(manifest_path)
entries = parse_files_list(files_path)
if is_device_plane_backhaul_vps_enrollment_slice(
manifest["component"],
entries,
):
die("vps_initiated_transport_frozen:ADR-0001")
for rel in entries:
allowed_payload_path(manifest["component"], rel)
if not (payload_dir / rel).exists():
@@ -8093,6 +8120,11 @@ def load_artifact(artifact, work_dir):
entries,
):
validate_device_plane_backhaul_target_payload(payload_dir)
if is_device_plane_backhaul_vps_enrollment_slice(
manifest["component"],
entries,
):
validate_device_plane_backhaul_vps_enrollment_payload(payload_dir)
if manifest["component"] == "n8n-private-extension":
validate_n8n_private_extension_release(payload_dir, entries)
if manifest["component"] == "engine":
@@ -8470,6 +8502,63 @@ def is_device_plane_backhaul_target_slice(component, entries):
)
def is_device_plane_backhaul_vps_enrollment_slice(component, entries):
return (
component == "device-plane"
and entries is not None
and tuple(entries)
== DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_ENTRIES
)
def expected_device_plane_backhaul_vps_enrollment_descriptor():
return {
"schemaVersion": (
"nodedc.device-plane.backhaul-vps-enrollment.v1"
),
"mode": "rotate-backhaul-client-mini-to-vps",
"predecessorPatchId": (
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_PATCH_ID
),
"predecessorArtifactSha256": (
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_ARTIFACT_SHA256
),
"sourceAction": "publish-vps-enrollment-marker-only",
"runtimeAction": (
"rotate-authorized-key-and-recreate-backhaul-target"
),
"selectedServices": [DEVICE_PLANE_BACKHAUL_TARGET_SERVICE],
"preservedServices": list(DEVICE_PLANE_RUNTIME_SERVICES),
"previousEnrollment": "device-edge-backhaul.pub",
"nextEnrollment": "device-edge-vps-backhaul.pub",
"nextKeyFingerprint": (
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_FINGERPRINT
),
"permittedTarget": DEVICE_PLANE_BACKHAUL_PERMITTED_TARGET,
"tailnetAddress": DEVICE_PLANE_BACKHAUL_TAILNET_ADDRESS,
"dockerPortPublication": "disabled",
"routerNatFirewall": "unchanged",
"edgePublicIngress": "disabled",
"funnel": "disabled",
"commandTransport": "disabled",
"gelios": "untouched",
"rollback": (
"restore-previous-authorized-key-and-recreate-target"
),
}
def validate_device_plane_backhaul_vps_enrollment_payload(payload_dir):
descriptor = read_strict_json(
payload_dir / DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_REL,
"Device Plane VPS backhaul enrollment descriptor",
max_bytes=16 * 1024,
)
if descriptor != expected_device_plane_backhaul_vps_enrollment_descriptor():
die("Device Plane VPS backhaul enrollment descriptor mismatch")
return descriptor
def expected_device_plane_backhaul_target_descriptor():
return {
"schemaVersion": (
@@ -8534,14 +8623,13 @@ def validate_device_plane_backhaul_target_payload(payload_dir):
return descriptor
def read_device_plane_backhaul_enrollment_public_key():
path = DEVICE_PLANE_BACKHAUL_ENROLLMENT_PUBLIC_KEY_FILE
def read_device_plane_ed25519_enrollment_public_key(path, comment, label):
try:
path_stat = path.lstat()
text = path.read_text(encoding="ascii")
except (FileNotFoundError, OSError, UnicodeDecodeError):
die(
"Device Plane Edge enrollment public key is missing or unreadable: "
f"{label} is missing or unreadable: "
f"{path}"
)
if (
@@ -8549,28 +8637,56 @@ def read_device_plane_backhaul_enrollment_public_key():
or not stat.S_ISREG(path_stat.st_mode)
or path_stat.st_size > 1024
):
die("Device Plane Edge enrollment public key is unsafe")
die(f"{label} is unsafe")
if text != text.strip() + "\n" or "\n" in text.strip():
die("Device Plane Edge enrollment public key must be one line")
die(f"{label} must be one line")
parts = text.strip().split()
if len(parts) not in (2, 3) or parts[0] != "ssh-ed25519":
die("Device Plane Edge enrollment public key type mismatch")
die(f"{label} type mismatch")
try:
blob = base64.b64decode(parts[1], validate=True)
except Exception:
die("Device Plane Edge enrollment public key encoding mismatch")
die(f"{label} encoding mismatch")
expected_prefix = b"\x00\x00\x00\x0bssh-ed25519\x00\x00\x00\x20"
if len(blob) != len(expected_prefix) + 32 or not blob.startswith(
expected_prefix
):
die("Device Plane Edge enrollment public key shape mismatch")
normalized = f"ssh-ed25519 {parts[1]} nodedc-device-edge-backhaul"
die(f"{label} shape mismatch")
normalized = f"ssh-ed25519 {parts[1]} {comment}"
return {
"line": normalized,
"sha256": hashlib.sha256((normalized + "\n").encode("ascii")).hexdigest(),
"fingerprint": (
"SHA256:"
+ base64.b64encode(hashlib.sha256(blob).digest())
.decode("ascii")
.rstrip("=")
),
}
def read_device_plane_backhaul_enrollment_public_key():
return read_device_plane_ed25519_enrollment_public_key(
DEVICE_PLANE_BACKHAUL_ENROLLMENT_PUBLIC_KEY_FILE,
"nodedc-device-edge-backhaul",
"Device Plane Edge enrollment public key",
)
def read_device_plane_backhaul_vps_enrollment_public_key():
enrollment = read_device_plane_ed25519_enrollment_public_key(
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PUBLIC_KEY_FILE,
"nodedc-device-edge-vps-backhaul",
"Device Plane VPS Edge enrollment public key",
)
if (
enrollment["fingerprint"]
!= DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_FINGERPRINT
):
die("Device Plane VPS Edge enrollment fingerprint mismatch")
return enrollment
def device_plane_tailscale_drop_privileges(uid, gid):
def demote():
os.setgroups([])
@@ -9136,6 +9252,37 @@ def validate_device_plane_backhaul_target_evidence(payload_dir):
}
def validate_device_plane_backhaul_vps_enrollment_evidence(payload_dir):
descriptor = validate_device_plane_backhaul_vps_enrollment_payload(
payload_dir
)
if not state_has_patch_id(
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_PATCH_ID
):
die("Device Plane VPS enrollment predecessor patch is not applied")
if not state_has_sha(
DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_ARTIFACT_SHA256
):
die("Device Plane VPS enrollment predecessor artifact is not applied")
root = component_root("device-plane")
marker = root / DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_REL
if marker.exists() or marker.is_symlink():
die("Device Plane VPS enrollment marker already exists")
runtime = device_plane_runtime_inventory(DEVICE_PLANE_RUNTIME_SERVICES)
validate_device_plane_backhaul_target_runtime(runtime)
previous = read_device_plane_backhaul_enrollment_public_key()
next_enrollment = read_device_plane_backhaul_vps_enrollment_public_key()
if previous["line"] == next_enrollment["line"]:
die("Device Plane VPS enrollment key is not a new identity")
return {
"mode": descriptor["mode"],
"runtime": runtime,
"previousEnrollmentPublicKeySha256": previous["sha256"],
"nextEnrollmentPublicKeySha256": next_enrollment["sha256"],
"nextKeyFingerprint": next_enrollment["fingerprint"],
}
def expected_device_plane_foundation_recovery_descriptor():
return {
"schemaVersion": "nodedc.device-plane.foundation-recovery.v1",
@@ -10713,7 +10860,10 @@ def validate_device_plane_preserved_runtime_unchanged(runtime_before, label):
return current
def validate_device_plane_backhaul_target_runtime(runtime_before):
def validate_device_plane_backhaul_target_runtime(
runtime_before,
expected_enrollment=None,
):
current = validate_device_plane_preserved_runtime_unchanged(
runtime_before,
"Device Plane backhaul",
@@ -10840,7 +10990,11 @@ def validate_device_plane_backhaul_target_runtime(runtime_before):
f"{required}"
)
enrollment = read_device_plane_backhaul_enrollment_public_key()
enrollment = (
expected_enrollment
if expected_enrollment is not None
else read_device_plane_backhaul_enrollment_public_key()
)
authorized = (
'restrict,port-forwarding,permitopen="127.0.0.1:9921" '
f"{enrollment['line']}\n"
@@ -12557,6 +12711,9 @@ def is_platform_provider_catalog_only(entries):
def component_services(component, entries=None):
if is_device_plane_backhaul_vps_enrollment_slice(component, entries):
return (DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,)
if is_device_plane_backhaul_target_slice(component, entries):
return (DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,)
@@ -12931,6 +13088,9 @@ def component_build_args(component, entries=None):
def component_builds(component, entries=None):
if is_device_plane_backhaul_vps_enrollment_slice(component, entries):
return ()
if is_device_plane_backhaul_target_slice(component, entries):
return ((
DEVICE_PLANE_ROOT,
@@ -14940,6 +15100,7 @@ def plan_artifact(artifact):
device_plane_b2_ingress_preflight = None
device_plane_b2_recovery_preflight = None
device_plane_backhaul_preflight = None
device_plane_backhaul_vps_enrollment_preflight = None
device_plane_runtime_before = None
composite_provider_v4_preflight = None
provider_rotating_slot_preflight = None
@@ -15149,6 +15310,15 @@ def plan_artifact(artifact):
device_plane_backhaul_preflight = (
validate_device_plane_backhaul_target_evidence(payload_dir)
)
if is_device_plane_backhaul_vps_enrollment_slice(
manifest["component"],
entries,
):
device_plane_backhaul_vps_enrollment_preflight = (
validate_device_plane_backhaul_vps_enrollment_evidence(
payload_dir
)
)
component = manifest["component"]
root = component_root(component)
@@ -16677,6 +16847,55 @@ def plan_artifact(artifact):
)
print("device_gateway_tcp_9921=disabled:unpublished")
print("device_plane_rollback=marker-only-runtime-unchanged")
if device_plane_backhaul_vps_enrollment_preflight is not None:
print(
"device_plane_transition="
f"{device_plane_backhaul_vps_enrollment_preflight['mode']}"
)
print(
"device_plane_predecessor_patch="
f"{DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_PATCH_ID}"
)
print(
"device_plane_predecessor_artifact_sha256="
f"{DEVICE_PLANE_BACKHAUL_VPS_ENROLLMENT_PREDECESSOR_ARTIFACT_SHA256}"
)
print("device_plane_build=none")
print(
"device_plane_runtime_mutation="
"rotate-authorized-key+recreate:device-backhaul-target"
)
print(
"device_plane_runtime_services="
"preserved:device-control-core,device-gateway,device-postgres"
)
print(
"device_backhaul_previous_enrollment_public_key_sha256="
f"{device_plane_backhaul_vps_enrollment_preflight['previousEnrollmentPublicKeySha256']}"
)
print(
"device_backhaul_next_enrollment_public_key_sha256="
f"{device_plane_backhaul_vps_enrollment_preflight['nextEnrollmentPublicKeySha256']}"
)
print(
"device_backhaul_next_key_fingerprint="
f"{device_plane_backhaul_vps_enrollment_preflight['nextKeyFingerprint']}"
)
print(
"device_backhaul_permitopen="
f"{DEVICE_PLANE_BACKHAUL_PERMITTED_TARGET}"
)
print("device_backhaul_docker_port_publication=disabled")
print("device_backhaul_tailscale_serve=unchanged")
print("device_backhaul_tailscale_funnel=disabled")
print("device_backhaul_router_nat_firewall=unchanged")
print("device_edge_public_ingress=disabled")
print("device_command_transport=disabled")
print("gelios=untouched")
print(
"device_plane_rollback="
"restore-previous-authorized-key+recreate-target"
)
if device_plane_backhaul_preflight is not None:
print(
"device_plane_transition="
@@ -16849,6 +17068,25 @@ def create_backup(root, backup_dir, entries, include_nginx_html):
(backup_dir / "missing-files.txt").write_text("\n".join(missing) + ("\n" if missing else ""), encoding="utf-8")
def backup_device_plane_backhaul_authorized_keys(backup_dir):
source_stat = DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE.lstat()
if (
stat.S_ISLNK(source_stat.st_mode)
or not stat.S_ISREG(source_stat.st_mode)
or source_stat.st_uid != 0
or stat.S_IMODE(source_stat.st_mode) != 0o444
or source_stat.st_size > 2048
):
die("Device Plane backhaul authorized_keys backup source is unsafe")
destination = backup_dir / DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_BACKUP
if destination.exists() or destination.is_symlink():
die("Device Plane backhaul authorized_keys backup collision")
shutil.copy2(DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE, destination)
os.chown(destination, 0, 0)
destination.chmod(0o600)
return sha256_file(destination)
def read_backup_path_list(path):
if not path.is_file():
die(f"deploy backup path list missing: {path}")
@@ -17216,6 +17454,53 @@ def rollback_device_plane_apply(
return f"source+runtime-restored:{restored_count}"
def rollback_device_plane_backhaul_vps_enrollment(
root,
backup_dir,
entries,
current_stamp,
runtime_before,
):
restored_count = restore_platform_overlay(
root,
backup_dir,
entries,
current_stamp,
)
backup = backup_dir / DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_BACKUP
backup_stat = backup.lstat()
previous = read_device_plane_backhaul_enrollment_public_key()
expected = (
'restrict,port-forwarding,permitopen="127.0.0.1:9921" '
f"{previous['line']}\n"
)
if (
stat.S_ISLNK(backup_stat.st_mode)
or not stat.S_ISREG(backup_stat.st_mode)
or backup_stat.st_uid != 0
or stat.S_IMODE(backup_stat.st_mode) != 0o600
or backup_stat.st_size > 2048
or backup.read_text(encoding="ascii") != expected
):
die("Device Plane VPS enrollment rollback backup mismatch")
install_device_plane_backhaul_authorized_key(previous)
run_compose(
"device-plane",
(DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,),
entries,
)
run_healthchecks(
"device-plane",
entries,
(DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,),
)
validate_device_plane_backhaul_target_runtime(
runtime_before,
expected_enrollment=previous,
)
return f"previous-key+target+source-restored:{restored_count}"
def rollback_engine_apply(root, backup_dir, entries, current_stamp, runtime_started, applied_services):
existing = read_backup_path_list(backup_dir / "existing-files.txt")
missing = read_backup_path_list(backup_dir / "missing-files.txt")
@@ -17764,6 +18049,32 @@ def run_engine_node_intelligence_compose(services, entries):
)
def install_device_plane_backhaul_authorized_key(enrollment):
DEVICE_PLANE_BACKHAUL_SECRET_DIR.mkdir(
parents=True,
exist_ok=True,
)
os.chown(DEVICE_PLANE_BACKHAUL_SECRET_DIR, 0, 0)
DEVICE_PLANE_BACKHAUL_SECRET_DIR.chmod(0o700)
authorized = (
'restrict,port-forwarding,permitopen="127.0.0.1:9921" '
f"{enrollment['line']}\n"
)
temporary = DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE.with_suffix(
".installing"
)
if temporary.exists() or temporary.is_symlink():
die("Device Plane backhaul authorized_keys staging path exists")
temporary.write_text(authorized, encoding="ascii")
os.chown(temporary, 0, 0)
temporary.chmod(0o444)
os.replace(temporary, DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE)
expected_sha256 = hashlib.sha256(authorized.encode("ascii")).hexdigest()
if sha256_file(DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE) != expected_sha256:
die("Device Plane backhaul authorized key verification failed")
return expected_sha256
def ensure_device_plane_backhaul_target_state():
enrollment = read_device_plane_backhaul_enrollment_public_key()
DEVICE_PLANE_BACKHAUL_SECRET_DIR.mkdir(
@@ -17818,19 +18129,9 @@ def ensure_device_plane_backhaul_target_state():
os.chown(path, 0, 0)
path.chmod(expected_mode)
authorized = (
'restrict,port-forwarding,permitopen="127.0.0.1:9921" '
f"{enrollment['line']}\n"
expected_authorized_sha256 = install_device_plane_backhaul_authorized_key(
enrollment
)
temporary = DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE.with_suffix(
".installing"
)
if temporary.exists() or temporary.is_symlink():
die("Device Plane backhaul authorized_keys staging path exists")
temporary.write_text(authorized, encoding="ascii")
os.chown(temporary, 0, 0)
temporary.chmod(0o444)
os.replace(temporary, DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE)
DEVICE_PLANE_BACKHAUL_TRUST_DIR.mkdir(parents=True, exist_ok=True)
os.chown(DEVICE_PLANE_BACKHAUL_TRUST_DIR, 0, 0)
@@ -17846,9 +18147,6 @@ def ensure_device_plane_backhaul_target_state():
public_temporary.chmod(0o444)
os.replace(public_temporary, DEVICE_PLANE_BACKHAUL_HOST_PUBLIC_KEY_FILE)
expected_authorized_sha256 = hashlib.sha256(
authorized.encode("ascii")
).hexdigest()
if (
sha256_file(DEVICE_PLANE_BACKHAUL_AUTHORIZED_KEYS_FILE)
!= expected_authorized_sha256
@@ -17929,6 +18227,14 @@ def prepare_component_runtime(component, entries=None):
MAP_GATEWAY_SECRET_RE,
"device plane identifier pepper",
)
if is_device_plane_backhaul_vps_enrollment_slice(
component,
entries,
):
install_device_plane_backhaul_authorized_key(
read_device_plane_backhaul_vps_enrollment_public_key()
)
return
if is_device_plane_backhaul_target_slice(component, entries):
ensure_device_plane_backhaul_target_state()
return
@@ -18685,6 +18991,17 @@ process.stdout.write('engine-l2-closed-loop:0.7.0:cas+safe-profile+external-plan
def run_healthchecks(component, entries=None, services=None):
if is_device_plane_backhaul_vps_enrollment_slice(component, entries):
if tuple(services or ()) != (DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,):
die("Device Plane VPS enrollment service set mismatch")
for service in DEVICE_PLANE_RUNTIME_SERVICES:
healthcheck_compose_service("device-plane", service)
healthcheck_compose_service(
"device-plane",
DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,
)
return
if is_device_plane_backhaul_target_slice(component, entries):
if tuple(services or ()) != (DEVICE_PLANE_BACKHAUL_TARGET_SERVICE,):
die("Device Plane backhaul target service set mismatch")
@@ -19551,6 +19868,7 @@ def apply_artifact(artifact):
node_intelligence_descriptor = None
l2_closed_loop_preflight = None
device_plane_backhaul_preflight = None
device_plane_backhaul_vps_enrollment_preflight = None
node_intelligence_service_stopped = False
apply_started = False
engine_backend_recreated = False
@@ -19628,6 +19946,15 @@ def apply_artifact(artifact):
payload_dir
)
)
if is_device_plane_backhaul_vps_enrollment_slice(
component,
entries,
):
device_plane_backhaul_vps_enrollment_preflight = (
validate_device_plane_backhaul_vps_enrollment_evidence(
payload_dir
)
)
if not root.is_dir():
if bootstrap_root:
root.mkdir(parents=True, exist_ok=True)
@@ -19955,6 +20282,13 @@ def apply_artifact(artifact):
encoding="utf-8",
)
runtime_inventory_path.chmod(0o600)
if (
device_plane_backhaul_vps_enrollment_preflight
is not None
):
backup_device_plane_backhaul_authorized_keys(
backup_dir
)
if device_plane_backhaul_preflight is not None:
tailscale_before_path = (
backup_dir / "tailscale-serve-before.json"
@@ -20067,6 +20401,21 @@ def apply_artifact(artifact):
validate_device_plane_backhaul_target_runtime(
device_plane_runtime_before
)
if is_device_plane_backhaul_vps_enrollment_slice(
component,
entries,
):
if device_plane_runtime_before is None:
die(
"Device Plane VPS enrollment predecessor runtime "
"inventory is missing"
)
validate_device_plane_backhaul_target_runtime(
device_plane_runtime_before,
expected_enrollment=(
read_device_plane_backhaul_vps_enrollment_public_key()
),
)
applied_path = move_artifact(artifact, APPLIED_DIR)
append_jsonl(STATE_FILE, {
@@ -20305,6 +20654,41 @@ def apply_artifact(artifact):
"automatic-rollback=failed",
file=sys.stderr,
)
elif (
is_device_plane_backhaul_vps_enrollment_slice(
component,
entries,
)
and device_plane_runtime_before is not None
):
try:
restored_state = (
rollback_device_plane_backhaul_vps_enrollment(
root,
backup_dir,
entries,
current_stamp,
device_plane_runtime_before,
)
)
rollback_status = (
"ok:device-plane-vps-enrollment:"
f"{restored_state}"
)
print(
"device-plane-vps-enrollment-"
f"automatic-rollback={rollback_status}",
file=sys.stderr,
)
except Exception as rollback_exc:
rollback_status = (
f"failed:{type(rollback_exc).__name__}"
)
print(
"device-plane-vps-enrollment-"
"automatic-rollback=failed",
file=sys.stderr,
)
elif (
component == "device-plane"
and entries is not None