#!/usr/bin/env python3
# tests/phase-02/make_fixtures.py [OUTDIR]
# Generate juju-status fixtures for the phase-02-vault-preflight regression.
# One healthy fixture + four single-fault fixtures (one broken gate each).
# ASCII + LF.
import json, copy, os, sys
OUTDIR = sys.argv[1] if len(sys.argv) > 1 else "."
def unit(cur, msg, agent="idle", subs=None):
u = {"workload-status": {"current": cur, "message": msg},
"agent-status": {"current": agent}}
if subs:
u["subordinates"] = subs
return u
base = {
"model": {"name": "openstack"},
"machines": {str(i): {"juju-status": {"current": "started"}, "hostname": f"openstack{i}"} for i in range(4)},
"applications": {
"mysql-innodb-cluster": {"units": {
"mysql-innodb-cluster/0": unit("active", "Unit is ready: Mode: R/W, Cluster is ONLINE and can tolerate up to ONE failure.",
subs={"mysql-router/0": unit("active", "Unit is ready")}),
"mysql-innodb-cluster/1": unit("active", "Unit is ready: Mode: R/O, Cluster is ONLINE and can tolerate up to ONE failure.",
subs={"mysql-router/1": unit("active", "Unit is ready")}),
"mysql-innodb-cluster/2": unit("active", "Unit is ready: Mode: R/O, Cluster is ONLINE and can tolerate up to ONE failure.",
subs={"mysql-router/2": unit("active", "Unit is ready")}),
}},
"vault": {"units": {
"vault/0": unit("blocked", "Vault needs to be initialized",
subs={"vault-mysql-router/0": unit("active", "Unit is ready")}),
}},
"octavia": {"units": {"octavia/0": unit("blocked", "Awaiting configure-resources action.")}},
"ovn-central": {"units": {f"ovn-central/{i}": unit("waiting", "ovsdb-peer/certificates") for i in range(3)}},
"nova-compute": {"units": {
f"nova-compute/{i}": unit("active", "Unit is ready", subs={f"ovn-chassis/{i}": unit("waiting", "certificates")})
for i in range(3)
}},
"neutron-api": {"units": {
"neutron-api/0": unit("active", "Unit is ready", subs={"neutron-api-plugin-ovn/0": unit("waiting", "ovsdb-cms")}),
}},
"ceph-osd": {"units": {f"ceph-osd/{i}": unit("active", "Unit is ready (1 OSD)") for i in range(4)}},
"ceph-mon": {"units": {f"ceph-mon/{i}": unit("active", "Unit is ready and clustered") for i in range(3)}},
"glance-simplestreams-sync": {"units": {"glance-simplestreams-sync/0": unit("unknown", "")}},
}
}
def dump(name, obj):
path = os.path.join(OUTDIR, name)
with open(path, "w") as f:
json.dump(obj, f, indent=2)
print(f" wrote {path}")
# healthy
dump("pass.json", base)
# FAIL D: vault already initialized (not the fresh blocked-needs-init state)
f = copy.deepcopy(base)
f["applications"]["vault"]["units"]["vault/0"]["workload-status"] = {"current": "active", "message": "Unit is ready"}
dump("fail-vault-initialized.json", f)
# FAIL C: one mysql unit OFFLINE (no ONLINE, not active)
f = copy.deepcopy(base)
f["applications"]["mysql-innodb-cluster"]["units"]["mysql-innodb-cluster/2"]["workload-status"] = \
{"current": "blocked", "message": "Cluster is inaccessible from this instance (OFFLINE)."}
dump("fail-mysql-degraded.json", f)
# FAIL E: a hook failure (agent-status error) on an otherwise-maintenance unit
f = copy.deepcopy(base)
f["applications"]["nova-compute"]["units"]["nova-compute/0"]["workload-status"] = {"current": "maintenance", "message": "installing"}
f["applications"]["nova-compute"]["units"]["nova-compute/0"]["agent-status"] = {"current": "error", "message": 'hook failed: "install"'}
dump("fail-hook-error.json", f)
# FAIL B: a machine not started
f = copy.deepcopy(base)
f["machines"]["2"]["juju-status"] = {"current": "down"}
dump("fail-machine-down.json", f)