OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

ironic — Summary

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 (redfish with virtual media and HTTPS boot, ipmi, pxe/ipxe boot) with the others as an inventory, the JSON-RPC transport, the built-in inspection (ironic-inspector merged), the new ironic-networking service (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) for openkeel-ironic.
  • Other repositories cited (prefix: repository @ commit): nova: nova stable/2026.2 @ 8df1922926dd; ipa: ironic-python-agent stable/2026.2 @ daa18e2103ef; ironicclient: python-ironicclient stable/2026.2 @ af2ceeb9c4f2; sdk: openstacksdk stable/2026.2 @ fbdc403819e0; heat: heat stable/2026.2 @ 5872396e548c; skyline: skyline-console stable/2026.2 @ 806d200392ee; backend: the generate-go-from-spec skill's references/backends/ directory.
FileContent
01-rest-api131 operations (discovered by probing), cross-cutting rules, functional sheets, lifecycles
02-errors214 exception classes, translation to HTTP, asynchronous errors, key logs
03-interactionsRPC (oslo.messaging and JSON-RPC, 62 methods), database, REST to Glance/Swift/Neutron/Nova/Cinder, IPA, BMCs
04-scenariosenrolment→available, Nova boot, deletion+cleaning, power drift, conductor failure
05-async-processingperiodic tasks, heartbeat processing, step loop, takeover, inspection, allocation
06-change-history22 commits since the branch point: 2 CVE fixes, 1 log-secret fix, 6 fixes
07-rewrite-packformal state machine and step framework, client expectations (Nova, IPA, ironicclient/OSC, SDK, Heat, Skyline), conformance data, relation to the OpenKeel bare metal backend
_verificationoutput 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

ProcessEntry pointRoleListens to / consumes
ironic-apiironic/command/api.py (pyproject.toml [project.scripts]), WSGI ironic/wsgiREST API, policy, validation, routing to conductorsHTTP 6385
ironic-conductorironic/command/conductor.pystate machine, drivers, steps, periodic tasksRPC topic ironic.conductor_manager (ironic/common/rpc.py:36) or JSON-RPC port 8089
ironicironic/command/singleprocess.pyAPI + conductor (+ novncproxy) in one process, rpc_transport=none possibleboth
ironic-networkingironic/command/networking.py:39switch configuration for the ironic-networking network interfaceRPC topic ironic.networking_manager
ironic-novncproxyironic/command/novncproxy.pygraphical console proxyHTTP
ironic-pxe-filterironic/command/pxe_filter.pydnsmasq host filtering for unmanaged inspectionDB polling
ironic-dbsync, ironic-status, ironic-manageironic/command/dbsync.py, ironic/command/status.py, ironic/command/manage.pymigrations, upgrade checks, admin—
IPA (other repository)ipa:ironic_python_agent/agent.pyin-band agent in the ramdiskHTTP 9999

External dependencies

SystemProtocolLibraryConfigurationUsage
MariaDB/MySQLSQLoslo.db, SQLAlchemy 2, Alembic[database]/connectionall state, node locks
RabbitMQ (optional)AMQPoslo.messaging[DEFAULT]/transport_url, rpc_transport=osloAPI → conductor RPC, notifications
JSON-RPC (alternative)HTTP(S)built inrpc_transport=json-rpc, [json_rpc]API → conductor RPC without a broker
KeystoneRESTkeystonemiddleware, keystoneauth1[keystone_authtoken], service sectionstoken validation, service credentials
Glance, SwiftRESTopenstacksdk / keystoneauth[glance], [swift]images, temp URLs, configdrive and ISO storage
NeutronRESTopenstacksdk[neutron]provisioning/cleaning networks, port binding, DHCP options
NovaRESTkeystoneauth[nova]power-update external events
CinderRESTopenstacksdk[cinder]boot from volume (not shipped by OpenKeel)
BMCsRedfish (HTTPS), IPMI (UDP 623)sushy, ipmitoolnode driver_infopower, boot device, virtual media, firmware, BIOS, RAID
IPAHTTP(S)requestsnode driver_internal_info.agent_urlin-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

  1. 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).
  2. Microversion default 1.1 when the header is absent; "too old" answers are 404, 405 or 406 depending on the route (07 § 3.6).
  3. 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.
  4. Foreign nodes answer 404, not 403 (existence concealment) — the RBAC matrix relies on it.
  5. Secrets in clear in the database: BMC passwords in nodes.driver_info, the agent token in driver_internal_info (masked only in API output, ironic/api/controllers/v1/node.py:1905); the token is compared with == (ironic/conductor/utils.py:1543).
  6. **[agent]/require_tls defaults to true**: plain-HTTP agent callback URLs are refused (ironic/conductor/manager.py:3554).
  7. **redfish hardware type defaults to ipxe boot**, not virtual media: OpenKeel must configure the boot interface explicitly.
  8. Conductor-local files (PXE configs, ISOs, image cache) are rebuilt on takeover; IPA and BMCs download from the owning conductor's HTTP server.
  9. api-ref errors: volume connector/target DELETE paths (singular), indicator path format; GET …/indicators/{component}/{indicator} answers 500; DELETE /v1/inspection_rules answered 500 in the probe (01 § 3).
  10. 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.
  11. 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), the ansible, anaconda, bootc, custom-agent deploy interfaces, consoles (graphical, socat), RAID/BIOS/firmware internals: inventory only (_rewrite/drivers_stable-2026.2.json).
  • ironic-networking and 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_rules 500 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.