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

Reverse-spec · Clients

python-manilaclient — Rewrite pack

Client contract (rewrite pack) — python-manilaclient — branch stable/2026.2

Branch stable/2026.2 @ d18a7e966 (6.3.0 + 2 maintenance commits) — repository https://opendev.org/openstack/python-manilaclient — analysed on 2026-11-01

This pack has two uses:

  • **Keep openstack share working against the OpenKeel Manila service in Go**, by stating what the client requires.
  • Re-implement the plugin, thanks to the machine-readable catalogue.

No Manila ran. Everything observed was against a local stub on 127.0.0.1, which answers version discovery, a Keystone token, and fixed bodies. It shows what the client sends and how it reacts. It says nothing about what a real service does, and no OpenKeel or Python Manila was contacted. See § 5.

1. Contents of _rewrite/

FileContentUse
[cli-commands_stable-2026.2.json](_rewrite/cli-commands_stable-2026.2.json)154 commands: class, purpose, 603 arguments (names, type, default, choices, required, exclusive groups, help), client calls with file:line, gates, error sitesgenerate a parser; compare argument names
[client-contract_stable-2026.2.json](_rewrite/client-contract_stable-2026.2.json)per command: client methods, observed requests with query and body keys, static requests with version ranges, gates, unit vector count, exit codes per probe, lowest working versionrequirements per command
[api-surface_stable-2026.2.md](_rewrite/api-surface_stable-2026.2.md)232 operations per service, with source, version range, experimental flag, keys, commandsgap analysis of a service
[manager-routes_stable-2026.2.json](_rewrite/manager-routes_stable-2026.2.json)288 variants of 187 manager methods: range, file:line, method, route, body sent with synthetic argumentsthe routes of every microversion, also those the probe does not reach at 2.98
[unit-vectors_stable-2026.2.json](_rewrite/unit-vectors_stable-2026.2.json)573 vectors: argv → ordered manager calls with arguments, normalised to {uuid}test argument mapping without Python
[wire-probe-404_stable-2026.2.json](_rewrite/wire-probe-404_stable-2026.2.json)each command against a stub answering 404 with a fault bodyerror path
[wire-probe-200_stable-2026.2.json](_rewrite/wire-probe-200_stable-2026.2.json)each command against a stub answering 200 {}what breaks when keys are missing
[wire-probe-shaped_stable-2026.2.json](_rewrite/wire-probe-shaped_stable-2026.2.json)each command against a stub answering a body holding every key, with options added so that set commands send somethingreal routes and bodies at 2.98
[wire-probe-all-options_stable-2026.2.json](_rewrite/wire-probe-all-options_stable-2026.2.json)same, with every optional argument at oncequery keys, body keys, keyword mismatches
[version-sweep_stable-2026.2.json](_rewrite/version-sweep_stable-2026.2.json)exit code per command and per microversion 2.0 to 2.98, on the shaped stubwhere a command stops working
[defects-observed_stable-2026.2.json](_rewrite/defects-observed_stable-2026.2.json)60 targeted cases: argv, stub setting, exit, stderr, requests, and one sample of headersreproducible evidence for 02
[manila-route-check_stable-2026.2.md](_rewrite/manila-route-check_stable-2026.2.md)client routes against Manila's declared routes and actionsstatic cross-check
ScriptRole
[extract_plugin_stable-2026.2.py](_rewrite/extract_plugin_stable-2026.2.py)imports the entry points, dumps parsers, walks take_action. Adapted for api_version comparisons, matches, and methods called on a Resource
[manager_routes_stable-2026.2.py](_rewrite/manager_routes_stable-2026.2.py)new: calls each manager variant on a fake HTTP client
[probe_plugin_stable-2026.2.py](_rewrite/probe_plugin_stable-2026.2.py)the stub and the probe. Adapted: Manila version document, shaped mode, --api-version, PROBE_ALL_OPTIONS, reset of the client cache
[capture_plugin_stable-2026.2.py](_rewrite/capture_plugin_stable-2026.2.py)runs the OSC unit tests with a recorder
[defects_stable-2026.2.py](_rewrite/defects_stable-2026.2.py)targeted cases on the same stub
[static_scan_stable-2026.2.py](_rewrite/static_scan_stable-2026.2.py)return values of command.Command, unknown parsed_args attributes
[compare_manila_stable-2026.2.py](_rewrite/compare_manila_stable-2026.2.py)static comparison with the Manila code
[build_contract_stable-2026.2.py](_rewrite/build_contract_stable-2026.2.py)merges everything

