UMKIM integration
Setup checklist and inventory schema: Operator guide.
Status: v0.2 experimental. YAIDAP is a sibling metal/VM provisioner (TempXE, libvirt guests, hypervisor prepare). UMKIM does not vendor YAIDAP; it binds a site jumphost and runs YAIDAP through hub connector jobs. For FleetSite API surface and cluster lifecycle after SSH-ready, see fleet-site.
Layering
Section titled “Layering”┌─────────────────────────────────────────────────────────────────────────┐│ UMKIM hub (L1: OS → fleet) ││ FleetSite.spec.provision · hub YAML inventory · provision API · console ││ FleetClusterJoin enroll / add-worker / Ready │└───────────────────────────────┬─────────────────────────────────────────┘ │ async Kubernetes Job (fleet-ansible-runner) │ SSH → site jumphost ▼┌─────────────────────────────────────────────────────────────────────────┐│ Site jumphost ││ YAIDAP checkout (ansible-playbook) · inventory path on disk │└───────────────────────────────┬─────────────────────────────────────────┘ │ ansible-playbook --limit SUBJECT ▼┌─────────────────────────────────────────────────────────────────────────┐│ YAIDAP (L0: iron → OS) ││ TempXE / PXE · libvirt · hypervisor discovery · guest host_vars │└─────────────────────────────────────────────────────────────────────────┘Contract: UMKIM owns intent and inventory on the hub. YAIDAP owns execution
semantics (kickstart, bridges, disks, HV XML). The connector translates hub
actions into ansible-playbook invocations on the jumphost and parses a minimal
handoff record (ansible_host, hostname, OS family) for enroll / add-worker.
Default provider is noop: operators supply SSH-ready hosts and skip YAIDAP
entirely.
Site binding
Section titled “Site binding”Configure on FleetSite.spec.provision (API: GET/PUT/PATCH /sites/{name}/provision).
| Field | Required (yaidap) | Meaning |
|---|---|---|
provider |
yes | yaidap |
jumphost |
yes | SSH target for connector jobs (user@host or user@host:port) |
sshSecretRef |
yes | Hub Secret in fleet-console namespace; private key for jumphost |
inventoryPath |
yes | Absolute path on the jumphost to this site’s YAIDAP inventory root |
yaidapRoot |
no | YAIDAP git checkout on jumphost (default ~/yaidap) |
yaidapRepo |
no* | Git remote for hub-managed YAIDAP sync (https://… or git@…) |
yaidapRef |
no | Branch, tag, or commit to checkout (default main) |
yaidapGitSecretRef |
no** | Hub Secret with deploy key (id_ed25519, deploy_key, or id_rsa) |
defaultExtraVars |
no | Safe non-secret -e pairs passed to every connector run |
*Required for POST …/provision/yaidap/sync.
**Required when yaidapRepo uses SSH (git@…); omit for public HTTPS remotes.
Secret: create before binding, e.g. fleet-site-{site}-provision-ssh with
id_ed25519 / id_ed25519.pub (or id_rsa / id_rsa.pub). The ansible-runner
Job mounts this under /root/.ssh. Jumphost must accept that key.
CLI binding:
umkim site provision set SITE --provider yaidap \ --jumphost USER@JUMPHOST \ --ssh-secret fleet-site-SITE-provision-ssh \ --inventory /path/on/jumphost/to/inventory \ --yaidap-root /path/on/jumphost/to/yaidapJumphost prep (operator):
- Bind
yaidapRepo,yaidapRoot, and optionalyaidapGitSecretRef, then run hub YAIDAP sync (POST /sites/{site}/provision/yaidap/syncorumkim site provision yaidap-sync SITE) to clone or update the checkout on the jumphost. For private repos, create a deploy-key Secret infleet-console(read-only repo access) and reference it inyaidapGitSecretRef; the key is mounted only on the connector Job pod and staged on the jumphost for the duration of the sync. - Maintain a per-site inventory tree at
inventoryPath(see layout below). - Ensure SSH from jumphost to hypervisors, guests, and BM targets on the site management LAN.
- For libvirt VM create: base OS images and
group_vars/all/all.ymlsite catalogs (networks,storages, …) must match what YAIDAP expects.
The published umkim tree does not include examples/homelab/. Lab binding and inventory steps are on this page and in playbooks/flows/site-provision-yaidap.yml.
Hub inventory store
Section titled “Hub inventory store”YAIDAP sites keep native Ansible YAML on the hub in ConfigMap
fleet-site-{site}-yaidap-inventory (file.* keys). Hub-only metadata lives
under _meta/ and is never pushed to the jumphost.
| Path | Synced to jumphost | Role |
|---|---|---|
guests.yml |
yes (hub: VM index only) | guests.hosts; jumphost copy also has external.hosts composed from hub bare_metals.yml on sync |
bare_metals.yml |
hub only | Bare-metal cluster nodes (kind: bare_metal in host APIs) |
hypervisors.yml |
yes | Hypervisor group (kind: hypervisor in host APIs) |
{cluster}.yml |
yes | Ansible group membership (hosts: list) |
group_vars/all/all.yml (+ optional group_vars/all/*.yml) |
yes | Site networks, storages, defaults |
group_vars/{group}.yml |
yes | Group vars; Fleet cluster intent for cluster groups |
host_vars/{guest}.yml |
yes | Per-guest YAIDAP profile (disks, uplinks, TempXE discovery) |
_meta/meta.json |
no | Sync phase, revision head, last sync/import timestamps |
_meta/status.json |
no | Per-guest provision + cluster-apply phase |
_meta/revisions.json |
no | Timestamped edit log |
_meta/import_preview.json |
no | Staging for jumphost import diff |
Jumphost-only (preserved on sync; not stored on hub): controllers.yml,
routers.yml, generated/ (discovery drafts, PXE tarballs).
Sync phases (_meta/meta.json → syncPhase):
| Phase | Meaning |
|---|---|
pending |
Never synced or freshly migrated |
dirty |
Hub edits not yet on jumphost |
syncing |
Push job running |
synced |
Jumphost tree matches last successful push |
POST /sites/{name}/provision/inventory/sync (or console Sync to jumphost)
builds a bundle from synced paths only, base64-encodes it, and runs
site-provision-connector.yml with fleet_provision_action=sync_inventory.
Success is detected via log marker FLEET_INVENTORY_SYNCED=1.
Provision actions that mutate YAIDAP input pre-sync the same bundle
(fleet_provision_sync_from_store=true) before running the YAIDAP playbook.
Run POST …/provision/inventory/migrate once to seed the native skeleton from
legacy JSON (catalog.json / guests.json) or deprecated clusters.yml.
Inventory editing
Section titled “Inventory editing”Three surfaces share one store:
| Surface | Commands / UI |
|---|---|
| File API | GET/PUT/DELETE …/provision/inventory/files/{path} |
| CLI | umkim site inventory files {list,get,put,delete} |
| Console | Sites → detail → Provisioner inventory |
VM guest bootstrap (console): Add VM guest writes guests.yml +
host_vars/{name}.yml from templates (inventoryVmTemplates.ts). Defaults:
vm_location: least-utilized, libvirt profile, lab-style uplink placeholders.
Hypervisor (console): Add hypervisor writes hypervisors.yml +
host_vars/{name}.yml with optional tempxe_discovery.enabled and uplink MAC.
Bare-metal node (console): Add bare metal node writes bare_metals.yml +
host_vars/{name}.yml (optional tempxe_discovery.enabled and uplink MAC for PXE discover).
Host kinds on GET /sites/{name}/hosts: vm (guests.hosts), bare_metal
(bare_metals.yml), hypervisor (hypervisors.yml). On jumphost sync, hub
bare_metals.yml is merged into YAIDAP guests.yml → external.hosts (not
stored as a separate file on the jumphost).
Cluster automation uses native Ansible layout:
- Root
{cluster}.yml— group withhosts:listing member guests. group_vars/{cluster}.yml—topology,autoApply,controlPlaneVip,haGroup,mode(enroll|add_worker|replace_control), etc.
Members are defined only in the root group file, not duplicated in group_vars.
Placeholder safety: discovery drafts and stubs often contain CHANGE_ME.
Import preview and the console editor surface warnings per path. Resolve
placeholders before sync or provision; YAIDAP may fail or produce wrong networks.
Provision actions
Section titled “Provision actions”POST /sites/{name}/provision/actions body:
{ "action": "create_vm", "subject": "guest01", "confirm": false, "params": { "tags": ["create"], "playbook": "playbooks/provision-vms.yml" }}params.tags and params.playbook override defaults. Destructive actions
require confirm: true.
Action → YAIDAP mapping
Section titled “Action → YAIDAP mapping”Implemented in controllers/fleet-site/internal/provision/yaidap.go and
executed by site-provision-connector.yml
on the jumphost (cd $yaidapRoot && ansible-playbook …).
| Action | Confirm | Pre-sync inventory | Default YAIDAP invocation |
|---|---|---|---|
discover |
no | yes | playbooks/discover-hosts.yml · tags check_host_details · -e yaidap_emit_result=true |
discover_pxe |
yes | yes | playbooks/discover-pxe.yml · tag discover_pxe · --connection local · -e tempxe_confirm=true |
install_bm |
yes | yes | playbooks/install-bare-metal.yml · tag install_bm · SSH to target for reboot/wait; TempXE arm uses per-play connection: local · -e tempxe_confirm=true · -e install_os=true |
create_vm |
no* | yes | playbooks/provision-vms.yml · tags create, never · -e yaidap_emit_result=true · -e yaidap_recreate=true when guest has concrete vm_location |
plan_vm |
no | yes | playbooks/plan-vm-placement.yml · tag plan (dry-run; no VM, no inventory rewrite) |
destroy |
yes | yes | playbooks/provision-vms.yml · tag destroy · -e confirm_destroy=true |
status |
no | no | playbooks/configure-tempxe.yml · tag tempxe_status |
*Re-provision on an already-Ready VM guest with a concrete vm_location
sets yaidap_recreate=true. The console requires a confirm checkbox for this
case; the API does not enforce confirm on create_vm today.
install_os in site inventory: commit install_os: false (or omit; role
default is false). Do not set install_os: true in host_vars for normal hosts.
The hub connector adds -e install_os=true only for install_bm.
discover_pxe forces install_os off in YAIDAP and arms the discovery menu via
tempxe_discovery.enabled.
Validation highlights:
discover: subject in inventory; reachableansible_host.discover_pxe: subject inhypervisors.ymlorbare_metals.yml;host_varswithtempxe_discovery.enabled: trueand uplinkmac_address.plan_vm: existing guest inguests.yml(not external-only).create_vm/plan_vm:ansible_hostrequired inhost_varsor sync extra vars (static management IP for cloud-init).destroy: VM guest inguests.yml; guest phase Ready or Failed;confirm=true; blocked when the guest is a cluster member and a liveFleetClusterJoinexists (unless the join is deleting). On success the hub sets guest phase Declared (not Ready), clears cluster sub-status, and keeps inventory for reprovision.
YAIDAP integration contract (YAIDAP_RESULT)
Section titled “YAIDAP integration contract (YAIDAP_RESULT)”YAIDAP v1 integration playbooks emit a single JSON line when the hub passes
-e yaidap_emit_result=true (set automatically for provision actions). The
fleet-site controller parses the last YAIDAP_RESULT= line from connector job
logs (schema: yaidap.integration.v1).
YAIDAP_RESULT={"schema":"yaidap.integration.v1","action":"create_vm","subject":"vm01","phase":"ready","ok":true,"message":"VM created","vm_location":"virt02","handoff":{"hostname":"vm01","ansible_host":"172.16.10.21","ansible_user":"linux","os_family":"rocky-10"},...}| Field | Hub use |
|---|---|
message |
Provision action history message |
handoff |
ProvisionHandoffHost for enroll / add-worker |
vm_location |
Persist concrete hypervisor after create_vm |
placement |
plan_vm assignment summary |
generated_paths |
Discovery import hints (optional) |
Upstream contract reference: YAIDAP integrations doc (sibling repo).
Connector workflow (site-provision-connector.yml)
Section titled “Connector workflow (site-provision-connector.yml)”The hub connector syncs inventory to the jumphost, then runs one YAIDAP playbook
per action. Composite TempXE / PXE flows live in YAIDAP (install-bare-metal.yml,
discover-pxe.yml); the connector no longer re-implements them.
create_vm
- Pre-sync hub bundle to jumphost.
- When the guest already has a concrete
vm_location, pass-e yaidap_recreate=true(YAIDAP destroys the stale libvirt domain before create). - Ensure libvirt guest profile in
host_varswhen using legacy per-field sync. - Run YAIDAP create with
-e yaidap_emit_result=true.
After successful create_vm, the fleet-site controller:
- Parses
vm_locationfromYAIDAP_RESULT(falls back to legacyFLEET_YAIDAP_VM_LOCATION=log marker when absent). - Rewrites
guests.ymlandhost_vars/{guest}.ymlfrom schedule tokens (least-utilized,auto, empty) to the concrete hypervisor. - Marks inventory
dirtyand enqueues inventory sync so the jumphost matches.
Schedule tokens are rejected for persistence (PersistResolvedVmLocation).
Action phases and handoff
Section titled “Action phases and handoff”| Phase | Meaning |
|---|---|
Pending |
Accepted; Job not yet observed |
Running |
Connector Job active |
Succeeded |
YAIDAP finished; host fragment populated when known |
Failed |
Job failed or missing; logTail on history entries |
Handoff record (ProvisionHandoffHost):
hostname: guest01ansible_host: 203.0.113.10ansible_user: linuxosFamily: rockysiteId: SITEUsed by enroll / add-worker flows. VM guests created through the connector use
minRootDiskGi: 38 on FleetClusterJoin (40G vdisk root LV < 40 GiB after
EFI/boot partitions).
Guest provision status is mirrored in _meta/status.json per guest:
Declared → Provisioning → Ready | Failed.
End-to-end flows
Section titled “End-to-end flows”A. Libvirt VM → cluster enroll
Section titled “A. Libvirt VM → cluster enroll”1. Bind site (yaidap + jumphost + inventoryPath + secret)2. Edit inventory (guests.yml, host_vars, group_vars/all, cluster files) OR console: Add VM guest → edit YAML → Save3. POST …/inventory/sync (or auto before provision)4. POST …/hosts/{guest}/provision action=create_vm5. Hub rewrites vm_location + syncs inventory6. Cluster-apply reconciler (or POST …/hosts/{guest}/cluster) when guests Ready7. cluster create → bootstrap → provision → ReadyConsole: Provisioner inventory → Site hosts → Provision → Apply cluster.
B. Bare metal hypervisor → single-node cluster
Section titled “B. Bare metal hypervisor → single-node cluster”1. bare_metals.yml + host_vars stub (discovery stub or hand-authored)2. sync3. discover OR discover_pxe (confirm for PXE)4. import preview → import apply (promote generated/ → host_vars, bare_metals.yml)5. install_bm (confirm) — full OS reinstall via TempXE6. cluster create on BM host (may be external to guests.yml)7. bootstrap / provision as usualBare-metal cluster nodes live under hub bare_metals.yml (kind: bare_metal).
Hypervisors live under hypervisors.yml (kind: hypervisor). Libvirt guests
live under guests.hosts (kind: vm). Do not put external.hosts in hub
guests.yml; the controller composes that section for the jumphost on sync.
C. Import from jumphost
Section titled “C. Import from jumphost”POST …/inventory/import/preview → Job reads jumphost tree + generated/GET …/inventory/import/preview → per-path diff, warningsPOST …/inventory/import/apply → merge selected paths into hub storeDiscovery promotion maps generated/host_vars/ → host_vars/,
generated/hosts/ → merged hypervisors.yml. *.notes.* files stay on jumphost.
D. Cluster intent automation
Section titled “D. Cluster intent automation”When group_vars/{cluster}.yml has autoApply: true and all member guests reach
Ready, the registry-site reconciler creates FleetClusterJoin from root +
group_vars cluster files. Manual retry: POST …/hosts/{guest}/cluster.
Status fields under each guest’s cluster key in _meta/status.json mirror apply
progress (Pending, WaitingGroup, Applying, Ready, Failed).
Job execution
Section titled “Job execution”fleet-site HTTP API → startYaidapProvisionJob() → Batch Job fleet-site-prov-{actionId} → Pod: fleet-ansible-runner image → ansible-playbook site-provision-connector.yml -l jumphost → SSH session on jumphost runs YAIDAP ansible-playbook- Jumphost ansible inventory: ConfigMap
fleet-site-{site}-provision-inventory([jumphost]group). - One running provision action per site (
409if busy). - History: last 20 actions on
FleetSite.status.provision; log tail up to 64 KiB. - Flow playbook for operators/automation: playbooks/flows/site-provision-yaidap.yml.
Inventory-only jobs use prefix fleet-site-inv- with the same connector playbook
and fleet_provision_action=sync_inventory or import_inventory_preview.
API quick reference
Section titled “API quick reference”| Method | Path | Purpose |
|---|---|---|
GET/PUT/PATCH |
/sites/{name}/provision |
Connector binding |
POST |
/sites/{name}/provision/yaidap/sync |
Clone/update YAIDAP checkout on jumphost |
POST |
/sites/{name}/provision/actions |
Start action |
GET |
/sites/{name}/provision/actions[/{id}] |
Poll / history |
GET |
/sites/{name}/provision/inventory |
Meta + file index |
GET/PUT/DELETE |
/sites/{name}/provision/inventory/files/{path} |
YAML CRUD |
POST |
/sites/{name}/provision/inventory/sync |
Push to jumphost |
POST |
/sites/{name}/provision/inventory/migrate |
Legacy → native YAML |
POST/GET |
/sites/{name}/provision/inventory/import/preview |
Jumphost pull + diff |
POST |
/sites/{name}/provision/inventory/import/apply |
Merge selected paths |
GET |
/sites/{name}/provision/inventory/hosts |
Derived host rows |
GET |
/sites/{name}/provision/inventory/clusters |
Cluster intent aggregate |
POST |
/sites/{name}/hosts/{host}/provision |
Provision by guest name |
GET/POST |
/sites/{name}/hosts/{host}/cluster |
Cluster apply status / manual trigger |
Deprecated: POST/PATCH/DELETE /sites/{name}/hosts/{host} — use file API.
OpenAPI: controllers/fleet-site/openapi.yaml.
CLI quick reference
Section titled “CLI quick reference”See umkim CLI for full syntax.
# Bindingumkim site provision get|set SITE …
# Inventoryumkim site inventory files list|get|put|delete SITE …umkim site inventory migrate|sync|revisions SITE …umkim site inventory import preview|apply SITE …
# Actionsumkim site provision run SITE --action ACTION --subject HOST [--confirm] [--wait]umkim site host list|get|provision|cluster-apply SITE HOST …Console
Section titled “Console”| Location | Function |
|---|---|
| Sites → detail → Provisioner inventory | YAML tree editor, sync, import preview, Add VM guest, Add discovery stub, revisions |
| Sites → detail → Site hosts | Derived host table, Provision / Plan / Discover, Apply cluster, provision history |
| Sites → detail → Edge and DNS | Site jumphost (separate from provision binding SSH target when both set) |
Provisioner panels are relevant when spec.provision.provider=yaidap. Site
jumphost for cluster bootstrap SSH may match the YAIDAP jumphost but is
configured independently (FleetSite.spec.jumphost).
Safety and exclusions
Section titled “Safety and exclusions”| Rule | Detail |
|---|---|
| Confirm gate | install_bm, discover_pxe, destroy require confirm=true |
| TempXE | Never arm PXE without confirm; connector always runs tempxe_stop after BM install / PXE discovery |
| Mutual exclusion | One site provision action at a time; inventory sync blocked while sync job runs |
| Hub schema boundary | Do not import YAIDAP networks / uplinks / storages / disk catalogs into Fleet CRDs — they stay in group_vars / host_vars |
| YAIDAP scope | Kubernetes, OCM, Harbor, and FleetApp are out of scope for YAIDAP; UMKIM takes over at SSH-ready |
| Secrets | Jumphost keys only via sshSecretRef; no passwords in defaultExtraVars |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Check |
|---|---|---|
guests.yml missing |
Empty hub store | Migrate; add guests.yml via file API |
confirm=true is required |
Destructive action | Pass --confirm / UI checkbox |
ansible_host is required for create_vm |
Missing static IP | Set in guests.yml + host_vars before provision |
discover_pxe requires tempxe_discovery.enabled |
Stub incomplete | Edit host_vars; enable PXE discovery + MAC |
Import warnings CHANGE_ME |
Unresolved discovery draft | Edit YAML; re-sync |
site provision action already running |
Overlapping jobs | Wait or inspect Job fleet-site-prov-* |
Placement still least-utilized after create |
Job log missing result | Inspect YAIDAP logs; verify YAIDAP_RESULT includes concrete vm_location |
Sync stuck syncing |
Failed push job | Job logs; jumphost disk permissions on inventoryPath |
| BM install timeout | PXE / kickstart | TempXE status on jumphost; target power; firewall |
| Cluster Failed but spoke joined | Hub provision Job or finalize error | kubectl get fleetclusterjoin; Job fleet-cluster-provision-*; retry umkim cluster provision |
Implementation map
Section titled “Implementation map”| Component | Path |
|---|---|
| CRD types | controllers/fleet-site/api/v1alpha1/fleetsite_types.go |
| Action mapping | controllers/fleet-site/internal/provision/yaidap.go |
| Hub YAML store | controllers/fleet-site/internal/connectors/yaidap/ |
| HTTP API | controllers/fleet-site/internal/httpapi/site_provision.go, site_inventory*.go |
| Job completion / vm_location | controllers/fleet-site/internal/siteprovision/sync.go |
| Connector playbook | site-provision-connector.yml |
| Operator flow | playbooks/flows/site-provision-yaidap.yml |
| CLI | cmd/umkim/internal/cmd/site_provision.go, site_inventory*.go |
| Console UI | ui/umkim-console/src/components/sites/SiteProvisionerInventoryPanel.tsx, SiteHostsPanel.tsx |
Historical design notes (superseded by this doc and fleet-site): archive/provisioner-connector-plan.md, archive/design-provisioner-connector.md.
Related
Section titled “Related”- fleet-site — FleetSite lifecycle, ingress, post-Ready ops
- umkim CLI — CLI examples
- Console README — console workflows
- Secrets — secret handling patterns