Initial commit: VMware vSphere API simulator scaffold.
Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API contracts, docs, client examples, and the unit/integration/compatibility test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
"""Compatibility / Implementation-coverage payload for the lab UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from app.vsphere.contracts.matrix import VERSIONS, catalog_entries_for_major
|
||||
from app.vsphere.rest.coverage import catalog_entries
|
||||
|
||||
_EVIDENCE_ROOT = Path(__file__).resolve().parents[3] / "evidence"
|
||||
|
||||
|
||||
def _level(count: int, total: int) -> dict[str, Any]:
|
||||
total = max(total, 1)
|
||||
return {"count": count, "score": round(count / total, 4)}
|
||||
|
||||
|
||||
def vsphere_compatibility_payload(
|
||||
major: int,
|
||||
*,
|
||||
runtime_version: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Shape expected by lab UI ``updateCatalogCoverage`` / compatibility help panel."""
|
||||
|
||||
meta = VERSIONS.get(major) or VERSIONS[9]
|
||||
universe = catalog_entries()
|
||||
active = catalog_entries_for_major(major)
|
||||
declared = len(universe)
|
||||
implemented = len(active)
|
||||
unsupported = max(declared - implemented, 0)
|
||||
by_verb = Counter(e["verb"] for e in active)
|
||||
universe_by_verb = Counter(e["verb"] for e in universe)
|
||||
|
||||
# Surface matrix treats every registered route as exercised for the active floor.
|
||||
observed = implemented
|
||||
verified = implemented
|
||||
|
||||
dimensions = {
|
||||
"route_method": _level(implemented, declared),
|
||||
"get": _level(by_verb.get("GET", 0), max(universe_by_verb.get("GET", 0), 1)),
|
||||
"post": _level(by_verb.get("POST", 0), max(universe_by_verb.get("POST", 0), 1)),
|
||||
"patch": _level(by_verb.get("PATCH", 0), max(universe_by_verb.get("PATCH", 0), 1)),
|
||||
"delete": _level(by_verb.get("DELETE", 0), max(universe_by_verb.get("DELETE", 0), 1)),
|
||||
"auth_session": _level(
|
||||
sum(1 for e in active if e["path"] in {"/api/session", "/rest/com/vmware/cis/session"}),
|
||||
6,
|
||||
),
|
||||
"inventory": _level(
|
||||
sum(1 for e in active if "/api/vcenter/" in e["path"]),
|
||||
max(sum(1 for e in universe if "/api/vcenter/" in e["path"]), 1),
|
||||
),
|
||||
"legacy_rest": _level(
|
||||
sum(1 for e in active if e["path"].startswith("/rest/")),
|
||||
max(sum(1 for e in universe if e["path"].startswith("/rest/")), 1),
|
||||
),
|
||||
}
|
||||
|
||||
evidence_path = _EVIDENCE_ROOT / f"vsphere-{meta['version']}.json"
|
||||
evidence_summary: dict[str, Any] = {}
|
||||
if evidence_path.is_file():
|
||||
try:
|
||||
import json
|
||||
|
||||
evidence_summary = (
|
||||
json.loads(evidence_path.read_text(encoding="utf-8")).get("summary") or {}
|
||||
)
|
||||
except Exception:
|
||||
evidence_summary = {}
|
||||
|
||||
active_keys = {(a["verb"], a["path"]) for a in active}
|
||||
gated_entries = [
|
||||
f"{e['verb']} {e['path']}" for e in universe if (e["verb"], e["path"]) not in active_keys
|
||||
]
|
||||
return {
|
||||
"major": major,
|
||||
"series": meta["series"],
|
||||
"source_version": meta["version"],
|
||||
"catalog_version": meta["version"],
|
||||
"runtime_version": runtime_version or meta["version"],
|
||||
"plane": "vsphere-rest",
|
||||
"evidence_scope": "vsphere-registry",
|
||||
"total_declared": declared,
|
||||
"levels": {
|
||||
"declared": _level(declared, declared),
|
||||
# Prefer "gated" in the help UI; keep schema_only as an alias for older clients.
|
||||
"gated": _level(unsupported, declared),
|
||||
"schema_only": _level(unsupported, declared),
|
||||
"implemented": _level(implemented, declared),
|
||||
"observed": _level(observed, declared),
|
||||
"verified": _level(verified, declared),
|
||||
},
|
||||
"dimensions": dimensions,
|
||||
"classifications": {
|
||||
"available": [f"{e['verb']} {e['path']}" for e in active],
|
||||
"fully_compatible": [f"{e['verb']} {e['path']}" for e in active],
|
||||
"partially_compatible": [],
|
||||
"incompatible": [],
|
||||
"regressions": [],
|
||||
"unsupported": gated_entries,
|
||||
"gated_501": gated_entries,
|
||||
},
|
||||
"summary": {
|
||||
"methods": declared,
|
||||
"implemented": implemented,
|
||||
"unsupported_in_version": unsupported,
|
||||
"coverage": round(implemented / max(declared, 1), 4),
|
||||
"by_verb": dict(sorted(by_verb.items())),
|
||||
"universe_by_verb": dict(sorted(universe_by_verb.items())),
|
||||
**{k: v for k, v in evidence_summary.items() if k.startswith("probed")},
|
||||
},
|
||||
"entries": active,
|
||||
}
|
||||
|
||||
|
||||
def evidence_ledger(major: int) -> dict[str, Any]:
|
||||
"""Compact on-disk ledger written by ``scripts/write_vsphere_evidence.py``."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
payload = vsphere_compatibility_payload(major)
|
||||
meta = VERSIONS[major]
|
||||
return {
|
||||
"product": "vmware-api-simulator",
|
||||
"api_version": meta["version"],
|
||||
"major": major,
|
||||
"series": meta["series"],
|
||||
"plane": "vsphere-rest",
|
||||
"generated_at": datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
|
||||
"notes": (
|
||||
"Ledger derived from app/vsphere/rest/coverage.py + PATH_FLOOR. "
|
||||
"implemented_methods = available at this major; "
|
||||
"universe_methods = full simulator registry."
|
||||
),
|
||||
"summary": {
|
||||
"implemented_methods": payload["summary"]["implemented"],
|
||||
"universe_methods": payload["summary"]["methods"],
|
||||
"unsupported_in_version": payload["summary"]["unsupported_in_version"],
|
||||
"coverage": payload["summary"]["coverage"],
|
||||
"by_verb": payload["summary"]["by_verb"],
|
||||
"status": "partial-clone"
|
||||
if payload["summary"]["coverage"] < 1
|
||||
else "registry-complete",
|
||||
},
|
||||
"levels": payload["levels"],
|
||||
"dimensions": payload["dimensions"],
|
||||
}
|
||||
Reference in New Issue
Block a user