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

Reverse-spec · Clients

osc-placement — Rewrite pack

Client contract (rewrite pack) — osc-placement → Placement — branch stable/2026.2

Branch stable/2026.2 @ 402610a21872 — repository https://opendev.org/openstack/osc-placement — analysed on 2026-10-28 Placement reference: ../reverse-spec-placement/ (branch stable/2026.2 @ f4a89d3803cb), used by the functional tests through placement.tests.functional.fixtures.placement.PlacementFixture

The goal here is not to rewrite the CLI. It is to state what osc-placement requires from Placement, so that a rewrite (OpenKeel Placement, in Go) keeps the openstack resource provider|resource class|trait|allocation candidate … commands working unchanged, and to provide a replayable suite that proves it.

1. Contents of _rewrite/

FileContent
[client-contract_stable-2026.2.md](_rewrite/client-contract_stable-2026.2.md), [client-contract_stable-2026.2.json](_rewrite/client-contract_stable-2026.2.json)for each of the 31 commands: entry point, number of runs in the tests (and failures), Placement operations called, microversions and statuses observed, request examples; plus the Placement operations the CLI never calls
[exchanges_stable-2026.2.json](_rewrite/exchanges_stable-2026.2.json)928 invocations of the openstack CLI (the command text and the test that ran it), and the 1,381 HTTP exchanges each one caused (request, headers, body, response)
[response-schemas_stable-2026.2.json](_rewrite/response-schemas_stable-2026.2.json), [response-schemas_stable-2026.2.md](_rewrite/response-schemas_stable-2026.2.md)Placement response schemas observed through the CLI (32 operations, 57 operation × status pairs)
[observed-errors_stable-2026.2.md](_rewrite/observed-errors_stable-2026.2.md)the 29 error messages received by the CLI
[replay-report_stable-2026.2.json](_rewrite/replay-report_stable-2026.2.json)replay of the 928 invocations against a fresh Placement

How it was produced: conformance_capture.py http, with the placement adapter for responses, over the 9 functional test modules of osc-placement (160 tests, 0 failures).

  • --wrap placement.deploy:loadapp records every HTTP exchange.
  • --record-calls osc_placement.tests.functional.base:BaseTestCase.openstack records every CLI command and links each one to its exchanges.

Versions: osc-lib 4.7.0, python-openstackclient 10.3.1, keystoneauth1 5.17.0.

2. What osc-placement requires from Placement

RequirementWhySource
GET / answers **406 with errors[0].max_version** when the requested microversion is too highdefault negotiation with 1.29osc_placement/http.py:69-86
every 4xx carries {"errors": [{"detail": "…"}]}; the useful message on the last line of detailturned into an osc-lib exception; otherwise KeyErrorosc_placement/http.py:33-46
POST /resource_providers returns a **Location** header that can be followedresource provider create reads the provider through Locationosc_placement/resources/resource_provider.py:65-66
GET /allocations/{consumer} on an unknown consumer: 200 with {"allocations": {}} and consumer_generation null (1.28+)allocation set reads the generation before writing; a 404 would break the first creationosc_placement/resources/allocation.py:120-125
PUT /allocations/{c} accepts allocations: {} with consumer_generation (1.28+)allocation unset of everythingosc_placement/resources/allocation.py:274-284
DELETE /resource_providers/{uuid}/inventories (1.5+)inventory delete without a classosc_placement/resources/inventory.py:339-347
repeated query parameters (required, member_of, resourcesN…) and the syntaxes in:, !lists and candidatesosc_placement/resources/common.py:41-58, osc_placement/resources/allocation_candidate.py:179-250
microversions used: 1.0 to 1.29 (all except 1.13, 1.15, 1.20, 1.23, 1.26, 1.27, unused), 1.38 and 1.39SUPPORTED_MICROVERSIONSosc_placement/version.py:21-55

Placement operations never called by the CLI: POST /allocations (multiple consumers), POST /reshaper, POST /resource_providers/{uuid}/inventories. GET / (negotiation) is not exercised by the functional tests, because they all pass an explicit version (osc_placement/tests/functional/base.py). A Placement rewrite can therefore be validated by this suite without those operations, but it must be validated for them by other means (gabbi, Nova).

3. Conformance suite and proof

Each functional test starts from an empty Placement and builds its data only through CLI commands. Its sequence of invocations is therefore a self-contained scenario, with no database seed. The only variable is the UUIDs that the server generates (providers created without --uuid).

Replay (conformance_capture.py replay --invocations …, adapter adapters/osc_placement.py):

  1. For each recorded test, run a test case derived from the original class: same setUp, so a fresh Placement and the same VERSION.
  2. Run its CLI commands again, in order, with the same options.
  3. After each command, pair the UUIDs of the recorded responses with those of the replayed responses, by walking both JSON bodies, and substitute them in the following commands.
  4. Compare, command by command, the outcome (success or exception class) and the sequence of (method, route, status).

Result against the Python Placement: 928/928 invocations identical, 159 tests, 0 test failing to run.

To validate OpenKeel Placement: run the same replay with the CLI pointed at the Go endpoint instead of the in-process fixture (the PlacementFixture of the test class replaced by a fixture that resets the Go database and returns its URL). The expected outcomes and sequences are those of exchanges_stable-2026.2.json. Then compare the response bodies against response-schemas_stable-2026.2.json, and the error messages (last line of detail) against observed-errors_stable-2026.2.md.

4. Gaps

GapImpact
negotiation GET / → 406 not covered by the functional teststest it separately (unit test osc_placement/tests/unit/test_http.py, or by hand with --os-placement-api-version 1 against a server < 1.29)
replay compares statuses and routes, not bodies field by fieldbody comparison through the response schemas; UUIDs and dates differ by nature
the Go replay needs a fixture that resets the database for every testa reset endpoint reserved for tests, or one database per test
POST /allocations, POST /reshaper, POST …/inventories not usedout of scope of this contract

5. Checklist (OpenKeel Placement seen from osc-placement)

  • [ ] 406 with max_version on GET / above the supported microversion
  • [ ] JSON error body errors[0].detail on every 4xx, message on the last line
  • [ ] Location on POST /resource_providers
  • [ ] empty GET /allocations/{c} with null consumer_generation; PUT with empty allocations at 1.28+
  • [ ] the 928 invocations replayed identically (outcome and sequence of method, route and status)
  • [ ] response bodies valid against response-schemas_stable-2026.2.json

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