Reverse-specification of python-octaviaclient — branch stable/2026.2
Branch
stable/2026.2@b37975ec8f2d(tag3.15.0+ 2 commits) — repository https://opendev.org/openstack/python-octaviaclient — analysed on 2026-11-01
- Scope: all non-test code of the repository: the OSC plugin (
octaviaclient/osc), and the Python API its commands use (octaviaclient/api). The change history (step 7) was not requested. - Dependencies read at fixed versions, from a venv: osc-lib 4.7.0, cliff 4.16.0, python-openstackclient 10.3.1, openstacksdk 4.20.0, keystoneauth1 5.17.0, Python 3.13. They are cited with the prefixes
osc-lib:,cliff:,openstackclient:,keystoneauth:andrequests:. - Service read for comparison, not run: Octavia
stable/2026.2@2900ed67cac2, cited asoctavia:, and its reportreverse-spec-octavia/. - References are
file:linefrom the repository root. [observed]: seen by running the real shell against the local stub of 07. [inferred]: not followed to the end. - The stub is not a service. An [observed] behaviour says how the client reacts to a given answer. It says nothing about what a real Octavia answers, and nothing here was run against one.
Files of this specification
| File | Content |
|---|---|
| 01-cli-commands_stable-2026.2.md | shared mechanics, functional description per family, the 82 commands, and the wire effect of each option |
| 02-errors_stable-2026.2.md | exit codes, HTTP error translation, client-side messages, and the 16 defects reproduced or read |
| 03-interactions_stable-2026.2.md | Keystone, Octavia, Neutron; headers, bodies, query strings, pagination |
| 04-scenarios_stable-2026.2.md | 5 scenarios with sequence diagrams and diagnostics |
| 05-async-processing_stable-2026.2.md | no background processing; the two client-side loops |
| 07-rewrite-pack_stable-2026.2.md | client contract: what the CLI requires from a load-balancer service, test data, proof and limits |
| _verification_stable-2026.2.md | output of the reference checker |
File 06 (change history) is absent because it was not requested.
Overview
python-octaviaclient is a plugin of the openstack command. It adds 82 loadbalancer … commands that drive the Octavia REST API. Each invocation is one short-lived process:
- python-openstackclient loads the plugin through entry points (
setup.cfg:30-31); - it authenticates against Keystone;
- the command resolves names to IDs with list calls;
- it sends its REST call through
OctaviaAPI, a thin wrapper onosc_lib.api.api.BaseAPI(octaviaclient/api/v2/octavia.py:65); - with
--wait, it polls a status field; - cliff prints the result.
It has no database, message bus, cache, server or background process. There is no version discovery and no microversion: the endpoint comes from the catalog and /v2.0 is appended (octaviaclient/api/v2/octavia.py:104-106).
Architecture
flowchart LR user[User or script] -->|argv, env| cli[openstack process] cfg[clouds.yaml, OS_* variables] --> cli cli --> plugin[octaviaclient.osc.v2 commands] plugin --> api[OctaviaAPI on osc-lib BaseAPI] plugin --> sdk[openstacksdk network proxy] plugin --> ksc[python-keystoneclient] api --> ksa[keystoneauth1 session] sdk --> ksa ksc --> ksa ksa -->|HTTPS, password, token, catalog| ks[Keystone] ksa -->|HTTPS, X-Auth-Token, /v2.0/lbaas and /v2.0/octavia| oct[Octavia] ksa -->|HTTPS, name to ID lookups| neu[Neutron]
Processes
| Process | Entry point | Role | Listens to / consumes |
|---|---|---|---|
openstack (from python-openstackclient) | entry points openstack.cli.extension = load_balancer and openstack.load_balancer.v2 (setup.cfg:29-33) | runs one command | argv, environment; listens on nothing |
The package has no legacy CLI and no console script of its own: setup.cfg:29-115 declares entry points for OSC only, and no console_scripts. No separate shell exists to list.
External dependencies
| System | Protocol | Library | Configuration | Usage |
|---|---|---|---|---|
| Keystone | HTTPS REST | keystoneauth1 | --os-auth-url, clouds.yaml | token, service catalog |
| Keystone | HTTPS REST | python-keystoneclient, through client_manager.identity | catalog | project name to ID (octaviaclient/osc/v2/utils.py:172-179) |
| Octavia | HTTPS REST | OctaviaAPI (octaviaclient/api/v2/octavia.py:65) | catalog, type load-balancer (octaviaclient/osc/plugin.py:32-36) | all commands |
| Neutron | HTTPS REST | openstacksdk | catalog, type network | subnet, port, network, QoS policy, security group lookups (octaviaclient/osc/v2/utils.py:270-289) |
Key figures
| Item | Count |
|---|---|
| Commands | 82 in 14 families: 38 without table output, 29 single, 15 list; 491 arguments |
Methods of OctaviaAPI | 73, one per route; all 73 routes observed on the wire |
| Commands sending at least one request to the stub | 72 of 82 with no optional argument; 82 of 82 with options. The other 10 are unset commands, which send no service request when no option is given |
| Wire runs | 82 per mode (404, {}, typed) and 475 in the option sweep, 1,275 requests in total |
| Exception classes defined by the package | 1 (octaviaclient/api/exceptions.py:15) |
| Unit tests | 421 pass, 287 vectors, 82 of 82 classes covered, 2 identical runs |
| Defects | 16 in 02 § 5: 14 reproduced on the stub (for 2 of them, one branch was only read), 1 reproduced on the client side with the service part read, 1 read only |
Points of attention
- A paginated list loses its filter [observed]: the client replaces all query parameters with those of the
nextlink (octaviaclient/api/v2/octavia.py:97-98), and Octavia's link carries only limit, sort and marker (octavia:octavia/api/common/pagination.py:151-172). A filter that fills the first page (1,000 items by default,octavia:octavia/common/constants.py:755) then shows unfiltered items from page 2 onwards, with exit 0. The two sides were read and the client reproduced on the stub; they were never run together. - 13 commands need Neutron in the catalog, even when they never call it [observed]:
loadbalancer liststops withService 'network' is disabled…when no network service is listed, because attribute maps touchclient_manager.sdk_connection.networkwhen they are built (octaviaclient/osc/v2/utils.py:273,octaviaclient/osc/v2/utils.py:456). OpenKeel must register a network endpoint. - **
--waithas no timeout when waiting for ACTIVE [observed]**:wait_for_statusloops untilactiveorerror(osc-lib:osc_lib/utils/__init__.py:805). - A single-resource response must have its wrapper as the first JSON key [observed]:
BaseAPI.findtakeslist(ret.keys())[0](osc-lib:osc_lib/api/api.py:409). This matters for a Go service whose encoder sorts or reorders keys. - Malformed responses and input give raw Python messages [observed]:
'NoneType' object has no attribute 'status_code','Response' object is not subscriptable,not enough values to unpack. See 02 § 5. - **Numbers may arrive as strings, booleans as
True/False[observed]**:connection_limitis a JSON string (octaviaclient/osc/v2/utils.py:262), and query booleans areadmin_state_up=True,cascade=True. A strict Go decoder would reject these. - **
loadbalancer quota defaults showcalls a route that does not exist as such**:GET /lbaas/quotas/defaults(octaviaclient/api/constants.py:61) lands onGET /quotas/{project_id}in Octavia (octavia:octavia/api/v2/controllers/quotas.py:37), so a non-admin should get 403 [inferred]. - Deprecated foundations:
BaseAPI,wait_for_status,wait_for_deleteandClientCacheare marked deprecated in osc-lib (osc-lib:osc_lib/api/api.py:69,osc-lib:osc_lib/utils/__init__.py:799,osc-lib:osc_lib/utils/__init__.py:747,osc-lib:osc_lib/clientmanager.py:41), and the package depends on all of them. - Unit tests cannot catch these: the commands use a mocked API (
octaviaclient/tests/unit/osc/v2/test_load_balancer.py:36), and the one port check that is dead is tested with a key the code never produces (octaviaclient/tests/unit/osc/v2/test_validations.py:116). - **No command returns a value from
command.Command**: the defect found in python-openstackclient does not occur here. An AST walk of the 38command.Commandclasses found noreturn <value>, and all of them exit 0 on the typed stub [observed].
Blind spots / [inferred]
- No real service was contacted. There are no functional tests:
octaviaclient/tests/functional/base.py:45holds a helper class andoctaviaclient/tests/functional/osc/v2/is empty. Compatibility with a real Octavia rests on reading its code and its OpenAPI, not on running them. - The stub answers what it is told. Its shaped responses are built from the client's own column lists, so an exit 0 in typed mode proves that requests are well formed for this stub and that the client copes with the answer.
- **Multi-page lookups, real
--waittransitions and real deletion** were simulated, not observed on a service. - The wait timeout of 300 s for deletion was read (
osc-lib:osc_lib/utils/__init__.py:727), not waited for. - Output formatting by cliff (table, JSON, YAML, CSV, value) was not specified command by command. Column lists are in
octaviaclient/osc/v2/constants.py:15. - Authentication variants other than password were not run; they belong to python-openstackclient and keystoneauth1.
- **
octaviaclient/hacking/checks.py:1** is a flake8 plugin for developers, not run-time code; it was not specified. - **
requests_mockAPI tests** (octaviaclient/tests/unit/api/test_octavia.py:1) pass and are counted in the 421, but they give no vectors, since the capture recordscheck_parsertests only.
Source: reverse-spec-python-octaviaclient/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.