The generic rewrite_pack.py and conformance_capture.py were not used: they expect a service, or functional tests running in-process. The functional tests here need a live cloud (manilaclient/tests/functional/osc/base.py:24).

Versions: python-manilaclient 6.3.1.dev2 at this commit (the installed copy was compared with the clone: identical), python-openstackclient 10.3.1, osc-lib 4.7.0, cliff 4.16.0, keystoneauth1 5.17.0, openstacksdk 4.20.0, Python 3.13.

To regenerate (HOME set to an empty directory, OS_* unset):

python -I -B _rewrite/extract_plugin_stable-2026.2.py <repo> python-manilaclient manilaclient _rewrite
python -I -B _rewrite/manager_routes_stable-2026.2.py <repo> <mr.json>
python -I -B _rewrite/probe_plugin_stable-2026.2.py _rewrite/cli-commands_stable-2026.2.json <dir> --mode 404      # then 200, shaped
PROBE_ALL_OPTIONS=1 python -I -B _rewrite/probe_plugin_stable-2026.2.py _rewrite/cli-commands_stable-2026.2.json <dir> --mode shaped
python -I -B _rewrite/probe_plugin_stable-2026.2.py _rewrite/cli-commands_stable-2026.2.json <dir> --mode shaped --api-version 2.N   # for N in 0..98
python -I -B _rewrite/capture_plugin_stable-2026.2.py <repo> _rewrite/cli-commands_stable-2026.2.json <run-N>/c.json <repo>/manilaclient/tests/unit/osc/v2 <repo>   # twice
python -I -B _rewrite/defects_stable-2026.2.py _rewrite/probe_plugin_stable-2026.2.py <defects.json>
python -I -B _rewrite/build_contract_stable-2026.2.py . <gen-dir> <404> <200> <shaped> <all-options> <run-1> <run-2> <mr.json> <sweep-dir> <defects.json>

2. What the plugin requires from a service

