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

Reverse-spec · Clients

python-openstackclient — Summary

Reverse-specification of python-openstackclient — branch stable/2026.2

Branch stable/2026.2 @ 8bb083b83829 (tag 10.3.1) — repository https://opendev.org/openstack/python-openstackclient — analysed on 2026-11-01

  • Scope: all non-test code of the repository, all command groups. The change history (step 7) was not requested.
  • Dependencies read at fixed versions, from the venv built with requirements/upper-constraints.txt: osc-lib 4.7.0, cliff 4.16.0, openstacksdk 4.20.0, keystoneauth1 5.17.0. They are cited with the prefixes osc-lib:, cliff: and openstacksdk:.
  • References are file:line from the repository root. [inferred] marks a statement not followed to the end. [observed] marks a behaviour seen by running the real CLI against the local stub described in 07; the stub is not a real service. [lab] marks a behaviour seen against the OpenKeel lab.

Files of this specification

FileContent
01-cli-commands_stable-2026.2.mdshared mechanics, functional description per service, and the 799 commands with purpose, class, required arguments, microversion gates and observed requests
02-errors_stable-2026.2.mdexit codes, how failures reach the user, and the 420 error messages with their raise sites
03-interactions_stable-2026.2.mdKeystone, the service APIs, microversion negotiation, the three client stacks, local files and stdin
04-scenarios_stable-2026.2.md5 scenarios with sequence diagrams and diagnostics
05-async-processing_stable-2026.2.mdno background processing; the client-side polling and threads that exist
07-rewrite-pack_stable-2026.2.mdclient contract: what the CLI requires from the services, test vectors, proof and limits
_verification_stable-2026.2.mdoutput of the reference checker

File 06 (change history) is absent because it was not requested.

Overview

python-openstackclient (OSC) provides the single openstack command. It runs in one short-lived process per invocation:

  1. it parses the global options and reads clouds.yaml and OS_* variables;
  2. it loads commands from Python entry points;
  3. it authenticates against Keystone, only when the command needs it;
  4. it sends REST calls through one of three client stacks;
  5. it prints the result through cliff formatters.

It has no database, message bus, cache, server or background process. Service plugins extend it through entry points.

Architecture

flowchart LR
  user[User or script] -->|argv, env, stdin| cli[openstack process]
  cfg[clouds.yaml, secure.yaml, OS_* variables] --> cli
  cli --> sdk[openstacksdk Proxy]
  cli --> ksc[python-keystoneclient]
  cli --> api[openstackclient.api: object_store_v1, compute_v2]
  sdk --> ksa[keystoneauth1 session]
  ksc --> ksa
  api --> ksa
  ksa -->|HTTPS, X-Auth-Token| ks[Keystone]
  ksa -->|HTTPS| nova[Nova]
  ksa -->|HTTPS| neutron[Neutron]
  ksa -->|HTTPS| glance[Glance]
  ksa -->|HTTPS| cinder[Cinder]
  ksa -->|HTTPS| swift[Swift]
  plugins[Plugins: manilaclient, heatclient, osc-placement...] -.->|entry points| cli

Processes

ProcessEntry pointRoleListens to / consumes
openstackpyproject.toml:33 → openstackclient/shell.py:212run one command, or an interactive shell when no command is givenargv, environment, stdin; listens on nothing

External dependencies

SystemProtocolLibraryConfigurationUsage
KeystoneHTTPS RESTkeystoneauth1--os-auth-url, clouds.yamltoken, service catalog, identity commands
Nova, Neutron, Glance, CinderHTTPS RESTopenstacksdkKeystone catalogcommands of each service
SwiftHTTPS RESTopenstackclient/api/object_store_v1.py:35Keystone catalogobject, container, object store account
Manila and other servicesHTTPS RESTtheir pluginKeystone catalogonly when a plugin is installed

Key figures

ItemCount
Commands declared in pyproject.toml799 in 17 groups, 662 loaded with the default API versions, 3,471 arguments in total
Client calls found in the code2,140, of which 1,920 resolved to a REST route
API operations in the surface815: 478 observed on the wire, 337 only read in the source
Commands sending at least one request to the stub615 of 661 probed
Microversion gates206, on 55 compute and 29 volume microversions
Exception classes defined by the project0; it raises osc_lib ones, 420 distinct messages at 538 sites
Unit tests3,250 pass, which give 3,109 vectors
OpenKeel lab, 657 commands probed219 succeeded, 28 got a 501, 27 hit a route absent from Keystone, no other 5xx; the rest is mostly inconclusive

