"""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(), }