#RequirementWhyEvidence
1The Keystone catalog holds shared-file-system or sharev2, with a public endpointendpoint lookup[observed] catalog-*, manilaclient/osc/plugin.py:41-55
2The endpoint path has /vN, and the path before it answers version discovery_get_base_url; without /vN, discovery goes to the host root[observed] override-unversioned, manilaclient/common/httpclient.py:70-83
3Discovery answers {"versions": [{"status": "CURRENT", "version": "2.x", "min_version": "2.y", ...}]}, with a status 2xx. Authenticated requests are sent to itwithout it, every command fails[observed] disc-*, manilaclient/api_versions.py:244-261; shape of manila:manila/api/versions.py:42
4X-OpenStack-Manila-API-Version selects the microversion, from the minimum up to the maximum announced. OpenStack-API-Version: shared-file-system is also sent, and can be ignoredthe client asks 2.98 by default and pins lower values on request[observed] headers-sample, manila:manila/api/openstack/wsgi.py:226
5Announcing a microversion means implementing it86 gate sites and every wraps variant trust itmanilaclient/api_versions.py:444-460
6Accept X-OpenStack-Manila-API-Experimental: true on any route, sticky once senta later request in the same process keeps it[observed], manilaclient/api_versions.py:413-414
7GET /<resource>/{name} answers 404 for an unknown ID or nametriggers the name lookup; for osc_lib lookups, any other error also falls to the listing[observed], osc-lib:osc_lib/utils/__init__.py:234
8Lists accept all_tenants=1, all_tenants=True, is_public=True, and honour is_soft_deleted=Truea service that ignores the filter breaks name lookup from 2.69[observed] name-lookup, name-lookup-nofilter
9Lists always hold the collection key, also when empty; shows hold the resource keyotherwise a bare key name is printed[observed] list-no-key, show-no-key
10Answers hold the fields of 02 § 5: size, required_extra_specs, extra_specs, is_public, backend_details, share_type_access:is_publicclient code reads them[observed]
11Error bodies: JSON, Content-Type: application/json, first key holds message; never {}the message shown to users, and {} crashes from_response[observed] error-*, manilaclient/common/apiclient/exceptions.py:505
12x-openstack-request-id on errorsprinted as Request-IDmanilaclient/common/apiclient/exceptions.py:485
13POST /<resource>/{id}/action takes the action in the body key; both os- names below 2.7 and plain namesthe managers can send 35 action names, of which 18 were seen through commands at 2.98[observed], [manila-route-check](_rewrite/manila-route-check_stable-2026.2.md)
14allow_access answers {"access": {...}} with idthe client reads it directly (manilaclient/v2/shares.py:968)[observed]
15metadata, extra_specs and group_specs filters arrive as a Python dict repr in the query string, and must be parsed as such{'k': 'v'}, URL-encoded[observed] access-list-filters, manila:manila/api/v2/shares.py:1526
16Query values are never False, 0 or empty, since the client drops thema filter on is_public=False is impossible through this clientmanilaclient/base.py:229
17migration_check always answers a non-empty bodyan empty one starts a migration[observed] server-migration-check-empty
18Strings "True" and "False" are accepted for the boolean fields of migrationsthe options are choices=['True', 'False'][observed], manilaclient/osc/v2/share_servers.py:684-687
19reset_task_state with null is accepted for admins, and a 403 for others is expected on every share setdefect 1[observed], [inferred] for the 403
20Routes keep their odd names: /share_instances, /shares/{id}/export_locations, /snapshot-instances, /types/{id}/share_type_access, /share-access-rulesno aliasing in the client[observed]

3. Microversion coverage

  • Range: client 2.0 to 2.98. Manila on this branch goes to 2.99 (manila:manila/api/openstack/api_version_request.py:229). The field snapshot_inherit_share_access_support of 2.99 is not requested by this client, and the CLI refuses --os-share-api-version 2.99.
  • Gates: 86 sites in the commands, on 43 microversions. Their list is in client-contract_stable-2026.2.json.
  • Variants: 288 across 187 manager methods.
  • Minimum for a useful client: shares, types, and snapshots mostly work from 2.0 to 2.9. Backups need 2.80, locks 2.81, transfers 2.77, QoS 2.94, groups 2.31, snapshot export locations 2.32. The version sweep gives this per command.

OpenKeel: openkeel-chart, openkeel-lab and the OpenKeel Manila service were not read or contacted for this pack. Whether a Go Manila exists at the announced version must be checked separately, starting from [api-surface_stable-2026.2.md](_rewrite/api-surface_stable-2026.2.md).

4. For a rewrite of the plugin itself

TopicRuleSource
Namesshare_access_create → share access create; scripts depend on these and on argument namespyproject.toml:60
Aliasesmany options have two spellings, such as --resource-action and --resource_actionmanilaclient/osc/v2/resource_locks.py:70
Name resolutionkeep the behaviour, not the two implementations03 § 5
Exit codes0, 1, 2 as in 02 § 1; do not copy the defects of its § 4
Formatstable strings differ from JSON structuresmanilaclient/osc/v2/share.py:828-832
Plugin forma non-Python rewrite cannot load through OSC entry points; it would replace openstack sharepyproject.toml:36-37

Recommendation: keep the Python plugin, make the service compatible, and propose defects 1 to 12 upstream. The surface is 154 commands with 288 manager variants, and the OSC shell cannot host a non-Python plugin.

5. Conformance: what was proved