Points of attention

  1. Two commands exit in error on success [observed]: federation protocol set and block storage cluster set derive from command.Command but return a table (openstackclient/identity/v3/federation_protocol.py:179, openstackclient/volume/v3/block_storage_cluster.py:188). cliff treats any return value as the exit code (cliff:cliff/command.py:196). The update is applied, then the process prints the tuple on stderr and exits 1. A script will retry or fail on a successful change.
  2. **block storage cleanup without filters crashes [observed]**: it drops unset filters (openstackclient/volume/v3/block_storage_cleanup.py:167), and the SDK method then raises TypeError for whichever of binary, host and cluster_name is missing. With all three, it does send PUT /workers/cleanup. The unit tests mock the SDK and do not see it.
  3. **catalog show exits 0 when the service is unknown [observed]**: it logs service %s not found and returns an empty table (openstackclient/identity/v3/catalog.py:108).
  4. **mapping set without --rules shows a raw Python error [observed]**: expected str, bytes or os.PathLike object, not NoneType, from openstackclient/identity/v3/mapping.py:236.
  5. The default API version is not the microversion sent [observed]: DEFAULT_API_VERSION for compute is 2.1 (openstackclient/compute/client.py:26), yet without --os-compute-api-version the SDK negotiates per resource: 2.100 on servers, 2.81 on aggregates, 2.61 on flavors. With the option, every call uses that value. A service must therefore support both the latest microversions and the old ones.
  6. Gates depend on the negotiated version [observed]: with the default compute version set explicitly to 2.1, --tag gives --os-compute-api-version 2.49 or greater is required to support the --tag option.
  7. A list response without its key shows a bare key name [observed]: a 200 {} on GET /servers/detail prints 'servers' and exits 1. A compatible service must always send the collection key, even when it is empty.
  8. **share commands in this repository are dead code at run time [observed]**: openstackclient.share.v2 is in IGNORED_MODULES (openstackclient/shell.py:34), and openstackclient/share/client.py is not in openstack.cli.base. openstack share list is served by python-manilaclient (manilaclient.osc.v2.share:ListShare), which is a required dependency (requirements.txt:12).
  9. Identity v2 and image v1 commands are reachable only by option: with a v3-only catalog, --os-identity-api-version 2 gives Could not find requested endpoint in Service Catalog. [observed].
  10. **project cleanup can hide failures [observed]**: against the stub, --dry-run exits 0 although the SDK cleanup thread logged a NotFoundException on GET /volume/v3/backups. An empty listing does not prove that the project is empty.
  11. Secrets on the command line and on stdout: --os-password and --password are visible in the process list. token issue and application credential create print secrets by design (openstackclient/identity/v3/token.py:187, openstackclient/identity/v3/application_credential.py:73). Debug logs mask passwords through strutils.mask_password (osc-lib:osc_lib/shell.py:435).
  12. No project-level timeout: api_timeout is not set by default (openstacksdk:openstack/config/cloud_region.py:997), so a stuck service can block a command. --wait loops only stop on a target or error status [inferred for the absence of a limit, from osc-lib:osc_lib/utils/__init__.py:779].
  13. Unit tests cannot catch these: they use mocks of the SDK proxies (openstackclient/tests/unit/compute/v2/fakes.py:49), which is why items 1 to 4 pass their tests.

Blind spots / [inferred]

  • No original OpenStack service was contacted. Functional tests need devstack, so they were not run. The CLI was run against the local stub and against the OpenKeel lab (Go services), so nothing here compares OpenKeel with the Python services.
  • Probe limits: synthetic arguments often stop a command at a missing parent resource, so many multi-step commands are known statically only. See the compatibility table.
  • 46 of the 661 probed commands sent no request, mostly because synthetic arguments failed client-side validation or a file was missing. Their routes come from the source only.
  • **consumer *, access token create and similar OAuth1 commands** need oauthlib, absent from the venv [observed]: To use 'consumers' oauthlib must be installed.
  • **server ssh** was never run, because it executes the local ssh binary.
  • The 137 non-default commands (identity.v2, image.v1, volume.v2, share.v2) were catalogued and analysed statically, not probed.
  • Static routes are approximate: the SDK fallback of find_* was read as GET /resource?name=, while the wire shows GET /servers/detail?name= on compute and block storage. For routes, the observed value wins.
  • Plugins are out of scope here; each has its own specification: osc-placement, barbican, designate, heat, ironic, manila, masakari, neutron, octavia.
  • Output formatting by cliff (table, JSON, YAML, CSV, value) was not specified command by command.

Source: reverse-spec-python-openstackclient/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.