End-to-end scenarios — ironic — branch stable/2026.2
Branch
stable/2026.2@39ce09ec3b0d— repository https://opendev.org/openstack/ironic — analysed on 2026-10-29
The scenarios use the configuration OpenKeel intends to ship: redfish hardware type, redfish-virtual-media boot, direct deploy (IPA writes the image), flat or neutron network interface, JSON-RPC between API and conductor. Variants for ipmi + ipxe are noted where they differ.
1. Enrolment, verification, inspection and cleaning
An operator registers a machine and makes it available for scheduling.
sequenceDiagram
autonumber
actor Op as Operator
participant API as ironic-api
participant C as ironic-conductor (ring owner)
participant DB as MariaDB
participant BMC as BMC (Redfish)
participant IPA as IPA (ramdisk)
Op->>API: POST /v1/nodes {driver: redfish, driver_info: {redfish_address,...}}
API->>C: RPC call create_node
C->>DB: INSERT nodes (provision_state=enroll)
API-->>Op: 201 Created
Op->>API: PUT /v1/nodes/{id}/states/provision {target: manage}
API->>C: RPC call do_provisioning_action(manage)
C->>DB: UPDATE nodes SET provision_state=verifying, reservation=C
API-->>Op: 202
C->>BMC: GET /redfish/v1/Systems/{id} (power.validate + get_power_state, verify steps)
alt BMC reachable
C->>DB: provision_state=manageable, power_state=power off
else credentials or address wrong
C->>DB: provision_state=enroll, last_error
end
Op->>API: PUT .../states/provision {target: inspect}
API->>C: RPC call inspect_hardware
C->>BMC: build deploy ISO, VirtualMedia.InsertMedia, boot override cd, Reset On
C->>DB: provision_state=inspect wait
IPA->>API: POST /v1/continue_inspection {inventory,...}
API->>C: RPC call continue_inspection
C->>DB: hooks + inspection rules: properties, ports, traits; INSERT node_inventory
C->>BMC: eject media, power off
C->>DB: provision_state=manageable
Op->>API: PUT .../states/provision {target: provide}
API->>C: RPC call do_provisioning_action(provide)
C->>DB: provision_state=cleaning (target available)
C->>BMC: boot IPA by virtual media
C->>DB: clean wait
IPA->>API: GET /v1/lookup?addresses=... → token
IPA->>API: POST /v1/heartbeat/{id}
API->>C: RPC call heartbeat
C->>IPA: POST /v1/commands clean.get_clean_steps, then clean.execute_clean_step (erase_devices_metadata)
IPA-->>C: status SUCCEEDED (next heartbeat)
C->>BMC: eject, power off
C->>DB: provision_state=availableDiagnostics: stuck in verifying → BMC address/credentials (last_error, GET …/validate); inspect wait then inspect failed after 30 min → the ramdisk did not boot or cannot reach the API ([DEFAULT]/ironic_api_url / [service_catalog] seen by the ramdisk, provisioning network routing); clean failed → last_error names the step, IPA logs are stored when [agent]/deploy_logs_collect allows it.
2. Nova boots a bare metal instance
Nova's ironic virt driver (nova:nova/virt/ironic/driver.py) uses openstacksdk with microversion negotiation; Placement already picked the node through its CUSTOM_<RESOURCE_CLASS> inventory.
sequenceDiagram
autonumber
participant N as nova-compute (ironic driver)
participant API as ironic-api
participant C as ironic-conductor
participant G as Glance
participant NE as Neutron
participant BMC as BMC
participant IPA as IPA
N->>API: PATCH /v1/nodes/{id} instance_uuid (retry_on_conflict=False)
alt another instance already reserved the node
API-->>N: 409 InstanceAssociated → reschedule
end
N->>API: PATCH /v1/nodes/{id} [/instance_info/image_source, root_gb, capabilities, traits, ...]
N->>API: POST /v1/nodes/{id}/vifs {id: neutron port} (per port)
API->>C: RPC call vif_attach
C->>NE: (flat) record VIF in port.internal_info; (neutron) bind later
N->>API: GET /v1/nodes/{id}/validate
N->>API: PUT /v1/nodes/{id}/states/provision {target: active, configdrive: ...}
API->>C: RPC call do_node_deploy
C->>G: GET image metadata
C->>C: deploy steps = driver steps + deploy templates (instance traits) + user steps
C->>NE: (neutron) create port on provisioning network, binding:host_id=node
C->>BMC: build ISO with IPA + params, InsertMedia, boot cd, Reset
C-->>API: node deploying → wait call-back
API-->>N: 202
IPA->>API: GET /v1/lookup → node + agent_token
IPA->>API: POST /v1/heartbeat/{id} {callback_url, agent_token, agent_version}
API->>C: RPC call heartbeat
C->>IPA: POST /v1/commands deploy.execute_deploy_step write_image (image URL served by conductor HTTP or Swift temp URL)
IPA->>G: (image_download_source=swift) GET temp URL / else GET conductor HTTP
loop each heartbeat
C->>IPA: GET /v1/commands (status)
end
C->>IPA: image.install_bootloader (UEFI), standby.power_off / sync
C->>NE: remove provisioning port; bind tenant ports (binding:profile local_link_information)
C->>BMC: eject media, boot override disk, power on
C->>C: provision_state=active
loop nova polls
N->>API: GET /v1/nodes/{id}?fields=...
end
N->>N: instance ACTIVEDiagnostics: 409 on the first PATCH is normal contention; deploy failed with "Timeout reached while waiting for callback" means no heartbeat for 30 min (ramdisk did not boot, provisioning network, [agent]/require_tls with a plain-HTTP agent); "Invalid or missing agent token" in conductor logs means a second lookup or a stale ramdisk; tenant network errors appear as NetworkError in last_error.
3. Instance deletion and automated cleaning
sequenceDiagram
autonumber
participant N as nova-compute
participant API as ironic-api
participant C as ironic-conductor
participant NE as Neutron
participant BMC as BMC
participant IPA as IPA
N->>API: PUT .../states/provision {target: deleted}
API->>C: RPC call do_node_tear_down
C->>BMC: power off
C->>NE: unbind tenant ports
C->>C: deleting → cleaning (target available)
API-->>N: 202
N->>API: DELETE /v1/nodes/{id}/vifs/{vif} (each)
N->>API: PATCH instance_uuid=null, instance_info={}
C->>BMC: boot IPA (virtual media)
IPA->>API: lookup, heartbeat
C->>IPA: clean steps (erase_devices_metadata by default)
alt success
C->>C: available (Nova's provider reserved until then)
else failure
C->>C: clean failed + last_error (operator: manage, fix, provide)
end4. Power state drift
sequenceDiagram
autonumber
participant C as ironic-conductor (_sync_power_states, 120 s)
participant DB as MariaDB
participant BMC as BMC
participant NA as Nova API
C->>DB: nodes mapped to me, maintenance=false
C->>BMC: get_power_state
alt BMC error 3 times
C->>DB: maintenance=true, fault=power failure
else state differs, force_power_state_during_sync=true
C->>BMC: set power to DB state
else state differs, force disabled
C->>DB: power_state=observed
C->>NA: POST /os-server-external-events power-update (2.76)
end5. Conductor failure
sequenceDiagram autonumber participant C1 as conductor-0 (dies) participant C2 as conductor-1 participant DB as MariaDB C1-xDB: no more UPDATE conductors.updated_at Note over C2: after heartbeat_timeout (60 s) C1 is offline; ring rebuilt within 15 s C2->>DB: _check_orphan_nodes: release reservations of C1, deploying→deploy failed, cleaning→clean failed+maintenance C2->>DB: _sync_local_state: active nodes now mine → _do_takeover (re-create boot configs), conductor_affinity=C2 C2->>DB: _check_orphan_allocations: take over C1's allocations Note over C1: on restart: clear own reservations, _correct_stuck_states fails nodes still transient
Diagnostics: nodes failing with "The deploying state can't be resumed by conductor X" after a restart are expected; frequent takeovers mean conductors flap (DB latency vs heartbeat_timeout).
Source: reverse-spec-ironic/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.