Files
openstack-api-simulator/app/openstack/demo_cloud.py
T
inecs 147f04c9a2 Fix Nova/Cinder stateful CRUD so the full surface and Pulumi matrix pass clean.
Persist aggregates and server security groups in domain tables, add Cinder quota
show from DB-backed defaults, align unit tests with OpenStack series, auto-pick
Keystone :5000/:15000 for smoke, and document test locations plus 6514/6514 results.
2026-07-18 09:38:45 +03:00

1936 lines
74 KiB
Python

"""Sized OpenStack demo cloud seed (small / large / big synthetic clusters)."""
from __future__ import annotations
import json
from dataclasses import asdict, dataclass
from typing import Any
from uuid import UUID
from asyncpg import Connection
from app.openstack.ids import oid
from app.security.auth import hash_secret
DEMO_PROFILE = "openstack-demo-cloud"
DEMO_SIZE_DEFAULT = "large"
@dataclass(frozen=True, slots=True)
class DemoClusterSize:
"""Proportional inventory for a demo cluster size button."""
name: str
hypervisors: int
servers: int
volumes: int
ironic_nodes: int
loadbalancers: int
stacks: int
floating_ips: int
surface_samples: int
server_groups: int
nested_samples: int
pack_per_type: int
extra_networks: int
extra_security_groups: int
keypairs_per_user: int
edge_routers: int
def _scale(base: int, factor: float, minimum: int) -> int:
return max(minimum, int(round(base * factor)))
def _cluster(name: str, *, hypervisors: int, servers: int) -> DemoClusterSize:
"""Derive secondary counts from the large (1000 VM) reference ratios."""
factor = servers / 1000
return DemoClusterSize(
name=name,
hypervisors=hypervisors,
servers=servers,
volumes=_scale(600, factor, 20),
ironic_nodes=_scale(24, factor, 2),
loadbalancers=_scale(12, factor, 2),
stacks=_scale(30, factor, 3),
floating_ips=_scale(120, factor, 6),
surface_samples=_scale(8, factor, 3),
server_groups=_scale(16, factor, 3),
nested_samples=_scale(24, factor, 8),
pack_per_type=_scale(3, factor, 2),
# Topology density (large reference: 3 extra nets, 3 SG tiers, 4 keys, 1 edge router).
extra_networks=_scale(3, factor, 1),
extra_security_groups=_scale(3, factor, 1),
keypairs_per_user=_scale(4, factor, 2),
edge_routers=_scale(1, factor, 0),
)
DEMO_CLUSTER_SIZES: dict[str, DemoClusterSize] = {
"small": _cluster("small", hypervisors=3, servers=50),
"large": _cluster("large", hypervisors=10, servers=1000),
"big": _cluster("big", hypervisors=20, servers=2000),
}
def resolve_demo_size(size: str | None) -> DemoClusterSize:
key = (size or DEMO_SIZE_DEFAULT).strip().lower()
aliases = {
"demo": "large",
"demo-cloud": "large",
"openstack-demo-cloud": "large",
"demo-small": "small",
"demo-large": "large",
"demo-big": "big",
"medium": "large",
}
key = aliases.get(key, key)
if key not in DEMO_CLUSTER_SIZES:
known = ", ".join(sorted(DEMO_CLUSTER_SIZES))
raise ValueError(f"unknown demo size {size!r} (use {known})")
return DEMO_CLUSTER_SIZES[key]
def demo_profile_name(size: str | DemoClusterSize) -> str:
name = size.name if isinstance(size, DemoClusterSize) else resolve_demo_size(size).name
return f"{DEMO_PROFILE}:{name}"
def is_demo_profile(profile: str | None) -> bool:
if not profile:
return False
return profile == DEMO_PROFILE or profile.startswith(f"{DEMO_PROFILE}:")
def list_demo_sizes() -> list[dict[str, Any]]:
return [asdict(cfg) for cfg in DEMO_CLUSTER_SIZES.values()]
# Backward-compatible aliases → large cluster (default demo).
DEMO_SERVER_COUNT = DEMO_CLUSTER_SIZES["large"].servers
DEMO_VOLUME_COUNT = DEMO_CLUSTER_SIZES["large"].volumes
DEMO_HYPERVISOR_COUNT = DEMO_CLUSTER_SIZES["large"].hypervisors
DEMO_IRONIC_COUNT = DEMO_CLUSTER_SIZES["large"].ironic_nodes
DEMO_LB_COUNT = DEMO_CLUSTER_SIZES["large"].loadbalancers
DEMO_STACK_COUNT = DEMO_CLUSTER_SIZES["large"].stacks
DEMO_FIP_COUNT = DEMO_CLUSTER_SIZES["large"].floating_ips
AZS = ("az-1", "az-2", "az-3")
PROJECTS = (
("admin", "Admin project"),
("demo", "Demo project"),
("production", "Production workloads"),
("staging", "Staging workloads"),
("development", "Development workloads"),
)
USERS = (
("admin", "admin"),
("demo", "member"),
("ops", "admin"),
("developer", "member"),
("auditor", "member"),
)
PREFIXES = (
"web",
"api",
"db",
"cache",
"worker",
"batch",
"gpu",
"ml",
"ci",
"jump",
)
def _server_name(index: int) -> str:
return f"{PREFIXES[index % len(PREFIXES)]}-{index:04d}"
def _status(index: int) -> str:
# Mostly ACTIVE for realistic inventory.
cycle = ("ACTIVE", "ACTIVE", "ACTIVE", "ACTIVE", "ACTIVE", "SHUTOFF", "ACTIVE", "ERROR")
return cycle[index % len(cycle)]
async def clear_openstack_state(conn: Connection) -> None:
"""Wipe all OpenStack lab tables (FK-safe truncate)."""
await conn.execute(
"""
TRUNCATE TABLE
os_tokens,
os_role_assignments,
os_security_group_rules,
os_security_groups,
os_floating_ips,
os_ports,
os_subnets,
os_networks,
os_routers,
os_server_groups,
os_servers,
os_volumes,
os_images,
os_flavors,
os_keypairs,
os_stacks,
os_swift_objects,
os_swift_containers,
os_nodes,
os_loadbalancers,
os_api_objects,
os_compute_services,
os_aggregates,
os_hypervisors,
os_availability_zones,
os_demo_meta,
os_users,
os_roles,
os_projects,
os_domains
RESTART IDENTITY CASCADE
"""
)
async def seed_openstack_demo(
conn: Connection,
*,
size: str = DEMO_SIZE_DEFAULT,
password: str = "secret",
) -> dict[str, Any]:
"""Load a full synthetic OpenStack cloud. Replaces prior OpenStack state."""
cfg = resolve_demo_size(size)
server_count = cfg.servers
volume_count = cfg.volumes
hypervisor_count = cfg.hypervisors
ironic_count = cfg.ironic_nodes
lb_count = cfg.loadbalancers
stack_count = cfg.stacks
fip_count = cfg.floating_ips
surface_sample_count = cfg.surface_samples
server_group_count = cfg.server_groups
nested_sample_count = cfg.nested_samples
pack_per_type = cfg.pack_per_type
extra_networks = cfg.extra_networks
extra_security_groups = cfg.extra_security_groups
keypairs_per_user = cfg.keypairs_per_user
edge_routers = cfg.edge_routers
profile = demo_profile_name(cfg)
await clear_openstack_state(conn)
pw = hash_secret(password, salt=b"openstack-sim-v1")
domain_id = oid("domain:Default")
await conn.execute(
"""INSERT INTO os_domains(id, name, description, enabled)
VALUES($1, 'Default', 'Default domain', true)""",
domain_id,
)
role_admin = oid("role:admin")
role_member = oid("role:member")
await conn.execute(
"""INSERT INTO os_roles(id, name) VALUES ($1, 'admin'), ($2, 'member')""",
role_admin,
role_member,
)
project_ids: dict[str, UUID] = {}
for name, desc in PROJECTS:
pid = oid(f"project:{name}")
project_ids[name] = pid
await conn.execute(
"""INSERT INTO os_projects(id, domain_id, name, description, enabled)
VALUES($1,$2,$3,$4,true)""",
pid,
domain_id,
name,
desc,
)
user_ids: dict[str, UUID] = {}
for uname, _role in USERS:
uid = oid(f"user:{uname}")
user_ids[uname] = uid
await conn.execute(
"""INSERT INTO os_users(id, domain_id, name, password_hash, enabled)
VALUES($1,$2,$3,$4,true)""",
uid,
domain_id,
uname,
pw,
)
# Role assignments
assignments = [
("admin", "admin", role_admin),
("admin", "demo", role_admin),
("admin", "production", role_admin),
("admin", "staging", role_admin),
("admin", "development", role_admin),
("demo", "demo", role_member),
("ops", "production", role_admin),
("ops", "staging", role_admin),
("developer", "development", role_member),
("developer", "staging", role_member),
("auditor", "production", role_member),
("auditor", "demo", role_member),
]
for i, (uname, pname, rid) in enumerate(assignments):
await conn.execute(
"""INSERT INTO os_role_assignments(id, role_id, user_id, project_id)
VALUES($1,$2,$3,$4)
ON CONFLICT (role_id, user_id, project_id) DO NOTHING""",
oid(f"assign:{uname}:{pname}:{i}"),
rid,
user_ids[uname],
project_ids[pname],
)
# AZs + hypervisors + services + aggregates
for az in AZS:
await conn.execute(
"INSERT INTO os_availability_zones(name, zone_state) VALUES($1, $2::jsonb)",
az,
'{"available": true}',
)
for i in range(hypervisor_count):
host = f"compute-{(i // len(AZS)) + 1:02d}.{AZS[i % len(AZS)]}"
az = AZS[i % len(AZS)]
vms_share = max(1, server_count // hypervisor_count)
await conn.execute(
"""INSERT INTO os_hypervisors(
id, hypervisor_hostname, state, status, host_ip, vcpus, vcpus_used,
memory_mb, memory_mb_used, local_gb, local_gb_used, running_vms,
service_host, availability_zone)
VALUES($1,$2,'up','enabled',$3,96,$4,524288,$5,4000,$6,$7,$2,$8)""",
i + 1,
host,
f"10.20.{i // 16}.{(i % 16) + 10}",
min(96, vms_share * 2),
min(400_000, vms_share * 4096),
min(3000, vms_share * 40),
vms_share,
az,
)
await conn.execute(
"""INSERT INTO os_compute_services("binary", host, zone, status, state)
VALUES('nova-compute',$1,$2,'enabled','up')""",
host,
az,
)
for az in AZS:
await conn.execute(
"""INSERT INTO os_compute_services("binary", host, zone, status, state)
VALUES('nova-scheduler',$1,$2,'enabled','up'),
('nova-conductor',$1,$2,'enabled','up')""",
f"controller-{az}",
az,
)
for i, az in enumerate(AZS):
hosts = [
f"compute-{(j // len(AZS)) + 1:02d}.{az}" for j in range(i, hypervisor_count, len(AZS))
]
await conn.execute(
"""INSERT INTO os_aggregates(id, name, availability_zone, hosts, metadata)
VALUES($1,$2,$3,$4::jsonb,'{"pinned":"false"}'::jsonb)""",
i + 1,
f"agg-{az}",
az,
json.dumps(hosts),
)
# Flavors + images
flavors = [
("1", "m1.tiny", 1, 512, 1),
("2", "m1.small", 1, 2048, 20),
("3", "m1.medium", 2, 4096, 40),
("4", "m1.large", 4, 8192, 80),
("5", "m1.xlarge", 8, 16384, 160),
("6", "g1.gpu", 8, 32768, 200),
("7", "c1.highcpu", 16, 8192, 40),
("8", "r1.highmem", 4, 65536, 80),
]
for fid, name, vcpus, ram, disk in flavors:
await conn.execute(
"""INSERT INTO os_flavors(id, name, vcpus, ram, disk, is_public)
VALUES($1,$2,$3,$4,$5,true)""",
fid,
name,
vcpus,
ram,
disk,
)
images = [
("image:cirros", "cirros", 13_287_936),
("image:cirros-full", "cirros-0.6.2-x86_64", 13_287_936),
("image:ubuntu2204", "ubuntu-22.04", 400_000_000),
("image:ubuntu2404", "ubuntu-24.04", 420_000_000),
("image:centos9", "centos-stream-9", 380_000_000),
("image:debian12", "debian-12", 350_000_000),
("image:rocky9", "rocky-9", 390_000_000),
]
image_ids: list[UUID] = []
for key, name, size in images:
iid = oid(key)
image_ids.append(iid)
await conn.execute(
"""INSERT INTO os_images(id, name, status, visibility, size, disk_format,
container_format, owner_project_id)
VALUES($1,$2,'active','public',$3,'qcow2','bare',$4)""",
iid,
name,
size,
project_ids["admin"],
)
# Networks per project + shared public
public_net = oid("net:public")
await conn.execute(
"""INSERT INTO os_networks(id, project_id, name, status, shared, admin_state_up)
VALUES($1,$2,'public','ACTIVE',true,true)""",
public_net,
project_ids["admin"],
)
public_subnet = oid("subnet:public")
await conn.execute(
"""INSERT INTO os_subnets(id, network_id, project_id, name, cidr, ip_version, gateway_ip)
VALUES($1,$2,$3,'public-subnet','203.0.113.0/24',4,'203.0.113.1')""",
public_subnet,
public_net,
project_ids["admin"],
)
project_nets: dict[str, tuple[UUID, UUID]] = {}
cidr_base = {
"demo": 10,
"production": 20,
"staging": 30,
"development": 40,
"admin": 50,
}
for pname, pid in project_ids.items():
base = cidr_base[pname]
net_id = oid(f"net:{pname}-private")
subnet_id = oid(f"subnet:{pname}-private")
project_nets[pname] = (net_id, subnet_id)
await conn.execute(
"""INSERT INTO os_networks(id, project_id, name, status, shared, admin_state_up)
VALUES($1,$2,$3,'ACTIVE',false,true)""",
net_id,
pid,
f"{pname}-net",
)
await conn.execute(
"""INSERT INTO os_subnets(id, network_id, project_id, name, cidr, ip_version, gateway_ip)
VALUES($1,$2,$3,$4,$5,4,$6)""",
subnet_id,
net_id,
pid,
f"{pname}-subnet",
f"10.{base}.0.0/16",
f"10.{base}.0.1",
)
# Router + external gateway
router_id = oid(f"router:{pname}")
await conn.execute(
"""INSERT INTO os_routers(id, project_id, name, status, admin_state_up, external_gateway_info)
VALUES($1,$2,$3,'ACTIVE',true,$4::jsonb)""",
router_id,
pid,
f"{pname}-router",
json.dumps(
{
"network_id": str(public_net),
"enable_snat": True,
"external_fixed_ips": [
{"ip_address": f"203.0.113.{base}", "subnet_id": str(public_subnet)}
],
}
),
)
# Default SG
sg_id = oid(f"sg:{pname}-default")
await conn.execute(
"""INSERT INTO os_security_groups(id, project_id, name, description)
VALUES($1,$2,'default',$3)""",
sg_id,
pid,
f"Default security group for {pname}",
)
for j, (direction, proto, pmin, pmax, prefix) in enumerate(
(
("egress", None, None, None, None),
("ingress", "tcp", 22, 22, "0.0.0.0/0"),
("ingress", "tcp", 80, 80, "0.0.0.0/0"),
("ingress", "tcp", 443, 443, "0.0.0.0/0"),
("ingress", "icmp", None, None, "0.0.0.0/0"),
)
):
await conn.execute(
"""INSERT INTO os_security_group_rules(
id, security_group_id, project_id, direction, ethertype, protocol,
port_range_min, port_range_max, remote_ip_prefix)
VALUES($1,$2,$3,$4,'IPv4',$5,$6,$7,$8)""",
oid(f"sgrule:{pname}:{j}"),
sg_id,
pid,
direction,
proto,
pmin,
pmax,
prefix,
)
# Extra project topology — density scales with cluster size.
extra_net_suffixes = ("mgmt", "storage", "dmz", "backup", "ci", "gpu")
sg_tiers = (
("web", "HTTP/S", 443),
("db", "Database tier", 5432),
("cache", "Cache tier", 6379),
("mq", "Message bus", 5672),
("internal", "Internal RPC", 9696),
("monitoring", "Metrics / scrape", 9100),
)
for pname, pid in project_ids.items():
base = cidr_base[pname]
for extra_i, suffix in enumerate(extra_net_suffixes[:extra_networks], start=1):
net_id = oid(f"net:{pname}-{suffix}")
subnet_id = oid(f"subnet:{pname}-{suffix}")
await conn.execute(
"""INSERT INTO os_networks(id, project_id, name, status, shared, admin_state_up)
VALUES($1,$2,$3,'ACTIVE',false,true)""",
net_id,
pid,
f"{pname}-{suffix}",
)
await conn.execute(
"""INSERT INTO os_subnets(id, network_id, project_id, name, cidr, ip_version, gateway_ip)
VALUES($1,$2,$3,$4,$5,4,$6)""",
subnet_id,
net_id,
pid,
f"{pname}-{suffix}-subnet",
f"10.{base}.{extra_i * 10}.0/24",
f"10.{base}.{extra_i * 10}.1",
)
for sg_name, desc_prefix, port in sg_tiers[:extra_security_groups]:
sg_id = oid(f"sg:{pname}-{sg_name}")
await conn.execute(
"""INSERT INTO os_security_groups(id, project_id, name, description)
VALUES($1,$2,$3,$4)""",
sg_id,
pid,
sg_name,
f"{desc_prefix} for {pname}",
)
await conn.execute(
"""INSERT INTO os_security_group_rules(
id, security_group_id, project_id, direction, ethertype, protocol,
port_range_min, port_range_max, remote_ip_prefix)
VALUES($1,$2,$3,'ingress','IPv4','tcp',$4,$5,'0.0.0.0/0')""",
oid(f"sgrule:{pname}:{sg_name}"),
sg_id,
pid,
port,
port,
)
# Secondary edge routers (0 on tiny clusters, more on big).
for edge_i in range(edge_routers):
await conn.execute(
"""INSERT INTO os_routers(id, project_id, name, status, admin_state_up, external_gateway_info)
VALUES($1,$2,$3,'ACTIVE',true,$4::jsonb)""",
oid(f"router:{pname}-edge-{edge_i}"),
pid,
f"{pname}-edge-router-{edge_i}" if edge_routers > 1 else f"{pname}-edge-router",
json.dumps(
{
"network_id": str(public_net),
"enable_snat": True,
"external_fixed_ips": [
{
"ip_address": f"203.0.113.{base + 1 + edge_i}",
"subnet_id": str(public_subnet),
}
],
}
),
)
# Keypairs (several per user — list is user-scoped; count scales with size)
keypair_suffixes = ("key", "deploy", "ci", "bastion", "ops", "batch", "gpu", "lab")
for uname, uid in user_ids.items():
for suffix in keypair_suffixes[:keypairs_per_user]:
await conn.execute(
"""INSERT INTO os_keypairs(name, user_id, fingerprint, public_key, type)
VALUES($1,$2,$3,$4,'ssh')""",
f"{uname}-{suffix}",
uid,
f"https://example.invalid/{uname}-{suffix}",
f"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC {uname}@{suffix}",
)
# Distribute servers across all lab projects (incl. admin — tokens often use admin)
tenant_cycle = ("admin", "demo", "production", "staging", "development", "demo")
hypervisor_names = [
f"compute-{(i // len(AZS)) + 1:02d}.{AZS[i % len(AZS)]}" for i in range(hypervisor_count)
]
server_rows = []
port_rows = []
for i in range(server_count):
pname = tenant_cycle[i % len(tenant_cycle)]
pid = project_ids[pname]
net_id, _subnet = project_nets[pname]
az = AZS[i % len(AZS)]
host = hypervisor_names[i % len(hypervisor_names)]
flavor = str((i % 8) + 1)
image = image_ids[i % len(image_ids)]
sid = oid(f"server:demo:{i}")
status = _status(i)
base = cidr_base[pname]
ip = f"10.{base}.{(i // 254) + 1}.{(i % 254) + 2}"
mac = f"fa:16:3e:{(i >> 16) & 0xFF:02x}:{(i >> 8) & 0xFF:02x}:{i & 0xFF:02x}"
addresses = {
f"{pname}-net": [
{
"OS-EXT-IPS-MAC:mac_addr": mac,
"version": 4,
"addr": ip,
"OS-EXT-IPS:type": "fixed",
}
]
}
owner = (
user_ids["ops"]
if pname in {"production", "staging"}
else user_ids.get("developer") or user_ids["demo"]
)
if pname == "demo":
owner = user_ids["demo"]
if pname == "admin":
owner = user_ids["admin"]
server_rows.append(
(
sid,
pid,
owner,
_server_name(i),
status,
flavor,
image,
json.dumps(addresses),
json.dumps(
{
"env": pname,
"index": i,
"_tags": [pname, az, PREFIXES[i % len(PREFIXES)], f"idx-{i}"],
}
),
az,
host,
)
)
port_id = oid(f"port:demo:{i}")
port_rows.append(
(
port_id,
net_id,
pid,
f"port-{_server_name(i)}",
"ACTIVE",
mac,
str(sid),
"compute:nova",
json.dumps([{"ip_address": ip, "subnet_id": str(project_nets[pname][1])}]),
)
)
await conn.executemany(
"""INSERT INTO os_servers(
id, project_id, user_id, name, status, flavor_id, image_id,
addresses, metadata, availability_zone, host)
VALUES($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9::jsonb,$10,$11)""",
server_rows,
)
await conn.executemany(
"""INSERT INTO os_ports(
id, network_id, project_id, name, status, mac_address,
device_id, device_owner, fixed_ips)
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb)""",
port_rows,
)
# One create action per server so GET …/os-instance-actions is never empty.
action_rows = [
(
oid(f"nova:instance_action:create:{i}"),
row[1], # project_id
f"create-{i}",
json.dumps(
{
"action": "create",
"instance_uuid": str(row[0]),
"server_id": str(row[0]),
"request_id": f"req-seed-{str(row[0])[:8]}",
"message": None,
}
),
)
for i, row in enumerate(server_rows)
]
await conn.executemany(
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
VALUES($1,'nova','instance_action',$2,$3,'DONE',$4::jsonb)""",
action_rows,
)
# Volumes
volume_rows = []
for i in range(volume_count):
pname = tenant_cycle[i % len(tenant_cycle)]
volume_rows.append(
(
oid(f"volume:demo:{i}"),
project_ids[pname],
f"vol-{pname}-{i:04d}",
"in-use" if i < server_count // 2 else "available",
(i % 5 + 1) * 10,
"lvmdriver-1" if i % 3 else "ceph",
i % 11 == 0,
f"Synthetic volume for {pname}",
)
)
await conn.executemany(
"""INSERT INTO os_volumes(id, project_id, name, status, size, volume_type, bootable, description)
VALUES($1,$2,$3,$4,$5,$6,$7,$8)""",
volume_rows,
)
# Per-server nested rows so any listed server has DB-backed attachments/allocations.
attachment_rows = []
for i in range(server_count):
pname = tenant_cycle[i % len(tenant_cycle)]
sid = str(oid(f"server:demo:{i}"))
vid = str(oid(f"volume:demo:{i % volume_count}"))
port_id = str(oid(f"port:demo:{i}"))
pid = project_ids[pname]
attachment_rows.append(
(
oid(f"nova:volume_attachment:all-{i}"),
"nova",
"volume_attachment",
pid,
f"vattach-all-{i}",
"ACTIVE",
json.dumps(
{
"server_id": sid,
"serverId": sid,
"volume_id": vid,
"volumeId": vid,
"device": "/dev/vdb",
}
),
)
)
attachment_rows.append(
(
oid(f"nova:interface_attachment:all-{i}"),
"nova",
"interface_attachment",
pid,
f"iattach-all-{i}",
"ACTIVE",
json.dumps(
{"server_id": sid, "port_id": port_id, "net_id": str(project_nets[pname][0])}
),
)
)
attachment_rows.append(
(
oid(f"placement:allocation:all-{i}"),
"placement",
"allocation",
None,
f"palloc-all-{i}",
"ACTIVE",
json.dumps(
{
"consumer_uuid": sid,
"resource_provider": str(oid(f"placement:resource_provider:rp-{i % 8}")),
"resource_provider_id": str(oid(f"placement:resource_provider:rp-{i % 8}")),
"resources": {"VCPU": 1, "MEMORY_MB": 1024, "DISK_GB": 10},
"consumer_generation": 1,
}
),
)
)
await conn.executemany(
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
VALUES($1,$2,$3,$4,$5,$6,$7::jsonb)""",
attachment_rows,
)
# Floating IPs
for i in range(fip_count):
pname = tenant_cycle[i % len(tenant_cycle)]
await conn.execute(
"""INSERT INTO os_floating_ips(
id, project_id, floating_ip_address, floating_network_id, port_id, status)
VALUES($1,$2,$3,$4,$5,$6)""",
oid(f"fip:demo:{i}"),
project_ids[pname],
f"203.0.113.{(i % 200) + 20}",
public_net,
port_rows[i][0] if i < len(port_rows) else None,
"ACTIVE" if i % 4 else "DOWN",
)
# Ironic nodes
for i in range(ironic_count):
await conn.execute(
"""INSERT INTO os_nodes(
id, name, driver, provision_state, power_state, resource_class,
properties, driver_info, ports)
VALUES($1,$2,'ipmi',$3,$4,'baremetal',$5::jsonb,'{}'::jsonb,'[]'::jsonb)""",
oid(f"node:demo:{i}"),
f"baremetal-{i:02d}",
"active" if i % 5 == 0 else "available",
"power on" if i % 5 == 0 else "power off",
json.dumps({"cpus": 64, "memory_mb": 262144, "local_gb": 2000, "az": AZS[i % 3]}),
)
# Octavia LBs
for i in range(lb_count):
pname = tenant_cycle[i % len(tenant_cycle)]
await conn.execute(
"""INSERT INTO os_loadbalancers(
id, project_id, name, description, vip_address, vip_subnet_id,
provisioning_status, operating_status, listeners, pools)
VALUES($1,$2,$3,$4,$5,$6,'ACTIVE','ONLINE',$7::jsonb,$8::jsonb)""",
oid(f"lb:demo:{i}"),
project_ids[pname],
f"lb-{pname}-{i:02d}",
f"Synthetic LB for {pname}",
f"10.{cidr_base[pname]}.200.{i + 1}",
project_nets[pname][1],
json.dumps(
[{"id": str(oid(f"listener:{i}")), "protocol": "HTTP", "protocol_port": 80}]
),
json.dumps(
[{"id": str(oid(f"pool:{i}")), "lb_algorithm": "ROUND_ROBIN", "protocol": "HTTP"}]
),
)
# Heat stacks
for i in range(stack_count):
pname = tenant_cycle[i % len(tenant_cycle)]
await conn.execute(
"""INSERT INTO os_stacks(
id, project_id, stack_name, stack_status, description, template, parameters, outputs)
VALUES($1,$2,$3,'CREATE_COMPLETE',$4,$5::jsonb,'{}'::jsonb,'[]'::jsonb)""",
oid(f"stack:demo:{i}"),
project_ids[pname],
f"stack-{pname}-{i:02d}",
f"Synthetic Heat stack {i}",
json.dumps({"heat_template_version": "2015-04-30", "resources": {}}),
)
# Swift
for pname, pid in project_ids.items():
account = f"AUTH_{pid}"
for cname in ("images", "backups", "artifacts"):
await conn.execute(
"""INSERT INTO os_swift_containers(account, name, meta)
VALUES($1,$2,'{}'::jsonb) ON CONFLICT DO NOTHING""",
account,
cname,
)
await conn.execute(
"""INSERT INTO os_swift_objects(id, account, container, name, content_type, bytes, body, meta)
VALUES($1,$2,$3,$4,'text/plain',$5,$6,'{}'::jsonb)
ON CONFLICT DO NOTHING""",
oid(f"swift:{pname}:{cname}:readme"),
account,
cname,
"readme.txt",
16,
f"hello {pname}\n".encode(),
)
# Generic service samples (multiple per service) — keep resource_type aligned
# with pack operation resource_type so schema list endpoints return rows.
samples = []
for i in range(surface_sample_count):
samples.extend(
[
("barbican", "secret", f"secret-{i}", {"secret_type": "passphrase"}),
(
"barbican",
"container",
f"container-{i}",
{"type": "generic", "status": "ACTIVE"},
),
("barbican", "order", f"order-{i}", {"type": "key", "status": "ACTIVE"}),
("barbican", "secret_store", f"store-{i}", {"status": "ACTIVE"}),
(
"manila",
"share",
f"share-{i}",
{"size": 50, "share_proto": "NFS", "status": "available"},
),
(
"manila",
"share_snapshot",
f"share-snap-{i}",
{"status": "available", "size": 50},
),
("manila", "share_network", f"share-net-{i}", {"status": "active"}),
("manila", "share_type", f"share-type-{i}", {"is_public": True}),
("manila", "share_server", f"share-srv-{i}", {"status": "active"}),
("manila", "security_service", f"sec-svc-{i}", {"type": "ldap", "status": "new"}),
("manila", "share_group", f"share-grp-{i}", {"status": "available"}),
("manila", "share_replica", f"share-rep-{i}", {"status": "available"}),
("designate", "zone", f"zone{i}.lab.", {"email": "hostmaster@lab", "ttl": 3600}),
("designate", "tld", f"tld-{i}", {"name": f"lab{i}"}),
("designate", "blacklist", f"bl-{i}", {"pattern": f"^bad{i}\\..*"}),
("designate", "pool", f"pool-{i}", {"name": f"pool-{i}"}),
("designate", "service_status", f"dns-svc-{i}", {"status": "UP"}),
(
"magnum",
"cluster",
f"k8s-{i}",
{"coe": "kubernetes", "status": "CREATE_COMPLETE", "node_count": 3},
),
(
"magnum",
"clustertemplate",
f"k8s-tmpl-{i}",
{"coe": "kubernetes", "image_id": "cirros"},
),
("magnum", "certificate", f"cert-{i}", {"cluster_uuid": f"cluster-{i}"}),
("zun", "container", f"ctr-{i}", {"image": "nginx", "status": "Running"}),
(
"trove",
"instance",
f"db-{i}",
{"datastore": {"type": "mysql"}, "status": "ACTIVE"},
),
("mistral", "workflow", f"wf-{i}", {"definition": "version: '2.0'"}),
("mistral", "execution", f"exec-{i}", {"state": "SUCCESS"}),
("mistral", "action", f"action-{i}", {"is_system": False}),
("mistral", "workbook", f"wb-{i}", {"definition": "version: '2.0'"}),
("mistral", "cron_trigger", f"cron-{i}", {"pattern": "0 * * * *"}),
("mistral", "task", f"task-{i}", {"state": "SUCCESS"}),
("aodh", "alarm", f"alarm-{i}", {"type": "threshold", "state": "ok"}),
("aodh", "quota", f"aodh-quota-{i}", {"alarm": 100}),
("freezer", "job", f"job-{i}", {"status": "scheduled"}),
("freezer", "client", f"client-{i}", {"status": "available"}),
("freezer", "backup", f"backup-{i}", {"status": "available"}),
("freezer", "session", f"session-{i}", {"status": "scheduled"}),
("freezer", "action", f"freezer-action-{i}", {"status": "available"}),
("blazar", "lease", f"lease-{i}", {"status": "ACTIVE"}),
("blazar", "host", f"blazar-host-{i}", {"status": "available"}),
(
"blazar",
"floatingip",
f"blazar-fip-{i}",
{"floating_ip_address": f"198.51.100.{i + 10}"},
),
("masakari", "segment", f"segment-{i}", {"recovery_method": "auto"}),
("masakari", "notification", f"notif-{i}", {"status": "finished"}),
(
"masakari",
"host",
f"masakari-host-{i}",
{
"name": f"compute-{(i % 3) + 1}",
"type": "compute",
"reserved": False,
"on_maintenance": False,
"segment_id": f"segment-{i % 8}",
},
),
("tacker", "vnf", f"vnf-{i}", {"status": "ACTIVE"}),
("adjutant", "task", f"task-{i}", {"status": "open"}),
("adjutant", "token", f"token-{i}", {"status": "active"}),
("adjutant", "notification", f"adj-notif-{i}", {"status": "sent"}),
(
"adjutant",
"status",
f"adj-status-{i}",
{"status": "UP", "service": "adjutant", "state": "up"},
),
(
"designate",
"recordset",
f"rs-{i}",
{
"zone_id": f"zone{i % 8}.lab.",
"type": "A",
"records": [f"203.0.113.{i + 10}"],
"ttl": 3600,
},
),
(
"zaqar",
"message",
f"zmsg-{i}",
{"queue_name": f"queue-{i % 8}", "body": {"event": f"demo-{i}"}, "ttl": 3600},
),
(
"zaqar",
"claim",
f"zclaim-{i}",
{"queue_name": f"queue-{i % 8}", "ttl": 300, "grace": 60},
),
(
"zaqar",
"subscription",
f"zsub-{i}",
{
"queue_name": f"queue-{i % 8}",
"subscriber": f"http://hook.lab/{i}",
"ttl": 3600,
},
),
("cloudkitty", "hashmap_service", f"svc-{i}", {"name": f"svc-{i}"}),
("cloudkitty", "hashmap_field", f"field-{i}", {"name": f"field-{i}"}),
("cloudkitty", "report_summary", f"summary-{i}", {"tenant_id": "demo"}),
("cloudkitty", "dataframes", f"df-{i}", {"period": "3600"}),
(
"vitrage",
"alarm",
f"vit-alarm-{i}",
{"state": "critical" if i % 3 == 0 else "ok"},
),
("heat-cfn", "stack", f"cfn-{i}", {"StackStatus": "CREATE_COMPLETE"}),
("cinder", "snapshot", f"snap-{i}", {"status": "available", "size": 10}),
("cinder", "backup", f"vol-backup-{i}", {"status": "available", "size": 10}),
("cinder", "volume_type", f"type-{i}", {"is_public": True}),
("cinder", "qos_spec", f"qos-{i}", {"consumer": "front-end"}),
("cinder", "group", f"cg-{i}", {"status": "available"}),
("cinder", "group_snapshot", f"cgsnap-{i}", {"status": "available"}),
("cinder", "consistencygroup", f"consis-{i}", {"status": "available"}),
("cinder", "attachment", f"attach-{i}", {"status": "attached"}),
("cinder", "transfer", f"xfer-{i}", {"status": "awaiting-transfer"}),
("cinder", "message", f"msg-{i}", {"message_level": "ERROR"}),
("cinder", "cluster", f"cinder-cl-{i}", {"state": "up", "status": "enabled"}),
(
"cinder",
"service",
f"cinder-svc-{i}",
{"binary": "cinder-volume", "state": "up"},
),
("glance", "metadef_namespace", f"ns-{i}", {"visibility": "public"}),
("glance", "task", f"task-{i}", {"type": "import", "status": "success"}),
(
"ironic",
"driver",
"ipmi" if i == 0 else f"redfish-{i}",
{
"name": "ipmi" if i == 0 else "redfish",
"hosts": ["simulator"],
"type": "classic",
},
),
(
"ironic",
"port",
f"iport-{i}",
{"address": f"52:54:00:00:00:{i:02x}", "pxe_enabled": True},
),
("ironic", "portgroup", f"pg-{i}", {"mode": "active-backup"}),
("ironic", "chassis", f"chassis-{i}", {"description": f"rack-{i}"}),
(
"ironic",
"allocation",
f"alloc-{i}",
{"state": "active", "resource_class": "baremetal"},
),
("ironic", "deploy_template", f"dt-{i}", {"steps": []}),
(
"ironic",
"volume_connector",
f"vc-{i}",
{"type": "iqn", "connector_id": f"iqn.lab:{i}"},
),
("ironic", "volume_target", f"vt-{i}", {"volume_type": "iscsi", "boot_index": 0}),
("keystone", "domain", f"dom-{i}", {"enabled": True}),
("keystone", "group", f"group-{i}", {"description": f"group {i}"}),
("keystone", "region", f"Region{i}", {"description": f"region {i}"}),
("keystone", "service", f"svc-cat-{i}", {"type": "compute", "enabled": True}),
(
"keystone",
"endpoint",
f"ep-{i}",
{"interface": "public", "url": f"http://svc{i}.lab:8774"},
),
("keystone", "credential", f"cred-{i}", {"type": "ec2"}),
("keystone", "policy", f"policy-{i}", {"type": "application/json"}),
("neutron", "address_group", f"ag-{i}", {"addresses": [f"10.10.{i}.0/24"]}),
("neutron", "segment", f"seg-{i}", {"network_type": "vxlan"}),
("neutron", "bgp_speaker", f"bgp-sp-{i}", {"local_as": 65000 + i}),
(
"neutron",
"bgp_peer",
f"bgp-peer-{i}",
{"peer_ip": f"192.0.2.{i + 1}", "remote_as": 65010},
),
("watcher", "audit_template", f"at-{i}", {"goal": "server_consolidation"}),
("watcher", "audit", f"audit-{i}", {"state": "SUCCEEDED"}),
("watcher", "action_plan", f"ap-{i}", {"state": "SUCCEEDED"}),
("watcher", "action", f"w-action-{i}", {"state": "SUCCEEDED"}),
("watcher", "goal", f"goal-{i}", {"display_name": f"Goal {i}"}),
("watcher", "strategy", f"strategy-{i}", {"goal_uuid": f"goal-{i}"}),
("watcher", "scoring_engine", f"se-{i}", {"description": f"engine {i}"}),
("zaqar", "queue", f"queue-{i}", {"_default_message_ttl": 3600}),
# Remaining surface collections previously empty in lab GET probes
("neutron", "address_scope", f"ascope-{i}", {"ip_version": 4, "shared": True}),
(
"neutron",
"subnetpool",
f"spool-{i}",
{"default_prefixlen": 24, "prefixes": [f"10.2{i}.0.0/16"]},
),
("neutron", "qos_policy", f"qospol-{i}", {"shared": False, "is_default": i == 0}),
("neutron", "trunk", f"trunk-{i}", {"status": "ACTIVE", "admin_state_up": True}),
(
"neutron",
"rbac_policy",
f"rbac-{i}",
{"action": "access_as_shared", "object_type": "network"},
),
("neutron", "metering_label", f"mlabel-{i}", {"description": f"label {i}"}),
(
"neutron",
"metering_label_rule",
f"mlrule-{i}",
{"direction": "ingress", "remote_ip_prefix": "0.0.0.0/0"},
),
(
"neutron",
"firewall_group",
f"fwg-{i}",
{"status": "ACTIVE", "admin_state_up": True},
),
("neutron", "firewall_policy", f"fwp-{i}", {"shared": False, "audited": True}),
(
"neutron",
"firewall_rule",
f"fwr-{i}",
{"protocol": "tcp", "action": "allow", "enabled": True},
),
(
"neutron",
"vpn_service",
f"vpns-{i}",
{"status": "ACTIVE", "admin_state_up": True},
),
(
"neutron",
"ipsec_site_connection",
f"ipsec-{i}",
{"status": "ACTIVE", "psk": "secret"},
),
(
"neutron",
"ike_policy",
f"ike-{i}",
{"auth_algorithm": "sha256", "encryption_algorithm": "aes-256"},
),
("neutron", "ipsec_policy", f"ipsecpol-{i}", {"transform_protocol": "esp"}),
(
"neutron",
"vpn_endpoint_group",
f"vepg-{i}",
{"type": "cidr", "endpoints": [f"10.3{i}.0.0/24"]},
),
(
"neutron",
"bgpvpn",
f"bgpvpn-{i}",
{"type": "l3", "route_targets": [f"64512:{i}"]},
),
(
"neutron",
"log",
f"nlog-{i}",
{"enabled": True, "resource_type": "security_group"},
),
("neutron", "ndp_proxy", f"ndp-{i}", {"ip_address": f"2001:db8::{i}"}),
(
"neutron",
"local_ip",
f"lip-{i}",
{"local_ip_address": f"10.0.0.{i + 20}", "ip_mode": "translate"},
),
(
"neutron",
"network_segment_range",
f"nsr-{i}",
{"network_type": "vxlan", "minimum": 100 + i, "maximum": 200 + i},
),
("neutron", "service_profile", f"sprof-{i}", {"driver": "dummy", "enabled": True}),
(
"neutron",
"neutron_flavor",
f"nflav-{i}",
{"service_type": "LOADBALANCERV2", "enabled": True},
),
(
"neutron",
"default_security_group_rule",
f"dsgr-{i}",
{"direction": "ingress", "ethertype": "IPv4", "protocol": "tcp"},
),
(
"neutron",
"lbaas_loadbalancer",
f"n-lb-{i}",
{"provisioning_status": "ACTIVE", "operating_status": "ONLINE"},
),
(
"neutron",
"lbaas_listener",
f"n-li-{i}",
{"protocol": "HTTP", "protocol_port": 80},
),
(
"neutron",
"lbaas_pool",
f"n-pool-{i}",
{"lb_algorithm": "ROUND_ROBIN", "protocol": "HTTP"},
),
(
"neutron",
"qos_rule_type",
f"qrt-{i}",
{"type": "bandwidth_limit", "drivers": ["openvswitch"]},
),
(
"neutron",
"network_ip_availability",
f"nipa-{i}",
{"network_name": f"net-{i}", "total_ips": 254, "used_ips": i},
),
("neutron", "auto_allocated_topology", f"aat-{i}", {"tenant_id": "demo"}),
(
"neutron",
"agent",
f"agent-{i}",
{"agent_type": "L3 agent", "alive": True, "host": f"net-{i}"},
),
(
"nova",
"extension",
f"ext-{i}",
{
"alias": f"ext-{i}",
"name": f"Extension {i}",
"namespace": "http://docs.openstack.org",
},
),
(
"nova",
"migration",
f"mig-{i}",
{
"status": "completed",
"migration_type": "migration",
"source_compute": f"compute-{(i % 3) + 1}",
"dest_compute": f"compute-{((i + 1) % 3) + 1}",
"instance_uuid": str(oid(f"server:demo:{i}")),
},
),
(
"nova",
"network",
f"nova-net-{i}",
{"label": f"nova-net-{i}", "cidr": f"10.9{i}.0.0/24"},
),
("nova", "security_group", f"nsg-{i}", {"description": f"nova sg {i}"}),
(
"nova",
"floating_ip",
f"nfip-{i}",
{"ip": f"203.0.113.{i + 50}", "pool": "public"},
),
("nova", "instance_usage_audit_log", f"iual-{i}", {"hosts_not_run": [], "log": {}}),
(
"nova",
"assisted_volume_snapshot",
f"avs-{i}",
{"id": f"avs-{i}", "volume_id": f"vol-{i}"},
),
(
"nova",
"usage",
f"usage-{i}",
{"tenant_id": "demo", "total_hours": 10.0 * (i + 1)},
),
(
"nova",
"host",
f"host-{i}",
{"host_name": f"compute-{(i % 3) + 1}", "service": "compute", "zone": "nova"},
),
(
"nova",
"agent",
f"nagent-{i}",
{
"hypervisor": "qemu",
"os": "linux",
"architecture": "x86_64",
"version": "1.0",
},
),
(
"octavia",
"listener",
f"ol-{i}",
{"protocol": "HTTP", "protocol_port": 80, "provisioning_status": "ACTIVE"},
),
(
"octavia",
"pool",
f"op-{i}",
{
"lb_algorithm": "ROUND_ROBIN",
"protocol": "HTTP",
"provisioning_status": "ACTIVE",
},
),
(
"octavia",
"healthmonitor",
f"ohm-{i}",
{"type": "HTTP", "delay": 5, "timeout": 3, "max_retries": 3},
),
(
"octavia",
"l7policy",
f"ol7-{i}",
{"action": "REJECT", "provisioning_status": "ACTIVE"},
),
(
"octavia",
"flavor",
f"oflav-{i}",
{"enabled": True, "description": f"octavia flavor {i}"},
),
(
"octavia",
"flavorprofile",
f"ofp-{i}",
{"provider_name": "amphora", "flavor_data": "{}"},
),
(
"octavia",
"amphora",
f"amph-{i}",
{"status": "ALLOCATED", "role": "MASTER", "cached_zone": "nova"},
),
("octavia", "quota", f"oquota-{i}", {"load_balancer": 10, "listener": 50}),
(
"octavia",
"provider",
f"provider-{i}",
{
"name": f"{('amphora', 'ovn', 'octavia', 'noop')[i % 4]}-{i}",
"description": f"Load balancer provider {i}",
},
),
("placement", "resource_class", f"rc-{i}", {"name": f"CUSTOM_CLASS_{i}"}),
("placement", "trait", f"trait-{i}", {"name": f"CUSTOM_TRAIT_{i}"}),
("placement", "allocation_candidate", f"ac-{i}", {"allocations": {}}),
("placement", "usage", f"pusage-{i}", {"resource_class": "VCPU", "usage": i}),
(
"placement",
"resource_provider",
f"rp-{i}",
{
"name": f"resource_provider-{i}",
"generation": 1,
"parent_provider_uuid": None,
},
),
(
"placement",
"inventory",
f"inv-{i}",
{
"resource_provider": str(oid(f"placement:resource_provider:rp-{i}")),
"resource_provider_id": str(oid(f"placement:resource_provider:rp-{i}")),
"resource_class": "VCPU",
"total": 64,
"reserved": 0,
},
),
(
"placement",
"aggregate",
f"pagg-{i}",
{
"name": f"agg-{i}",
"resource_provider": str(oid(f"placement:resource_provider:rp-{i}")),
"resource_provider_id": str(oid(f"placement:resource_provider:rp-{i}")),
},
),
("tacker", "vnfd", f"vnfd-{i}", {"name": f"vnfd-{i}", "description": "demo"}),
("tacker", "vim", f"vim-{i}", {"type": "openstack", "status": "REACHABLE"}),
(
"tacker",
"vnf_package",
f"vnfpkg-{i}",
{"onboardingState": "ONBOARDED", "operationalState": "ENABLED"},
),
("tacker", "vnf_instance", f"vnfinst-{i}", {"instantiationState": "INSTANTIATED"}),
("trove", "datastore", f"ds-{i}", {"name": "mysql", "version": f"8.0.{i}"}),
("trove", "backup", f"tbak-{i}", {"status": "COMPLETED", "size": 1.5}),
("trove", "configuration", f"tcfg-{i}", {"datastore_name": "mysql"}),
("trove", "cluster", f"tcl-{i}", {"task": {"name": "NONE"}, "instance_count": 3}),
("vitrage", "topology", f"topo-{i}", {"nodes": [], "links": []}),
("vitrage", "resource", f"vres-{i}", {"type": "nova.instance", "state": "ACTIVE"}),
("vitrage", "template", f"vtmpl-{i}", {"type": "standard", "status": "active"}),
("vitrage", "event", f"vevt-{i}", {"type": "compute.host.down"}),
("watcher", "service", f"wsvc-{i}", {"host": f"watcher-{i}", "status": "ACTIVE"}),
("zun", "image", f"zimg-{i}", {"image": "nginx", "status": "ACTIVE"}),
("zun", "capsule", f"cap-{i}", {"status": "Running", "cpu": 1, "memory": 512}),
("zun", "host", f"zhost-{i}", {"hostname": f"zun-compute-{i}", "state": "up"}),
(
"zun",
"service",
f"zsvc-{i}",
{"host": f"zun-{i}", "binary": "zun-compute", "state": "up"},
),
("cinder", "limit", f"clim-{i}", {"name": f"limit-{i}", "value": 1000 + i}),
(
"cinder",
"resource_filter",
f"rf-{i}",
{"resource": "volume", "filters": ["name", "status"]},
),
(
"cinder",
"pool",
f"cpool-{i}",
{"name": f"pool@backend#{i}", "capabilities": {"free_capacity_gb": 1000}},
),
(
"ironic",
"conductor",
f"cond-{i}",
{"hostname": f"ironic-{i}", "conductor_group": "", "alive": True},
),
(
"keystone",
"role_assignment",
f"ra-{i}",
{
"role": {"id": f"role-{i}"},
"user": {"id": f"user-{i}"},
"scope": {"project": {"id": "demo"}},
},
),
(
"keystone",
"limit",
f"klim-{i}",
{"resource_name": "servers", "resource_limit": 100 + i},
),
(
"keystone",
"registered_limit",
f"krlim-{i}",
{"resource_name": "servers", "default_limit": 50 + i},
),
("glance", "info_import", f"gimport-{i}", {"type": "glance-direct"}),
(
"glance",
"info_store",
f"gstore-{i}",
{"id": f"store-{i}", "type": "file", "description": f"store {i}"},
),
(
"glance",
"schema",
f"gschema-{i}",
{"name": "image", "properties": {"name": {"type": "string"}}},
),
("zaqar", "health", f"zhealth-{i}", {"catalog": True, "storage": True}),
("zaqar", "ping", f"zping-{i}", {"ok": True}),
]
)
for service, rtype, name, data in samples:
item_id = oid(f"{service}:{rtype}:{name}")
payload = {"id": str(item_id), "name": name, "status": data.get("status", "ACTIVE"), **data}
await conn.execute(
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
VALUES($1,$2,$3,$4,$5,$6,$7::jsonb)""",
item_id,
service,
rtype,
None, # shared across projects so admin/demo/… tokens all see density
name,
payload["status"],
json.dumps(payload),
)
# Nova server groups (specialized table) — denser in demo project
for i in range(server_group_count):
pname = (
"demo" if i < max(1, server_group_count // 2) else tenant_cycle[i % len(tenant_cycle)]
)
span = max(1, min(8, max(1, server_count // 4)))
members = [
str(oid(f"server:demo:{(3 + (i % span) * 6) % server_count}")),
str(oid(f"server:demo:{(3 + ((i + 1) % span) * 6) % server_count}")),
]
await conn.execute(
"""INSERT INTO os_server_groups(id, project_id, name, policies, members)
VALUES($1,$2,$3,$4::jsonb,$5::jsonb)""",
oid(f"sgroup:demo:{i}"),
project_ids[pname],
f"sg-{pname}-{i}",
json.dumps(["soft-anti-affinity"] if i % 2 == 0 else ["anti-affinity"]),
json.dumps(members),
)
# Nested / parent-scoped collections used by pack GET probes.
# tenant_cycle index 3,9,15… maps to the demo project for servers/volumes/ports.
demo_pid = project_ids["demo"]
demo_router = str(oid("router:demo"))
demo_fip = str(oid("fip:demo:3"))
demo_image = str(image_ids[0])
demo_qos = str(oid("neutron:qos_policy:qospol-0"))
demo_trunk = str(oid("neutron:trunk:trunk-0"))
demo_local_ip = str(oid("neutron:local_ip:lip-0"))
demo_bgpvpn = str(oid("neutron:bgpvpn:bgpvpn-0"))
demo_stack_idx = min(3, max(0, stack_count - 1))
demo_stack_id = str(oid(f"stack:demo:{demo_stack_idx}"))
demo_stack_name = f"stack-demo-{demo_stack_idx:02d}"
nested_samples: list[tuple[str, str, str, dict[str, Any]]] = []
# Cover the first listed servers (and a wider spread) so nested GETs hit DB rows.
for i in range(nested_sample_count):
sidx = i % server_count
sid = str(oid(f"server:demo:{sidx}"))
vid = str(oid(f"volume:demo:{sidx % volume_count}"))
port_id = str(oid(f"port:demo:{sidx}"))
nested_samples.extend(
[
(
"nova",
"volume_attachment",
f"vattach-{i}",
{
"server_id": sid,
"serverId": sid,
"volume_id": vid,
"volumeId": vid,
"device": f"/dev/vd{chr(98 + (i % 4))}",
},
),
(
"nova",
"interface_attachment",
f"iattach-{i}",
{"server_id": sid, "port_id": port_id, "net_id": str(project_nets["demo"][0])},
),
(
"nova",
"server_metadata",
f"smeta-{i}",
{
"server_id": sid,
"key": "env",
"value": "demo",
"metadata": {"env": "demo", "index": str(sidx)},
},
),
(
"nova",
"server_tag",
f"stag-{i}",
{"server_id": sid, "tags": [f"az-{AZS[i % 3]}", "demo", f"idx-{sidx}"]},
),
(
"nova",
"server_security_group",
f"ssg-{i}",
{"server_id": sid, "name": "default", "id": str(oid("sg:demo-default"))},
),
(
"nova",
"server_migration",
f"smig-{i}",
{
"server_id": sid,
"status": "completed",
"migration_type": "migration",
"source_compute": f"compute-{(i % 3) + 1}",
},
),
(
"nova",
"console",
f"console-{i}",
{
"server_id": sid,
"protocol": "vnc",
"type": "novnc",
"url": f"http://console.lab:6080/vnc_auto.html?token=demo-{i}",
},
),
(
"nova",
"instance_action",
f"iaction-{i}",
{
"server_id": sid,
"action": "create",
"instance_uuid": sid,
"request_id": f"req-demo-{i}",
"message": None,
},
),
(
"nova",
"flavor_extra_spec",
f"fes-{i}",
{
"flavor_id": str((i % 8) + 1),
"extra_specs": {
"hw:cpu_policy": "shared",
"aggregate_instance_extra_specs:demo": "true",
},
},
),
(
"neutron",
"conntrack_helper",
f"cth-{i}",
{"router_id": demo_router, "protocol": "tcp", "port": 22 + i, "helper": "ftp"},
),
(
"neutron",
"floatingip_port_forwarding",
f"fpf-{i}",
{
"floatingip_id": demo_fip,
"internal_port_id": port_id,
"internal_ip_address": f"10.10.0.{i + 10}",
"internal_port": 8080 + i,
"external_port": 9000 + i,
"protocol": "tcp",
},
),
(
"neutron",
"qos_bandwidth_limit_rule",
f"qbl-{i}",
{
"policy_id": demo_qos,
"max_kbps": 10000 * (i + 1),
"max_burst_kbps": 1000,
"direction": "egress",
},
),
(
"neutron",
"qos_dscp_marking_rule",
f"qdscp-{i}",
{"policy_id": demo_qos, "dscp_mark": i % 64},
),
(
"neutron",
"qos_minimum_bandwidth_rule",
f"qmb-{i}",
{"policy_id": demo_qos, "min_kbps": 1000 * (i + 1), "direction": "egress"},
),
(
"neutron",
"trunk_subport",
f"tsp-{i}",
{
"trunk_id": demo_trunk,
"port_id": port_id,
"segmentation_type": "vlan",
"segmentation_id": 100 + i,
},
),
(
"neutron",
"local_ip_association",
f"lia-{i}",
{
"local_ip_id": demo_local_ip,
"fixed_port_id": port_id,
"fixed_ip": f"10.10.0.{i + 20}",
},
),
(
"neutron",
"bgpvpn_network_association",
f"bna-{i}",
{"bgpvpn_id": demo_bgpvpn, "network_id": str(project_nets["demo"][0])},
),
(
"neutron",
"bgpvpn_router_association",
f"bra-{i}",
{"bgpvpn_id": demo_bgpvpn, "router_id": demo_router},
),
(
"glance",
"image_member",
f"imem-{i}",
{
"image_id": demo_image,
"member_id": str(list(project_ids.values())[i % len(project_ids)]),
"status": "accepted",
},
),
(
"glance",
"image_tag",
f"itag-{i}",
{"image_id": demo_image, "tags": [f"tag-{i}", "demo", "lab"]},
),
(
"heat",
"stack_resource",
f"sres-{i}",
{
"tenant_id": str(demo_pid),
# Soft parent filter: omit stack_* so any stack list is populated.
"resource_name": f"resource_{i}",
"resource_type": "OS::Nova::Server",
"resource_status": "CREATE_COMPLETE",
"physical_resource_id": sid,
},
),
(
"heat",
"stack_event",
f"sevt-{i}",
{
"tenant_id": str(demo_pid),
"resource_name": f"resource_{i}",
"resource_status": "CREATE_COMPLETE",
"event_time": "2026-01-01T00:00:00Z",
},
),
(
"heat",
"software_config",
f"swcfg-{i}",
{
"tenant_id": str(demo_pid),
"group": "script",
"config": f"#!/bin/bash\necho demo-{i}\n",
},
),
(
"heat",
"software_deployment",
f"swdep-{i}",
{
"tenant_id": str(demo_pid),
"status": "COMPLETE",
"server_id": sid,
"config_id": f"swcfg-{i}",
},
),
(
"heat",
"resource_type",
f"rtype-{i}",
{
"tenant_id": str(demo_pid),
"resource_type": f"OS::Demo::Type{i}",
"attributes": {},
},
),
(
"heat",
"service",
f"hsvc-{i}",
{
"tenant_id": str(demo_pid),
"host": f"heat-{i}",
"binary": "heat-engine",
"status": "up",
},
),
(
"placement",
"allocation",
f"palloc-{i}",
{
"consumer_uuid": sid,
"resource_provider": str(oid(f"placement:resource_provider:rp-{i % 8}")),
"resources": {"VCPU": 1, "MEMORY_MB": 1024, "DISK_GB": 10},
},
),
]
)
for service, rtype, name, data in nested_samples:
item_id = oid(f"{service}:{rtype}:{name}")
payload = {"id": str(item_id), "name": name, "status": data.get("status", "ACTIVE"), **data}
# Scope nested rows to the parent server's project (tenant_cycle index).
owner_pid = demo_pid
server_ref = str(data.get("server_id") or data.get("instance_uuid") or "")
for idx in range(min(server_count, 64)):
if server_ref == str(oid(f"server:demo:{idx}")):
owner_pid = project_ids[tenant_cycle[idx % len(tenant_cycle)]]
break
await conn.execute(
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
VALUES($1,$2,$3,$4,$5,$6,$7::jsonb)""",
item_id,
service,
rtype,
owner_pid,
name,
str(payload.get("status") or "ACTIVE"),
json.dumps(payload),
)
# Quotas as api objects (name=project id so Neutron-style /quotas/{project_id} resolves)
quota_factor = max(1.0, server_count / 1000)
for pname, pid in project_ids.items():
for svc, rtype, data in (
(
"nova",
"quota_set",
{
"instances": _scale(200, quota_factor, 40),
"cores": _scale(800, quota_factor, 80),
"ram": _scale(1_024_000, quota_factor, 64_000),
},
),
(
"cinder",
"quota_set",
{
"volumes": _scale(200, quota_factor, 40),
"gigabytes": _scale(50_000, quota_factor, 5_000),
},
),
(
"neutron",
"quota",
{
"network": _scale(50, quota_factor, 10),
"subnet": _scale(100, quota_factor, 20),
"port": _scale(500, quota_factor, 80),
"floatingip": _scale(50, quota_factor, 10),
},
),
):
await conn.execute(
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
VALUES($1,$2,$3,$4,$5,'ACTIVE',$6::jsonb)
ON CONFLICT (id) DO UPDATE SET
name=EXCLUDED.name, status=EXCLUDED.status, data=EXCLUDED.data,
project_id=EXCLUDED.project_id, updated_at=now()""",
oid(f"quota:{svc}:{pname}"),
svc,
rtype,
pid,
str(pid),
json.dumps({"id": str(pid), "project_id": str(pid), "tenant_id": str(pid), **data}),
)
from app.openstack.pack_seed import seed_pack_surface_samples
from app.openstack.seed_discovery import seed_discovery_documents
discovery = await seed_discovery_documents(conn)
pack_seed = await seed_pack_surface_samples(conn, per_type=pack_per_type)
pack_seed = {**pack_seed, **discovery}
await conn.execute(
"""INSERT INTO os_demo_meta(key, value)
VALUES('profile', $1), ('size', $2), ('servers', $3), ('password', $4)""",
profile,
cfg.name,
str(server_count),
password,
)
return {
"profile": profile,
"size": cfg.name,
"servers": server_count,
"pack_seed": pack_seed,
"volumes": volume_count,
"hypervisors": hypervisor_count,
"ironic_nodes": ironic_count,
"loadbalancers": lb_count,
"stacks": stack_count,
"floating_ips": fip_count,
"extra_networks": extra_networks,
"extra_security_groups": extra_security_groups,
"keypairs_per_user": keypairs_per_user,
"edge_routers": edge_routers,
"projects": list(project_ids.keys()),
"users": list(user_ids.keys()),
"password": password,
"availability_zones": list(AZS),
"cluster": asdict(cfg),
}
async def openstack_demo_summary(conn: Connection) -> dict[str, Any]:
profile = await conn.fetchval("SELECT value FROM os_demo_meta WHERE key='profile'")
size = await conn.fetchval("SELECT value FROM os_demo_meta WHERE key='size'")
servers = await conn.fetchval("SELECT count(*) FROM os_servers")
volumes = await conn.fetchval("SELECT count(*) FROM os_volumes")
networks = await conn.fetchval("SELECT count(*) FROM os_networks")
ports = await conn.fetchval("SELECT count(*) FROM os_ports")
images = await conn.fetchval("SELECT count(*) FROM os_images")
hypervisors = await conn.fetchval("SELECT count(*) FROM os_hypervisors")
projects = await conn.fetchval("SELECT count(*) FROM os_projects")
users = await conn.fetchval("SELECT count(*) FROM os_users")
lbs = await conn.fetchval("SELECT count(*) FROM os_loadbalancers")
stacks = await conn.fetchval("SELECT count(*) FROM os_stacks")
nodes = await conn.fetchval("SELECT count(*) FROM os_nodes")
fips = await conn.fetchval("SELECT count(*) FROM os_floating_ips")
loaded = is_demo_profile(profile)
cfg = DEMO_CLUSTER_SIZES.get(str(size or ""), None)
if cfg is None and loaded and isinstance(profile, str) and ":" in profile:
cfg = DEMO_CLUSTER_SIZES.get(profile.rsplit(":", 1)[-1])
return {
"loaded": loaded,
"profile": profile or "minimal",
"size": (cfg.name if cfg else size) or None,
"servers": int(servers or 0),
"volumes": int(volumes or 0),
"networks": int(networks or 0),
"ports": int(ports or 0),
"images": int(images or 0),
"hypervisors": int(hypervisors or 0),
"projects": int(projects or 0),
"users": int(users or 0),
"loadbalancers": int(lbs or 0),
"stacks": int(stacks or 0),
"ironic_nodes": int(nodes or 0),
"floating_ips": int(fips or 0),
"target_servers": cfg.servers if cfg else int(servers or 0),
"sizes": list_demo_sizes(),
}