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

Reverse-spec · Clients

python-ironicclient — Scenarios

End-to-end scenarios — python-ironicclient — branch stable/2026.2

Branch stable/2026.2 @ af2ceeb9c4f2 — repository https://opendev.org/openstack/python-ironicclient — analysed on 2026-11-01

Each scenario was run against a local stub, with its id in _rewrite/scenarios_stable-2026.2.json. The stub's answers were scripted, and do not show that Ironic answers the same way. What Ironic does behind the API is in reverse-spec-ironic/.

1. A list against an older server: negotiation

Id neg-406-downgrade. The stub announces a maximum of 1.90.

sequenceDiagram
  participant U as openstack baremetal node list
  participant K as Keystone
  participant I as Stub
  U->>K: POST /v3/auth/tokens
  K-->>U: 201, token and catalog
  U->>I: GET /v1/nodes, API version 1.115
  I-->>U: 406
  U->>I: GET /v1, no version header
  alt headers present
    I-->>U: 200, Minimum 1.1, Maximum 1.90
    U->>I: GET /v1/nodes, API version 1.90
    I-->>U: 200 {"nodes": []}
    Note over U: exit 0, cache file written
  else no version headers
    I-->>U: 200
    U->>I: GET /v1 again
    Note over U: RuntimeError, exit 1
  else GET /v1 fails
    I-->>U: 500
    Note over U: Internal Server Error (HTTP 500), exit 1
  end

Where to look: --debug shows the version used. If the user pinned a version, the failure says Requested API version … is not supported, and removing the option fixes it. Each new process goes through this again.

2. Moving a node with --wait

Ids wait-manage-ok, wait-manage-failed, wait-manage-other-stable, wait-manage-timeout.

sequenceDiagram
  participant U as baremetal node manage x --wait 30
  participant I as Stub
  U->>I: PUT /v1/nodes/x/states/provision {"target": "manage"}
  I-->>U: 200
  Note over U: prints Waiting for provision state manageable
  loop every 2 s
    U->>I: GET /v1/nodes/x
    alt provision_state = manageable
      I-->>U: 200
      Note over U: exit 0
    else "error" or ends in " failed"
      I-->>U: 200, last_error
      Note over U: StateTransitionFailed, exit 1
    else no target_provision_state
      I-->>U: 200
      Note over U: unexpected stable state, exit 1
    else still moving
      I-->>U: 200, target_provision_state set
    end
  end
  Note over U: timeout: StateTransitionTimeout, exit 1

Where to look: the error shows the state and last_error. A timeout on the client changes nothing on the server: the action keeps running. The first check runs at once, with no delay, so a state that already matches ends the wait [observed wait-service-already-active].

3. A conflict on a locked node

Id err-409-exhausts-retries.

sequenceDiagram
  participant U as baremetal node create --driver x
  participant I as Stub
  loop 6 attempts, 2 s apart
    U->>I: POST /v1/nodes
    I-->>U: 409 {"error_message": "...locked..."}
  end
  Note over U: logs Attempt 6 of 6, prints locked (HTTP 409), exit 1

Where to look: a command that takes 10 seconds then fails is this loop. A 503 behaves the same, unless it has a Retry-After header, in which case the command fails at once with a TypeError [observed err-503-retry-after].

4. Bulk creation from a file

Ids create-file-ok, create-file-port-mismatch.

sequenceDiagram
  participant U as baremetal create resources.yaml
  participant F as File
  participant I as Stub
  U->>F: read, then JSON schema check
  alt invalid
    Note over U: While validating the resources file(s)..., nothing sent
  else valid
    U->>I: POST /v1/chassis
    I-->>U: uuid c1
    U->>I: POST /v1/nodes, chassis_uuid c1
    I-->>U: uuid n1
    U->>I: POST /v1/ports, node_uuid n1
    I-->>U: uuid p1
    Note over U: exit 0
  end
  Note over U,I: a failed parent skips its children, others go on
  Note over U,I: nothing is rolled back, errors are listed at the end, exit 1

Where to look: after an error, the resources before it exist. Running the file again will conflict with them, unless UUIDs or names differ [inferred]. A wrong node_uuid on a port shows a raw Python tuple (02 § 5, defect 8).

5. The baremetal shell without Keystone

Ids legacy-noauth-endpoint, legacy-latest, legacy-no-endpoint.

sequenceDiagram
  participant U as baremetal
  participant I as Stub
  alt --os-endpoint given
    U->>I: GET /v1/nodes, 1.115, dummy token
    I-->>U: 200
  else --os-baremetal-api-version latest
    U->>I: GET /v1
    I-->>U: Maximum 1.90
    U->>I: GET /v1/nodes, 1.90
  else no endpoint
    Note over U: The Bare Metal API endpoint cannot be detected..., exit 1
  end

Where to look: this path is for a standalone Ironic, so nothing contacts Keystone. Whether a given Ironic accepts it depends on its auth settings; this was not tested.

Source: reverse-spec-python-ironicclient/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.