Reverse-specification of ironic — branch stable/2026.2
Branch
stable/2026.2@39ce09ec3b0d— repository https://opendev.org/openstack/ironic — analysed on 2026-10-29
- Repository: https://opendev.org/openstack/ironic — specified branch:
stable/2026.2@39ce09ec3b0d(39.0.0+29) — analysed on: 2026-10-29 - Scope: the whole service —
ironic-api(v1 REST API, microversions 1.1–1.115, ramdisk endpoints),ironic-conductor(provision state machine, steps, inspection, periodic tasks, locks, hash ring, takeover), the hardware types shipped by OpenKeel (redfishwith virtual media and HTTPS boot,ipmi,pxe/ipxeboot) with the others as an inventory, the JSON-RPC transport, the built-in inspection (ironic-inspector merged), the newironic-networkingservice (inventory level), and the integrations (Neutron, Glance, Swift, Nova, Cinder; Placement through Nova). Steps 1 to 9: change history (06) on the automatic range, rewrite pack (07) foropenkeel-ironic. - Other repositories cited (prefix: repository @ commit):
nova:novastable/2026.2@8df1922926dd;ipa:ironic-python-agentstable/2026.2@daa18e2103ef;ironicclient:python-ironicclientstable/2026.2@af2ceeb9c4f2;sdk:openstacksdkstable/2026.2@fbdc403819e0;heat:heatstable/2026.2@5872396e548c;skyline:skyline-consolestable/2026.2@806d200392ee;backend:thegenerate-go-from-specskill'sreferences/backends/directory.
| File | Content |
|---|---|
| 01-rest-api | 131 operations (discovered by probing), cross-cutting rules, functional sheets, lifecycles |
| 02-errors | 214 exception classes, translation to HTTP, asynchronous errors, key logs |
| 03-interactions | RPC (oslo.messaging and JSON-RPC, 62 methods), database, REST to Glance/Swift/Neutron/Nova/Cinder, IPA, BMCs |
| 04-scenarios | enrolment→available, Nova boot, deletion+cleaning, power drift, conductor failure |
| 05-async-processing | periodic tasks, heartbeat processing, step loop, takeover, inspection, allocation |
| 06-change-history | 22 commits since the branch point: 2 CVE fixes, 1 log-secret fix, 6 fixes |
| 07-rewrite-pack | formal state machine and step framework, client expectations (Nova, IPA, ironicclient/OSC, SDK, Heat, Skyline), conformance data, relation to the OpenKeel bare metal backend |
| _verification | output of check_refs.py --context |
Overview
Ironic provisions physical servers. Operators enrol machines with their BMC credentials; Ironic verifies, inspects and cleans them, and then deploys an image on request — usually from Nova, which schedules bare metal flavors on nodes through Placement and drives Ironic through its ironic virt driver. The API is a thin Pecan layer that validates, checks policies and forwards everything that touches a node to the conductor responsible for it, chosen by a consistent hash ring over the live conductors. The conductor holds a database lock on the node, applies the provision state machine, and runs long operations in worker threads: out-of-band through the BMC (Redfish via sushy, IPMI via ipmitool) and in-band through ironic-python-agent (IPA), a ramdisk that looks the node up, heartbeats to the API and executes the conductor's commands on port 9999. Steps (clean, deploy, service, verify) are the unit of work, ordered by priority. Periodic tasks keep power states in sync, time out silent agents, and take over the nodes of dead conductors.
Architecture
flowchart LR user[Operator / OSC baremetal / Skyline] -->|HTTPS /v1| api[ironic-api] nova[nova-compute ironic driver] -->|HTTPS /v1 via openstacksdk| api api -->|SQL| db[(MariaDB)] api -->|RPC: oslo.messaging or JSON-RPC| cond[ironic-conductor x N] cond -->|SQL| db cond -->|Redfish HTTPS / IPMI| bmc[(BMCs)] cond -->|HTTP :9999 commands| ipa[IPA ramdisk on the node] ipa -->|lookup / heartbeat / continue_inspection| api ipa -->|image download HTTP| cond cond -->|REST| glance[Glance] & neutron[Neutron] & swift[Swift] & novaapi[Nova external events] cond -->|RPC| net[ironic-networking] net --> sw[(switches)] api -->|token validation| ks[Keystone]
Processes
| Process | Entry point | Role | Listens to / consumes |
|---|---|---|---|
ironic-api | ironic/command/api.py (pyproject.toml [project.scripts]), WSGI ironic/wsgi | REST API, policy, validation, routing to conductors | HTTP 6385 |
ironic-conductor | ironic/command/conductor.py | state machine, drivers, steps, periodic tasks | RPC topic ironic.conductor_manager (ironic/common/rpc.py:36) or JSON-RPC port 8089 |
ironic | ironic/command/singleprocess.py | API + conductor (+ novncproxy) in one process, rpc_transport=none possible | both |
ironic-networking | ironic/command/networking.py:39 | switch configuration for the ironic-networking network interface | RPC topic ironic.networking_manager |
ironic-novncproxy | ironic/command/novncproxy.py | graphical console proxy | HTTP |
ironic-pxe-filter | ironic/command/pxe_filter.py | dnsmasq host filtering for unmanaged inspection | DB polling |
ironic-dbsync, ironic-status, ironic-manage | ironic/command/dbsync.py, ironic/command/status.py, ironic/command/manage.py | migrations, upgrade checks, admin | — |
| IPA (other repository) | ipa:ironic_python_agent/agent.py | in-band agent in the ramdisk | HTTP 9999 |
External dependencies
| System | Protocol | Library | Configuration | Usage |
|---|---|---|---|---|
| MariaDB/MySQL | SQL | oslo.db, SQLAlchemy 2, Alembic | [database]/connection | all state, node locks |
| RabbitMQ (optional) | AMQP | oslo.messaging | [DEFAULT]/transport_url, rpc_transport=oslo | API → conductor RPC, notifications |
| JSON-RPC (alternative) | HTTP(S) | built in | rpc_transport=json-rpc, [json_rpc] | API → conductor RPC without a broker |
| Keystone | REST | keystonemiddleware, keystoneauth1 | [keystone_authtoken], service sections | token validation, service credentials |
| Glance, Swift | REST | openstacksdk / keystoneauth | [glance], [swift] | images, temp URLs, configdrive and ISO storage |
| Neutron | REST | openstacksdk | [neutron] | provisioning/cleaning networks, port binding, DHCP options |
| Nova | REST | keystoneauth | [nova] | power-update external events |
| Cinder | REST | openstacksdk | [cinder] | boot from volume (not shipped by OpenKeel) |
| BMCs | Redfish (HTTPS), IPMI (UDP 623) | sushy, ipmitool | node driver_info | power, boot device, virtual media, firmware, BIOS, RAID |
| IPA | HTTP(S) | requests | node driver_internal_info.agent_url | in-band steps |
| DHCP/TFTP/HTTP | — | dnsmasq / Neutron / web server | [dhcp], [pxe], [deploy]/http_* | network boot, image serving |
Key figures
- 131 REST operations (+107 Pecan dispatch artifacts), 118 documented in the api-ref, 115 microversions, 198 JSON schemas, 71 version-gate functions, 133 policy rules.
- 57 conductor RPC methods (RPC API 1.62) + 5 networking RPC methods; 14 conductor periodic tasks + 9 in shipped hardware interfaces; 0 notification listeners.
- Provision state machine: 30 states, 93 transitions, 21 events → 630 vectors.
- 21 tables; migrations = models (1 index name differs).
- 214 exception classes, 1 208 raise sites.
- 6 hardware types, 66 interface implementations.
- Conformance: 613 RBAC cases replayed from seeds (613/613 statuses identical); 2 215 exchanges from 2 021 API tests.
- Change history: 22 commits, 9 functional (2 CVEs).
Points of attention
- The API is only half of Ironic: every node operation is an RPC to the conductor that owns the node; what clients observe (states,
last_error) is produced asynchronously by workers, heartbeats and periodic tasks (05). - Microversion default 1.1 when the header is absent; "too old" answers are 404, 405 or 406 depending on the route (07 § 3.6).
- 409 vs 400 on refused transitions depends on whether the node is reserved (
ironic/api/controllers/v1/node.py:1282); three clients retry 409 automatically. - Foreign nodes answer 404, not 403 (existence concealment) — the RBAC matrix relies on it.
- Secrets in clear in the database: BMC passwords in
nodes.driver_info, the agent token indriver_internal_info(masked only in API output,ironic/api/controllers/v1/node.py:1905); the token is compared with==(ironic/conductor/utils.py:1543). - **
[agent]/require_tlsdefaults to true**: plain-HTTP agent callback URLs are refused (ironic/conductor/manager.py:3554). - **
redfishhardware type defaults toipxeboot**, not virtual media: OpenKeel must configure the boot interface explicitly. - Conductor-local files (PXE configs, ISOs, image cache) are rebuilt on takeover; IPA and BMCs download from the owning conductor's HTTP server.
- api-ref errors: volume connector/target DELETE paths (singular), indicator path format;
GET …/indicators/{component}/{indicator}answers 500;DELETE /v1/inspection_rulesanswered 500 in the probe (01 § 3). - Two CVE fixes on the branch (Redfish subscription id path traversal, maintenance policy bypass via PATCH) and step secrets masked in logs (06); the maintenance fix's CVE number differs between commit and release note.
- No route table in the code: the route list is the result of probing (07 § 8).
Blind spots / [inferred]
- Vendor hardware types (
idrac,intel-ipmi), theansible,anaconda,bootc,custom-agentdeploy interfaces, consoles (graphical, socat), RAID/BIOS/firmware internals: inventory only (_rewrite/drivers_stable-2026.2.json). ironic-networkingand Trait Based Networking: covered at the interface and change-history level, not traced step by step.- In-band steps implemented in IPA (erase devices, write image, bootloader installation): out of this repository.
- Conductor flows end to end (deploy/clean with a real BMC and agent) were read, not run; the unit tests mock drivers and the agent (07 § 8).
- Rows marked [inferred] in the files: the
DELETE /v1/inspection_rules500 cause, the reachability of TBN universal constructors.
Source: reverse-spec-ironic/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.