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

Reverse-spec · Clients

python-heatclient — Interactions

Interactions — python-heatclient — branch stable/2026.2

Branch stable/2026.2 @ 88120f11830d (5.3.0-2-g88120f1) — repository https://opendev.org/openstack/python-heatclient — analysed on 2026-11-01

No database, message bus or cache: the inventory finds 0 RPC calls and 0 triggers. The client talks HTTP to Heat, Keystone and, in one path, Swift, and it reads local files and URLs. [observed] means seen on the local stub, not on a real service.

SystemProtocolLibraryDirectionMode
HeatHTTPS RESTkeystoneauth1 session, adapter SessionClient (heatclient/common/http.py:303)outboundsynchronous, one request at a time
KeystoneHTTPS RESTkeystoneauth1, python-keystoneclient for --projectoutboundsynchronous
SwiftHTTPS RESTpython-swiftclient (heatclient/common/deployment_utils.py:134-152)outboundlegacy shell only, see § 3
Local files, URLsfile:, http(s):, any urllib schemeurllib.request.urlopenread; --output-file writessynchronous

No subprocess call exists in the package, and nothing reads a terminal except the two delete prompts (heatclient/osc/v1/stack.py:753-756, heatclient/osc/v1/snapshot.py:216-220).

1. Heat

Endpoint and session

  • Discovery: Keystone catalog, service_type=orchestration, with the region and interface of OSC (heatclient/osc/plugin.py:38-43). The URL is used as published, normally <heat>/v1/{tenant_id}. Neither GET / nor a version document is requested [observed: 0 hits].
  • Credentials: the OSC session's token. The session re-authenticates once on a 401 (keystoneauth1:keystoneauth1/session.py:1130-1131) [observed, E09].
  • Timeouts and retries: none set here; the session's values apply. No retry on 5xx or 409.
  • Redirects: keystoneauth follows 301, 302, 303, 305, 307 and 308 up to its limit, re-sending the same method and body (keystoneauth1:keystoneauth1/session.py:385, keystoneauth1:keystoneauth1/session.py:1246) [observed].

Headers [observed]

HeaderValueSource
X-Auth-Tokensession tokenkeystoneauth
Acceptapplication/jsonkeystoneauth1:keystoneauth1/adapter.py:671
Content-Typeapplication/json, on every request including GETheatclient/common/http.py:310-311
User-Agentpython-heatclientheatclient/common/http.py:34, heatclient/common/http.py:308
X-Region-Name--os-region-name, when setheatclient/common/http.py:363-365

Request bodies are always jsonutils.dumps of a Python value (heatclient/common/http.py:313-314).

Routes

The full list, with sources and users, is in [_rewrite/api-surface_stable-2026.2.md](_rewrite/api-surface_stable-2026.2.md) (57 operations). Each command's list is in [_rewrite/client-contract_stable-2026.2.json](_rewrite/client-contract_stable-2026.2.json).

Name resolution

FormSendsUsed bySource
Direct<METHOD> /stacks/{typed}[/resources|events|template|actions|environment|files], follows Heat's 302stacks.get, update, delete, template, environment, files; actions.*; resources.list; events.listheatclient/v1/stacks.py:182-185, heatclient/v1/stacks.py:298, heatclient/v1/actions.py:39
ResolvedGET /stacks/{typed} with redirect off; then /stacks/{after /stacks/ in Location}/…everything else on a stackheatclient/v1/stacks.py:102-116

