Fix CI/pulumi blockers and document test suites with latest PASS results.
Stabilize node status, cluster-wide Ceph OSD ids, and QEMU cpu utilization so status/list no longer 500 on model strings; align pulumi defaults to pve1; add bilingual testing docs with the 2026-07-18 ci-all + pulumi-tests pass.
This commit is contained in:
@@ -131,7 +131,7 @@ def register_acme_handlers(registry: HandlerRegistry) -> None:
|
||||
entry = {"plugin": plugin_id, **{k: v for k, v in item.items() if k != "data"}}
|
||||
if "digest" not in entry:
|
||||
material = {k: v for k, v in entry.items() if k != "digest"}
|
||||
entry["digest"] = hashlib.sha1(
|
||||
entry["digest"] = hashlib.sha1( # noqa: S324 - Proxmox config digests use SHA-1
|
||||
json.dumps(material, sort_keys=True, separators=(",", ":")).encode()
|
||||
).hexdigest()
|
||||
result.append(entry)
|
||||
|
||||
+12
-6
@@ -245,7 +245,7 @@ def register_ceph_handlers(registry: HandlerRegistry) -> None:
|
||||
def _wire_ceph_pool(name: str, item: dict[str, Any], *, index: int) -> dict[str, Any]:
|
||||
pool_id = item.get("pool_id", item.get("pool"))
|
||||
try:
|
||||
pool_num = int(pool_id)
|
||||
pool_num = int(pool_id) if pool_id is not None else index
|
||||
except (TypeError, ValueError):
|
||||
pool_num = index
|
||||
crush_raw = item.get("crush_rule", 0)
|
||||
@@ -586,9 +586,8 @@ def register_ceph_handlers(registry: HandlerRegistry) -> None:
|
||||
MAX(NULLIF(regexp_replace(external_id, '\\D', '', 'g'), '')::int),
|
||||
-1
|
||||
) + 1
|
||||
FROM resources r JOIN nodes n ON n.id=r.node_id
|
||||
WHERE n.name=$1 AND r.kind='ceph-osd'""",
|
||||
node,
|
||||
FROM resources
|
||||
WHERE kind='ceph-osd'""",
|
||||
)
|
||||
osd_id = int(next_id or 0)
|
||||
external_id = f"osd.{osd_id}"
|
||||
@@ -607,8 +606,15 @@ def register_ceph_handlers(registry: HandlerRegistry) -> None:
|
||||
"used_bytes": 0,
|
||||
}
|
||||
await database(request).pool.execute(
|
||||
"""INSERT INTO resources(id, node_id, kind, external_id, state)
|
||||
VALUES(gen_random_uuid(), $1, 'ceph-osd', $2, $3::jsonb)""",
|
||||
"""INSERT INTO resources(id, node_id, cluster_id, kind, external_id, state)
|
||||
VALUES(
|
||||
gen_random_uuid(),
|
||||
$1,
|
||||
(SELECT cluster_id FROM nodes WHERE id=$1),
|
||||
'ceph-osd',
|
||||
$2,
|
||||
$3::jsonb
|
||||
)""",
|
||||
node_id,
|
||||
external_id,
|
||||
json.dumps(osd_state, sort_keys=True),
|
||||
|
||||
@@ -193,8 +193,8 @@ def register_cluster_extra_handlers(registry: HandlerRegistry) -> None:
|
||||
now = int(time.time())
|
||||
seeded = metrics.get("export_series")
|
||||
if isinstance(seeded, list) and seeded:
|
||||
series = [dict(item) for item in seeded if isinstance(item, dict)]
|
||||
return {"data": series, "timestamp": now}
|
||||
seeded_series = [dict(item) for item in seeded if isinstance(item, dict)]
|
||||
return {"data": seeded_series, "timestamp": now}
|
||||
# Build a PVE-shaped metric array from live inventory (scales with seed).
|
||||
rows = await database(request).pool.fetch("SELECT name FROM nodes ORDER BY name")
|
||||
series: list[dict[str, Any]] = []
|
||||
|
||||
@@ -168,6 +168,7 @@ def build_core_handlers(settings: Settings) -> HandlerRegistry:
|
||||
"machine": "x86_64",
|
||||
}
|
||||
return {
|
||||
"status": str(row["status"]),
|
||||
"uptime": int(payload.get("uptime") or 0),
|
||||
"wait": float(payload.get("wait") or 0.0),
|
||||
"idle": float(payload.get("idle") or 0.95),
|
||||
@@ -344,7 +345,7 @@ def build_core_handlers(settings: Settings) -> HandlerRegistry:
|
||||
"vmid": vmid,
|
||||
"name": str(state.get("name") or f"{kind}-{external_id}"),
|
||||
"status": status,
|
||||
"template": bool(state.get("template") in {True, "1", 1}),
|
||||
"template": bool(state.get("template") in (True, "1", 1)),
|
||||
"cpu": _cpu_util(state, running=running),
|
||||
"maxcpu": _maxcpu(state),
|
||||
"mem": int(_num(state.get("mem"), 0)) if running else 0,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ def _ha_groups(metadata: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
|
||||
def _ha_digest(payload: dict[str, Any]) -> str:
|
||||
material = {key: value for key, value in payload.items() if key != "digest"}
|
||||
return hashlib.sha1(
|
||||
return hashlib.sha1( # noqa: S324 - Proxmox HA digests use SHA-1
|
||||
json.dumps(material, sort_keys=True, separators=(",", ":")).encode()
|
||||
).hexdigest()
|
||||
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ def _public_lxc_config(
|
||||
continue
|
||||
payload[key] = value
|
||||
digest_src = {key: value for key, value in payload.items() if key != "digest"}
|
||||
payload["digest"] = hashlib.sha1(
|
||||
payload["digest"] = hashlib.sha1( # noqa: S324 - Proxmox config digests use SHA-1
|
||||
json.dumps(digest_src, sort_keys=True, separators=(",", ":")).encode()
|
||||
).hexdigest()
|
||||
return payload
|
||||
|
||||
@@ -554,10 +554,10 @@ def register_nodes_extra_handlers(registry: HandlerRegistry) -> None:
|
||||
}
|
||||
)
|
||||
else:
|
||||
path = payload.get("path")
|
||||
target_path = payload.get("path")
|
||||
index = payload.get("index")
|
||||
for file_idx, file_entry in enumerate(files):
|
||||
if path is not None and file_entry.get("path") != path:
|
||||
if target_path is not None and file_entry.get("path") != target_path:
|
||||
continue
|
||||
repos = [
|
||||
dict(item)
|
||||
@@ -584,7 +584,7 @@ def register_nodes_extra_handlers(registry: HandlerRegistry) -> None:
|
||||
}
|
||||
},
|
||||
}
|
||||
elif path is not None or file_idx == 0:
|
||||
elif target_path is not None or file_idx == 0:
|
||||
if repos:
|
||||
repos[0] = {
|
||||
**repos[0],
|
||||
@@ -604,7 +604,7 @@ def register_nodes_extra_handlers(registry: HandlerRegistry) -> None:
|
||||
},
|
||||
}
|
||||
file_entry["repositories"] = repos
|
||||
if path is not None:
|
||||
if target_path is not None:
|
||||
break
|
||||
apt["repositories"] = {
|
||||
"digest": secrets.token_hex(4),
|
||||
|
||||
+16
-2
@@ -78,12 +78,26 @@ def _public_qemu_config(config: dict[str, Any], state: dict[str, Any]) -> dict[s
|
||||
agent = config.get("agent", state.get("agent", "0"))
|
||||
payload["agent"] = _agent_config_string(agent)
|
||||
digest_src = {key: value for key, value in payload.items() if key != "digest"}
|
||||
payload["digest"] = hashlib.sha1(
|
||||
payload["digest"] = hashlib.sha1( # noqa: S324 - Proxmox config digests use SHA-1
|
||||
json.dumps(digest_src, sort_keys=True, separators=(",", ":")).encode()
|
||||
).hexdigest()
|
||||
return payload
|
||||
|
||||
|
||||
def _cpu_utilization(vm_state: Mapping[str, Any], *, running: bool, idle: float = 0.1) -> float:
|
||||
"""Status ``cpu`` is utilization; config may store model strings like ``qemu64``."""
|
||||
|
||||
raw = vm_state.get("cpu")
|
||||
if isinstance(raw, (int, float)):
|
||||
return float(raw)
|
||||
if isinstance(raw, str):
|
||||
try:
|
||||
return float(raw)
|
||||
except ValueError:
|
||||
pass
|
||||
return idle if running else 0.0
|
||||
|
||||
|
||||
def _qemu_index_item(vmid: int, vm_state: dict[str, Any], config: dict[str, Any]) -> dict[str, Any]:
|
||||
status = str(vm_state.get("status", "stopped"))
|
||||
running = status in {"running", "paused"}
|
||||
@@ -96,7 +110,7 @@ def _qemu_index_item(vmid: int, vm_state: dict[str, Any], config: dict[str, Any]
|
||||
"status": status,
|
||||
"qmpstatus": status if running else "stopped",
|
||||
"cpus": int(config.get("cores", config.get("cpus", 1))),
|
||||
"cpu": float(vm_state.get("cpu", 0.1 if running else 0.0)),
|
||||
"cpu": _cpu_utilization(vm_state, running=running),
|
||||
"maxmem": maxmem,
|
||||
"mem": mem,
|
||||
"memhost": int(vm_state.get("memhost", mem)),
|
||||
|
||||
Reference in New Issue
Block a user