EvidenceResultWhat it provesWhat it does not prove
Parser dump154 of 154 classes imported, 0 failuresthe catalogue is complete for the declared entry points
OSC unit tests with recorder573 of 573 pass, 573 vectors, in two independent runsargv → manager calls for 153 of 154 classesanything about a service: the clients are mocks
Comparison of the two runs573 of 573 vectors have the same test, call sequence and statussequences are deterministicargument values were not compared
Other unit tests of the repository1,433 tests in total, including those 573: 0 failures, 0 errors, 0 skippedmanager code against a fake HTTP layer (manilaclient/tests/unit/fakes.py)a real service
Wire probe, 404 and {}154 of 154 commands send at least one request; 269 and 286 requestsroutes, headers and negotiation up to the first failurelater steps
Wire probe, shaped154 of 154 send a request, 154 exit 0; 348 requeststhe client's path to the end, against a body it acceptsthat Manila answers this way
All options149 of 154 send a request; 141 exit 0; of the 13 others, 2 are defects (8, 9), and the rest are synthetic values refused locallyquery and body keyscombinations
Version sweep99 versions × 154 commandslowest working version; defects 4, 6, 7versions on a real service
Route check134 of 139 routes and 35 of 35 actions found in Manila's declarationsnames agreebodies, status codes, behaviour
Functional testsnot run: they need a cloud

The usual "replay is mandatory" bar is not met for service behaviour. No vector was replayed against a rewrite, and nothing was compared with a running Python Manila. The mocks also hide real defects: defects 1 to 12 of 02 § 4 all pass the unit tests, except defect 11, which has none.

To close the gap: run the probe's argv from wire-probe-shaped_stable-2026.2.json against a devstack on stable/2026.2 with real resources, or run manilaclient/tests/functional/osc against both implementations.

Class without a unit vector (1): manilaclient.osc.v2.share:RestoreShare.

6. Gaps found while building the pack

  • 55 of the 232 operations are static only. Most are variants below 2.98, such as /os-services, or manager methods no command reaches.
  • 8 static client methods do not resolve, because they are Resource methods or to_dict: set_keys, unset_keys, to_dict, share_access_rules.list. The wire probe shows the first ones, for example POST /types/{id}/extra_specs.
  • manager_routes calls each method with its required arguments only, so optional body fields are not all in manager-routes. wire-probe-all-options covers them at 2.98 only.
  • The extractor's static call list follows variables assigned from find_resource and get, not attributes passed through helpers. The unit vectors cover that.
  • The route check uses Manila's generated OpenAPI. Its 5 misses are /services/enable and /services/disable, which it stores as /services/{id}; /os-services/* likewise; and /types/{id}/os-share-type-access below 2.7.

7. Non-functional requirements

  • Latency: each invocation authenticates and reads discovery. Keystone and GET / of Manila are on the hot path.
  • Listing cost: a name lookup lists the collection four times for shares from 2.69, twice elsewhere. A service must keep GET /shares/detail fast with all_tenants=1.
  • Polling: 1 GET every 5 s per waiting command, without a limit for status waits.

8. Checklist for an OpenKeel Manila service

  • [ ] Catalog entry shared-file-system or sharev2, with public, and /v2 in the URL path.
  • [ ] Version document at the base URL, with a CURRENT entry, version, min_version.
  • [ ] X-OpenStack-Manila-API-Version honoured; every announced microversion implemented.
  • [ ] Experimental header accepted on every route.
  • [ ] GET /<resource>/{name} gives 404; all_tenants, is_public, is_soft_deleted honoured on lists.
  • [ ] Empty lists keep their key; required fields present; errors in {"<key>": {"code": n, "message": "…"}}, never {}.
  • [ ] Dict filters parsed from a Python repr.
  • [ ] migration_check never answers empty.
  • [ ] reset_task_state with null handled as Manila does, since every share set sends it.
  • [ ] Each operation of api-surface_stable-2026.2.md implemented, or its absence documented.
  • [ ] The argv of wire-probe-shaped_stable-2026.2.json, pointed at the service with real resources, gives the same routes and exit 0.

Source: reverse-spec-python-manilaclient/07-rewrite-pack_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.