Heat answers 302 on /stacks/{name} for every method, and on those sub-paths for GET (POST except environment and files), keeping the query (heat:heat/api/openstack/v1/__init__.py:155-183, heat:heat/api/openstack/v1/stacks.py:426-442). The stub copies this. **The query must survive in Location**: the client sends ?resolve_outputs=False on the first request only, and keystoneauth just follows the header [observed: the stub's fixed Location dropped it in G01].

Path values: the stack manager inserts stack_id, snapshot_id and output_key unquoted (heatclient/v1/stacks.py:248, heatclient/v1/stacks.py:281). The resource and event managers quote them (heatclient/v1/resources.py:80-82, heatclient/v1/events.py:82-85).

Query parameters [observed]

Values that are falsy are dropped by the managers (heatclient/v1/stacks.py:153-155), except for the two with_* flags, always sent.

CommandOptionParameter
stack list--property k=vk=v, directly in the query
--limit, --markerlimit, marker
--tags ttags=t
--tags t --tag-mode any / not / not-anytags_any, not_tags, not_tags_any (heatclient/osc/v1/stack.py:678-690)
--all-projects, or **--long**global_tenant=True
--deleted, --hidden, --nestedshow_deleted=True, show_hidden=True, show_nested=True
--projecttenant=<id>, after a Keystone lookup [read: heatclient/osc/v1/stack.py:705-706; the probe stopped at the lookup]
stack show--no-resolve-outputsresolve_outputs=False
stack update --dry-run--show-nestedshow_nested=True
stack event listalwayssort_dir=asc or desc
--sort k[:dir]sort_keys=k (:dir alone sends none)
--limit, --marker, --nested-depth, --filter k=vlimit, marker, nested_depth, k=v
stack resource list-n, --long, --filter k=vnested_depth, with_detail=True, k=v
stack resource show--with-attr a repeatedwith_attr=a&with_attr=b
orchestration resource type list--long, --filterwith_description=True or False, k=v
orchestration resource type show--long; --template-typewith_description=…; template_type=hot on /template
orchestration template function list--with_conditionswith_condition_func=True or False
orchestration template validate--show-nested, --ignore-errorsshow_nested=True, ignore_errors=99001,99002 in the query, not the body
software config list--limit, --markerlimit, marker
software deployment list--serverserver_id

Request bodies [observed]

POST /stacks
{"stack_name": "x", "disable_rollback": true, "parameters": {"a": "b"},
 "template": {"heat_template_version": "2013-05-23", "resources": {}},
 "files": {}, "environment": {}, "tags": "t1,t2", "timeout_mins": 5}
RequestBody
POST /stacks/previewas POST /stacks
PUT /stacks/{x}parameters, template, files, environment; optional environment_files, files_container, tags, timeout_mins, clear_parameters, disable_rollback, converge
PATCH /stacks/{x} (--existing)same, with template possibly null
POST /stacks (adopt)stack_name, disable_rollback, adopt_stack_data as a string, parameters, files, environment, timeout
POST /stacks/{x}/actionsone key with a null value: suspend, resume, check, cancel_update, cancel_without_rollback
POST /validatetemplate, parameters, files, environment; optional environment_files, files_container
POST …/signalthe --data dict; JSON null without data; {"unset_hook": "pre-create"} for hooks
PATCH …/resources/{r}mark_unhealthy, resource_status_reason
POST …/snapshots{} or {"name": "n"}
POST /software_configsgroup, config, inputs, outputs, options, name
POST /software_deploymentsconfig_id, server_id, action, status: IN_PROGRESS

tags is a comma-separated string, not a list. Environment files appear in files under their absolute file:// URL, so local paths reach the server [observed in the wire probe variant with -e].

Responses read

The full list of keys is in 07 § 2. Rule: the body counts only if Content-Type contains application/json (heatclient/common/utils.py:436-445). List routes parse JSON regardless (heatclient/common/base.py:113).

Error handling

See 02. No retry; every error ends the command, or the current stack on multi-stack delete.

2. Keystone

  • Authentication: by OSC and keystoneauth, not by this package. The plugin only receives the session.
  • **stack list --project**: identity_common.find_project (heatclient/osc/v1/stack.py:586-590) sends GET /v3/projects/{project}, then GET /v3/projects?name= [observed].

3. Swift (TempURL for deployments)

software deployment create with TEMP_URL_SIGNAL creates a URL that the server calls to signal its result (heatclient/common/deployment_utils.py:94-115):

  1. PUT the container, named <deployment>-<uuid>;
  2. HEAD the account, and POST a random x-account-meta-temp-url-key if none exists;
  3. compute an HMAC TempURL for PUT, valid for --timeout minutes;
  4. PUT an empty object;
  5. put the URL in the derived config as input deploy_signal_id.

Seen only through heat deployment-create [observed, L02]. **openstack software deployment create fails before step 1** (defect 1). --container exists on the OSC command, but create_temp_url is called without it (heatclient/common/deployment_utils.py:131), so the option has no effect [read in the code].

4. Local files and URLs

ReadFunctionArgumentsNote
templateheatclient/common/template_utils.py:70-tfile:// or any URL
child filesheatclient/common/utils.py:412get_file, type: *.yaml in templatesabsolute URL used as key in files
environmentheatclient/common/template_utils.py:323-e
parameter fileheatclient/common/utils.py:412--parameter-filerelative to the template's directory
adopt fileheatclient/osc/v1/stack.py:876--adopt-filesent as a string
signal dataheatclient/osc/v1/resource.py:238--data-file
config filesheatclient/osc/v1/software_config.py:158, heatclient/osc/v1/software_config.py:168--definition-file, --config-file
writtenheatclient/osc/v1/stack.py:934, heatclient/osc/v1/stack.py:978--output-filemode w, overwrites

request.urlopen is called with no timeout, so a slow URL can block a command [read in the code]. See defect 13 for what template contents can trigger.

5. Python API

The commands use the managers of heatclient/v1/ directly, and third-party code can do the same through heatclient.client.Client('1', session=…) (heatclient/client.py:17-21). Only http_client.request bypasses managers, in authenticated_fetcher (heatclient/common/http.py:40-50).

Source: reverse-spec-python-heatclient/03-interactions_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.