"""Seed OpenStack identity and sample cloud resources.""" from __future__ import annotations from asyncpg import Connection from app.openstack.ids import oid from app.security.auth import hash_secret async def seed_openstack(conn: Connection, *, password: str = "secret") -> dict[str, object]: """Idempotent OpenStack lab seed (admin + demo project/user + sample resources).""" domain_id = oid("domain:Default") admin_project = oid("project:admin") demo_project = oid("project:demo") admin_user = oid("user:admin") demo_user = oid("user:demo") role_admin = oid("role:admin") role_member = oid("role:member") pw = hash_secret(password, salt=b"openstack-sim-v1") await conn.execute( """INSERT INTO os_domains(id, name, description, enabled) VALUES($1, 'Default', 'Default domain', true) ON CONFLICT (id) DO NOTHING""", domain_id, ) await conn.execute( """INSERT INTO os_projects(id, domain_id, name, description, enabled) VALUES ($1, $3, 'admin', 'Admin project', true), ($2, $3, 'demo', 'Demo project', true) ON CONFLICT (id) DO NOTHING""", admin_project, demo_project, domain_id, ) await conn.execute( """INSERT INTO os_users(id, domain_id, name, password_hash, enabled) VALUES ($1, $3, 'admin', $4, true), ($2, $3, 'demo', $4, true) ON CONFLICT (id) DO NOTHING""", admin_user, demo_user, domain_id, pw, ) await conn.execute( """INSERT INTO os_roles(id, name) VALUES ($1, 'admin'), ($2, 'member') ON CONFLICT (id) DO NOTHING""", role_admin, role_member, ) await conn.execute( """INSERT INTO os_role_assignments(id, role_id, user_id, project_id) VALUES ($1, $3, $5, $7), ($2, $4, $6, $8) ON CONFLICT (role_id, user_id, project_id) DO NOTHING""", oid("assign:admin-admin"), oid("assign:demo-member"), role_admin, role_member, admin_user, demo_user, admin_project, demo_project, ) # admin also admin on demo for convenience 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("assign:admin-demo-admin"), role_admin, admin_user, demo_project, ) 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), ] 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) ON CONFLICT (id) DO NOTHING""", fid, name, vcpus, ram, disk, ) cirros = oid("image:cirros") ubuntu = oid("image:ubuntu") await conn.execute( """INSERT INTO os_images(id, name, status, visibility, size, disk_format, container_format, owner_project_id) VALUES ($1, 'cirros', 'active', 'public', 13287936, 'qcow2', 'bare', $3), ($2, 'ubuntu-22.04', 'active', 'public', 400000000, 'qcow2', 'bare', $3) ON CONFLICT (id) DO NOTHING""", cirros, ubuntu, admin_project, ) demo_net = oid("net:demo-net") await conn.execute( """INSERT INTO os_networks(id, project_id, name, status, shared, admin_state_up) VALUES($1, $2, 'demo-net', 'ACTIVE', false, true) ON CONFLICT (id) DO NOTHING""", demo_net, demo_project, ) demo_subnet = oid("subnet:demo-subnet") await conn.execute( """INSERT INTO os_subnets(id, network_id, project_id, name, cidr, ip_version, gateway_ip) VALUES($1, $2, $3, 'demo-subnet', '10.0.0.0/24', 4, '10.0.0.1') ON CONFLICT (id) DO NOTHING""", demo_subnet, demo_net, demo_project, ) vol = oid("volume:demo-vol") await conn.execute( """INSERT INTO os_volumes(id, project_id, name, status, size, volume_type, bootable) VALUES($1, $2, 'demo-volume', 'available', 10, 'lvmdriver-1', false) ON CONFLICT (id) DO NOTHING""", vol, demo_project, ) server = oid("server:demo-1") await conn.execute( """INSERT INTO os_servers(id, project_id, user_id, name, status, flavor_id, image_id, addresses, metadata) VALUES($1, $2, $3, 'demo-instance', 'ACTIVE', '2', $4, $5::jsonb, $6::jsonb) ON CONFLICT (id) DO NOTHING""", server, demo_project, demo_user, cirros, '{"demo-net":[{"OS-EXT-IPS-MAC:mac_addr":"fa:16:3e:00:00:01","version":4,"addr":"10.0.0.12","OS-EXT-IPS:type":"fixed"}]}', '{"env":"lab","_tags":["lab","env","demo"]}', ) await seed_openstack_extras(conn) from app.openstack.pack_seed import seed_pack_surface_samples from app.openstack.seed_discovery import seed_discovery_documents await seed_discovery_documents(conn) await seed_pack_surface_samples(conn, per_type=3) # Minimal topology tables (011+) — ignore if migration not applied yet. try: await conn.execute( """INSERT INTO os_availability_zones(name, zone_state) VALUES('nova', '{"available": true}'::jsonb) ON CONFLICT (name) DO NOTHING""" ) 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,'compute-1','up','enabled','10.20.0.10',64,1,262144,2048,2000,20,1,'compute-1','nova') ON CONFLICT (id) DO NOTHING""" ) await conn.execute( """INSERT INTO os_demo_meta(key, value) VALUES('profile','minimal') ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value, updated_at=now()""" ) except Exception: pass return { "domain": "Default", "users": ["admin", "demo"], "password": password, "projects": ["admin", "demo"], "sample_server": "demo-instance", "profile": "minimal", } async def seed_openstack_extras(conn: Connection) -> None: """Seed routers, SG, ironic nodes, LB, heat stack, swift objects, generic services.""" import json from uuid import uuid4 demo_project = oid("project:demo") admin_project = oid("project:admin") demo_user = oid("user:demo") # default security group sg = oid("sg:demo-default") await conn.execute( """INSERT INTO os_security_groups(id, project_id, name, description) VALUES($1,$2,'default','Default security group') ON CONFLICT (id) DO NOTHING""", sg, demo_project, ) for direction, proto, pmin, pmax, prefix in ( ("egress", None, None, None, None), ("ingress", "tcp", 22, 22, "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) ON CONFLICT (id) DO NOTHING""", oid(f"sgrule:{direction}:{proto}:{pmin}"), sg, demo_project, direction, proto, pmin, pmax, prefix, ) await conn.execute( """INSERT INTO os_routers(id, project_id, name, status, admin_state_up, external_gateway_info) VALUES($1,$2,'demo-router','ACTIVE',true,NULL) ON CONFLICT (id) DO NOTHING""", oid("router:demo"), demo_project, ) await conn.execute( """INSERT INTO os_nodes(id, name, driver, provision_state, power_state, resource_class, properties, driver_info, ports) VALUES($1,'baremetal-1','ipmi','available','power off','baremetal', '{"cpus":64,"memory_mb":262144,"local_gb":2000}'::jsonb,'{}'::jsonb,'[]'::jsonb) ON CONFLICT (id) DO NOTHING""", oid("node:baremetal-1"), ) await conn.execute( """INSERT INTO os_loadbalancers(id, project_id, name, description, vip_address, provisioning_status, operating_status) VALUES($1,$2,'demo-lb','Seed LB','10.0.0.50','ACTIVE','ONLINE') ON CONFLICT (id) DO NOTHING""", oid("lb:demo"), demo_project, ) await conn.execute( """INSERT INTO os_stacks(id, project_id, stack_name, stack_status, description, template, parameters, outputs) VALUES($1,$2,'demo-stack','CREATE_COMPLETE','Seed stack','{"heat_template_version":"2015-04-30"}'::jsonb,'{}'::jsonb,'[]'::jsonb) ON CONFLICT (id) DO NOTHING""", oid("stack:demo"), demo_project, ) for project in (demo_project, admin_project): account = f"AUTH_{project}" await conn.execute( """INSERT INTO os_swift_containers(account, name, meta) VALUES($1,'images','{}'::jsonb) ON CONFLICT DO NOTHING""", account, ) await conn.execute( """INSERT INTO os_swift_objects(id, account, container, name, content_type, bytes, body, meta) VALUES($1,$2,'images','readme.txt','text/plain',12,$3,'{}'::jsonb) ON CONFLICT (account, container, name) DO NOTHING""", oid(f"swift:readme:{account}"), account, b"hello swift\n", ) for user_id, key_name in ((demo_user, "demo-key"), (oid("user:admin"), "admin-key")): await conn.execute( """INSERT INTO os_keypairs(name, user_id, fingerprint, public_key, type) VALUES($1,$2,$3,$4,'ssh') ON CONFLICT DO NOTHING""", key_name, user_id, f"https://example.invalid/{key_name}", f"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC {key_name}@lab", ) # Sample objects for every remaining service collection samples = [ ( "barbican", "secret", "demo-secret", {"payload_content_type": "text/plain", "secret_type": "passphrase"}, ), ( "manila", "share", "demo-share", {"size": 10, "share_proto": "NFS", "status": "available"}, ), ( "designate", "zone", "example.lab.", {"email": "hostmaster@example.lab", "ttl": 3600, "type": "PRIMARY"}, ), ( "magnum", "cluster", "demo-k8s", {"coe": "kubernetes", "status": "CREATE_COMPLETE", "node_count": 2}, ), ("zun", "container", "demo-ctr", {"image": "cirros", "status": "Running"}), ( "trove", "instance", "demo-db", {"datastore": {"type": "mysql", "version": "8.0"}, "status": "ACTIVE"}, ), ( "mistral", "workflow", "demo-wf", {"input": {}, "definition": "version: '2.0'\ndemo_wf:\n tasks: {}"}, ), ( "aodh", "alarm", "cpu-high", {"type": "threshold", "state": "ok", "severity": "avg(cpu)>80"}, ), ( "freezer", "job", "daily-backup", {"description": "lab backup job", "status": "scheduled"}, ), ( "blazar", "lease", "demo-lease", {"start_date": "2026-01-01T00:00:00", "end_date": "2026-12-31T00:00:00"}, ), ("vitrage", "alarm", "host-down", {"type": "host", "state": "critical"}), ( "masakari", "segment", "az-segment", {"recovery_method": "auto", "service_type": "compute"}, ), ("tacker", "vnf", "demo-vnf", {"status": "ACTIVE", "vnfd_id": "vnfd-1"}), ("adjutant", "task", "invite-user", {"task_type": "create_user", "status": "open"}), ("adjutant", "token", "adj-token-demo", {"status": "active"}), ("adjutant", "notification", "adj-notif-demo", {"status": "sent"}), ( "adjutant", "status", "adj-status-demo", {"status": "UP", "service": "adjutant", "state": "up"}, ), ("cloudkitty", "hashmap_service", "compute", {"name": "compute"}), ( "heat-cfn", "stack", "demo-cfn", {"StackName": "demo-cfn", "StackStatus": "CREATE_COMPLETE"}, ), ("watcher", "audit", "demo-audit", {"state": "SUCCEEDED"}), ("zaqar", "queue", "demo-queue", {"_default_message_ttl": 3600}), ( "masakari", "host", "compute-1", {"name": "compute-1", "type": "compute", "reserved": False}, ), ("designate", "recordset", "www", {"type": "A", "records": ["203.0.113.10"], "ttl": 3600}), # Extra types that pack lists expose and previously relied on lazy fixtures. ("barbican", "container", "demo-container", {"type": "generic", "status": "ACTIVE"}), ("barbican", "order", "demo-order", {"type": "key", "status": "ACTIVE"}), ("barbican", "secret_store", "demo-store", {"status": "ACTIVE"}), ("manila", "share_type", "default", {"is_public": True}), ("manila", "share_network", "demo-share-net", {"status": "active"}), ("manila", "share_snapshot", "demo-share-snap", {"status": "available", "size": 10}), ("manila", "share_server", "demo-share-srv", {"status": "active"}), ("manila", "security_service", "demo-sec-svc", {"type": "ldap", "status": "new"}), ("manila", "share_group", "demo-share-grp", {"status": "available"}), ("manila", "share_replica", "demo-share-rep", {"status": "available"}), ("designate", "tld", "lab", {"name": "lab"}), ("designate", "blacklist", "bad-pattern", {"pattern": "^bad\\..*"}), ("designate", "pool", "default", {"name": "default"}), ("designate", "service_status", "dns-central", {"status": "UP"}), ("magnum", "clustertemplate", "k8s-default", {"coe": "kubernetes", "image_id": "cirros"}), ("magnum", "certificate", "demo-cert", {"cluster_uuid": "demo-k8s"}), ("zun", "capsule", "demo-capsule", {"status": "Running", "cpu": 1, "memory": 512}), ("zun", "host", "zun-compute-1", {"hostname": "zun-compute-1", "state": "up"}), ("zun", "image", "nginx", {"image": "nginx", "status": "ACTIVE"}), ( "zun", "service", "zun-compute", {"host": "zun-1", "binary": "zun-compute", "state": "up"}, ), ("trove", "backup", "demo-db-bak", {"status": "COMPLETED", "size": 1.5}), ("trove", "cluster", "demo-db-cl", {"instance_count": 3}), ("trove", "configuration", "demo-db-cfg", {"datastore_name": "mysql"}), ("trove", "datastore", "mysql", {"name": "mysql", "version": "8.0"}), ("mistral", "action", "demo-action", {"is_system": False}), ("mistral", "cron_trigger", "hourly", {"pattern": "0 * * * *"}), ("mistral", "execution", "demo-exec", {"state": "SUCCESS"}), ("mistral", "task", "demo-task", {"state": "SUCCESS"}), ("mistral", "workbook", "demo-wb", {"definition": "version: '2.0'"}), ("aodh", "quota", "aodh-default", {"alarm": 100}), ("freezer", "action", "demo-freezer-action", {"status": "available"}), ("freezer", "backup", "demo-freezer-bak", {"status": "available"}), ("freezer", "client", "demo-freezer-client", {"status": "available"}), ("freezer", "session", "demo-freezer-session", {"status": "scheduled"}), ("blazar", "floatingip", "blazar-fip", {"floating_ip_address": "198.51.100.10"}), ("blazar", "host", "blazar-host-1", {"status": "available"}), ("vitrage", "event", "host-down-evt", {"type": "compute.host.down"}), ("vitrage", "resource", "vit-server", {"type": "nova.instance", "state": "ACTIVE"}), ("vitrage", "template", "vit-tmpl", {"type": "standard", "status": "active"}), ("vitrage", "topology", "vit-topo", {"nodes": [], "links": []}), ("masakari", "notification", "demo-notif", {"status": "finished"}), ("tacker", "vim", "demo-vim", {"type": "openstack", "status": "REACHABLE"}), ("tacker", "vnf_instance", "demo-vnf-inst", {"instantiationState": "INSTANTIATED"}), ("tacker", "vnf_package", "demo-vnf-pkg", {"onboardingState": "ONBOARDED"}), ("tacker", "vnfd", "demo-vnfd", {"name": "demo-vnfd"}), ("cloudkitty", "dataframes", "df-0", {"period": "3600"}), ("cloudkitty", "hashmap_field", "field-0", {"name": "field-0"}), ("cloudkitty", "report_summary", "summary-0", {"tenant_id": "demo"}), ("watcher", "action", "w-action-0", {"state": "SUCCEEDED"}), ("watcher", "action_plan", "ap-0", {"state": "SUCCEEDED"}), ("watcher", "audit_template", "at-0", {"goal": "server_consolidation"}), ("watcher", "goal", "goal-0", {"display_name": "Goal 0"}), ("watcher", "scoring_engine", "se-0", {"description": "engine 0"}), ("watcher", "service", "wsvc-0", {"host": "watcher-0", "status": "ACTIVE"}), ("watcher", "strategy", "strategy-0", {"goal_uuid": "goal-0"}), ("ironic", "driver", "ipmi", {"name": "ipmi", "hosts": ["simulator"], "type": "classic"}), ( "ironic", "driver", "redfish", {"name": "redfish", "hosts": ["simulator"], "type": "classic"}, ), ( "neutron", "agent", "l3-agent", {"agent_type": "L3 agent", "host": "network-1", "alive": True, "admin_state_up": True}, ), ( "neutron", "agent", "ovs-agent", { "agent_type": "Open vSwitch agent", "host": "compute-1", "alive": True, "admin_state_up": True, }, ), ( "nova", "console_output", "default-console", {"output": "Booting...\nSimulator console\n"}, ), ( "nova", "console", "default-vnc", {"type": "novnc", "url": "https://127.0.0.1:6080/vnc_auto.html?token=simulator"}, ), ( "nova", "migration", "demo-mig", { "status": "completed", "migration_type": "migration", "source_compute": "compute-1", "dest_compute": "compute-2", "instance_uuid": str(oid("server:demo-1")), }, ), ( "nova", "server_topology", "demo-topo", { "server_id": str(oid("server:demo-1")), "nodes": [ { "vcpu_set": [0], "siblings": [[0]], "host_node": 0, "memory_mb": 2048, "cpu_pinning": {}, } ], "pagesize_kb": 4, "host": "compute-1", }, ), ( "nova", "server_password", "demo-password", {"server_id": str(oid("server:demo-1")), "password": ""}, ), ( "placement", "resource_provider", "rp-0", {"name": "compute-1", "generation": 1}, ), ( "placement", "allocation", "alloc-demo", { "consumer_uuid": str(oid("server:demo-1")), "resource_provider": str(oid("placement:resource_provider:rp-0")), "resource_provider_id": str(oid("placement:resource_provider:rp-0")), "resources": {"VCPU": 1, "MEMORY_MB": 2048, "DISK_GB": 20}, "consumer_generation": 1, }, ), ( "placement", "inventory", "inv-demo", { "resource_provider": str(oid("placement:resource_provider:rp-0")), "resource_provider_id": str(oid("placement:resource_provider:rp-0")), "resource_class": "VCPU", "total": 64, "reserved": 0, }, ), ( "placement", "aggregate", "agg-demo", { "name": "agg-demo", "resource_provider": str(oid("placement:resource_provider:rp-0")), "resource_provider_id": str(oid("placement:resource_provider:rp-0")), }, ), ( "nova", "console_auth_token", "demo-cat", { "token": "demo-console-token", "console_type": "novnc", "host": "127.0.0.1", "port": 6080, "internal_access_path": None, }, ), ] 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) ON CONFLICT (id) DO NOTHING""", item_id, service, rtype, None, # visible to any project-scoped token name, payload["status"], json.dumps(payload), )