Interactions — heat — branch stable/2026.2
Branch
stable/2026.2@5872396e548c(27.0.1) — repository https://opendev.org/openstack/heat — analysed on 2026-10-29
| System | Protocol | Library | Configuration | Used by |
|---|---|---|---|---|
| RabbitMQ (or any oslo.messaging driver) | AMQP 0-9-1 RPC + notifications | oslo.messaging (heat/common/messaging.py) | [DEFAULT]/transport_url, [oslo_messaging_*], rpc_response_timeout | heat-api, heat-api-cfn → heat-engine; heat-engine ↔ heat-engine |
| MariaDB / MySQL / PostgreSQL | SQL | oslo.db + SQLAlchemy 2 (heat/db/api.py) | [database]/connection, db_max_retries… | heat-engine, heat-manage (the APIs never open the database) |
| Keystone | REST | keystonemiddleware, keystoneauth1, python-keystoneclient (heat/engine/clients/os/keystone/heat_keystoneclient.py) | [keystone_authtoken], [trustee], [clients_keystone], [ec2authtoken], stack_user_domain_*, stack_domain_admin* | all |
| Other OpenStack services | REST | one client plugin per service (setup.cfg heat.clients, heat/engine/clients/os/*.py) | [clients], [clients_<service>], region_name_for_services | heat-engine (resource plugins) |
| Swift (signals, files, metadata) | REST / TempURL | python-swiftclient | catalog | heat-engine |
| Any HTTP(S) server | HTTP GET | requests (heat/common/urlfetch.py:38-84) | template_fetch_timeout, max_template_size | heat-api (template_url), heat-engine (get_file/type: http://… template resources) |
| Memcached / Redis | cache protocol | oslo.cache | [cache], [constraint_validation_cache], [service_extension_cache], [resource_finder_cache] | heat-engine (constraint and lookup caches, disabled unless [cache]/enabled) |
1. RabbitMQ
1.1 Topology
Transport defaults set the control exchange to **heat** (heat/common/messaging.py:83). All RPC uses the oslo.messaging topic exchange; the JSON payload serialiser wraps a context serialiser that sends RequestContext.to_dict() (heat/common/messaging.py:29-61, heat/common/context.py:140-154). Remote exceptions are re-raised on the caller side only for classes of heat.common.exception (allowed_remote_exmods, heat/common/messaging.py:84); anything else arrives as RemoteError.
| Topic | Server | Consumers | Producers | Version (server) | Pattern |
|---|---|---|---|---|---|
engine | [DEFAULT]/host of each engine process; clients send without server → any engine | EngineService (heat/engine/service.py:462-468) | heat-api, heat-api-cfn, heat-engine (nested stacks) | 1.36 | call (one cast variant, never used) |
engine_worker | engine id | WorkerService (heat/engine/worker.py:85-97) | heat-engine | 1.9 | cast (to any engine, or to one engine for cancel_check_resource) |
heat-engine-listener | engine id | EngineListener (heat/engine/service.py:369-374) | heat-engine | 1.0 | call addressed to one engine |
Notifications are sent on the notification transport with publisher_id = orchestration.<host> (or [DEFAULT]/default_publisher_id) and event_type = orchestration.<…> (heat/engine/notification/__init__.py, notify).
1.2 RPC methods (API → engine, topic engine)
Generated from [_rewrite/rpc-contract_stable-2026.2.json](_rewrite/rpc-contract_stable-2026.2.json) (_rewrite/tools/heat_rpc.py). Every server method except get_revision, list_resource_types, list_template_versions, list_template_functions, resource_schema and generate_template is wrapped by @context.request_context, which turns a HeatException into an expected remote error, i.e. no server-side traceback log (heat/common/context.py:362-369). The reply is the method's return value, JSON-serialised.
| Method | Mode | Version | Arguments | Timeout | Client | Server |
|---|---|---|---|---|---|---|
abandon_stack | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:467 | heat/engine/service.py:1618 |
authenticated_to_backend | call | 1.0 | — | rpc_response_timeout | heat/rpc/client.py:409 | heat/engine/service.py:1391 |
check_software_deployment | call | 1.27 | deployment_id, timeout | timeout argument | heat/rpc/client.py:725 | heat/engine/service.py:2415 |
count_stacks | call | 1.33 | filters, show_deleted, show_nested, show_hidden, tags, tags_any, not_tags, not_tags_any | rpc_response_timeout | heat/rpc/client.py:192 | heat/engine/service.py:684 |
create_software_config | call | 1.0 | group, name, config, inputs, outputs, options | rpc_response_timeout | heat/rpc/client.py:700 | heat/engine/service.py:2375 |
create_software_deployment | call | 1.0 | server_id, config_id, deployment_id, input_values, action, status, status_reason, stack_user_project_id | rpc_response_timeout | heat/rpc/client.py:736 | heat/engine/service.py:2420 |
create_stack | call | 1.36 | stack_name, template, params, files, environment_files, files_container, args, owner_id, nested_depth, user_creds_id, stack_user_project_id, parent_resource_name, template_id | rpc_response_timeout | heat/rpc/client.py:282 | heat/engine/service.py:858 |
delete_snapshot | call | 1.0 | stack_identity, snapshot_id | rpc_response_timeout | heat/rpc/client.py:788 | heat/engine/service.py:2266 |
delete_software_config | call | 1.0 | config_id | rpc_response_timeout | heat/rpc/client.py:709 | heat/engine/service.py:2396 |
delete_software_deployment | call | 1.0 | deployment_id | rpc_response_timeout | heat/rpc/client.py:765 | heat/engine/service.py:2469 |
delete_stack | call (cast when cast=True) | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:457 | heat/engine/service.py:1489 |
describe_stack_resource | call | 1.2 | stack_identity, resource_name, with_attr | rpc_response_timeout | heat/rpc/client.py:576 | heat/engine/service.py:1935 |
describe_stack_resources | call | 1.0 | stack_identity, resource_name | rpc_response_timeout | heat/rpc/client.py:601 | heat/engine/service.py:2108 |
export_stack | call | 1.22 | stack_identity | rpc_response_timeout | heat/rpc/client.py:821 | heat/engine/service.py:1606 |
find_physical_resource | call | 1.0 | physical_resource_id | rpc_response_timeout | heat/rpc/client.py:589 | heat/engine/service.py:2081 |
generate_template | call | 1.9 | type_name, template_type | rpc_response_timeout | heat/rpc/client.py:535 | heat/engine/service.py:1806 |
get_environment | call | 1.28 | stack_identity | rpc_response_timeout | heat/rpc/client.py:428 | heat/engine/service.py:1410 |
get_files | call | 1.32 | stack_identity | rpc_response_timeout | heat/rpc/client.py:441 | heat/engine/service.py:1430 |
get_revision | call | 1.0 | — | rpc_response_timeout | heat/rpc/client.py:682 | heat/engine/service.py:623 |
get_template | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:417 | heat/engine/service.py:1400 |
identify_stack | call | 1.0 | stack_name | rpc_response_timeout | heat/rpc/client.py:122 | heat/engine/service.py:539 |
list_events | call | 1.31 | stack_identity, filters, limit, marker, sort_keys, sort_dir, nested_depth | rpc_response_timeout | heat/rpc/client.py:558 | heat/engine/service.py:1829 |
list_outputs | call | 1.19 | stack_identity | rpc_response_timeout | heat/rpc/client.py:805 | heat/engine/service.py:1443 |
list_resource_types | call | 1.30 | support_status, type_name, heat_version, with_description | rpc_response_timeout | heat/rpc/client.py:485 | heat/engine/service.py:1663 |
list_services | call | 1.4 | — | rpc_response_timeout | heat/rpc/client.py:802 | heat/engine/service.py:2474 |
list_software_configs | call | 1.33 | limit, marker | rpc_response_timeout | heat/rpc/client.py:689 | heat/engine/service.py:2364 |
list_software_deployments | call | 1.0 | server_id | rpc_response_timeout | heat/rpc/client.py:713 | heat/engine/service.py:2400 |
list_stack_resources | call | 1.25 | stack_identity, nested_depth, with_detail, filters | rpc_response_timeout | heat/rpc/client.py:616 | heat/engine/service.py:2118 |
list_stacks | call | 1.33 | limit, sort_keys, marker, sort_dir, filters, show_deleted, show_nested, show_hidden, tags, tags_any, not_tags, not_tags_any | rpc_response_timeout | heat/rpc/client.py:157 | heat/engine/service.py:627 |
list_template_functions | call | 1.35 | template_version, with_condition | rpc_response_timeout | heat/rpc/client.py:510 | heat/engine/service.py:1721 |
list_template_versions | call | 1.11 | — | rpc_response_timeout | heat/rpc/client.py:498 | heat/engine/service.py:1685 |
metadata_software_deployments | call | 1.0 | server_id | rpc_response_timeout | heat/rpc/client.py:717 | heat/engine/service.py:2405 |
migrate_convergence_1 | call | 1.34 | stack_id | rpc_response_timeout | heat/rpc/client.py:832 | heat/engine/service.py:2480 |
preview_stack | call | 1.36 | stack_name, template, params, files, environment_files, files_container, args | rpc_response_timeout | heat/rpc/client.py:234 | heat/engine/service.py:823 |
preview_update_stack | call | 1.36 | stack_identity, template, params, files, environment_files, files_container, args | rpc_response_timeout | heat/rpc/client.py:362 | heat/engine/service.py:1131 |
resource_mark_unhealthy | call | 1.26 | stack_identity, resource_name, mark_unhealthy, resource_status_reason | rpc_response_timeout | heat/rpc/client.py:672 | heat/engine/service.py:2007 |
resource_schema | call | 1.30 | type_name, with_description | rpc_response_timeout | heat/rpc/client.py:522 | heat/engine/service.py:1744 |
resource_signal | call | 1.3 | stack_identity, resource_name, details, sync_call | rpc_response_timeout | heat/rpc/client.py:654 | heat/engine/service.py:1953 |
show_output | call | 1.19 | stack_identity, output_key | rpc_response_timeout | heat/rpc/client.py:810 | heat/engine/service.py:1456 |
show_snapshot | call | 1.0 | stack_identity, snapshot_id | rpc_response_timeout | heat/rpc/client.py:783 | heat/engine/service.py:2259 |
show_software_config | call | 1.0 | config_id | rpc_response_timeout | heat/rpc/client.py:685 | heat/engine/service.py:2360 |
show_software_deployment | call | 1.0 | deployment_id | rpc_response_timeout | heat/rpc/client.py:721 | heat/engine/service.py:2410 |
show_stack | call | 1.20 | stack_identity, resolve_outputs | rpc_response_timeout | heat/rpc/client.py:211 | heat/engine/service.py:585 |
signal_software_deployment | call | 1.6 | deployment_id, details, updated_at | rpc_response_timeout | heat/rpc/client.py:770 | heat/engine/service.py:2445 |
stack_cancel_update | call | 1.14 | stack_identity, cancel_with_rollback | rpc_response_timeout | heat/rpc/client.py:638 | heat/engine/service.py:1241 |
stack_check | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:633 | heat/engine/service.py:2307 |
stack_list_snapshots | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:793 | heat/engine/service.py:2353 |
stack_restore | call | 1.0 | stack_identity, snapshot_id | rpc_response_timeout | heat/rpc/client.py:797 | heat/engine/service.py:2325 |
stack_resume | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:629 | heat/engine/service.py:2170 |
stack_snapshot | call | 1.0 | stack_identity, name | rpc_response_timeout | heat/rpc/client.py:778 | heat/engine/service.py:2191 |
stack_suspend | call | 1.0 | stack_identity | rpc_response_timeout | heat/rpc/client.py:625 | heat/engine/service.py:2149 |
update_software_deployment | call | 1.5 | deployment_id, config_id, input_values, output_values, action, status, status_reason, updated_at | rpc_response_timeout | heat/rpc/client.py:752 | heat/engine/service.py:2454 |
update_stack | call | 1.36 | stack_identity, template, params, files, environment_files, files_container, args, template_id | rpc_response_timeout | heat/rpc/client.py:330 | heat/engine/service.py:1065 |
validate_template | call | 1.36 | template, params, files, show_nested, environment_files, files_container, ignorable_errors | rpc_response_timeout | heat/rpc/client.py:390 | heat/engine/service.py:1315 |
stack_identity is the dict form of HeatIdentifier: {"tenant": "<project id>", "stack_name": "s", "stack_id": "<uuid>", "path": ""} (heat/common/identifier.py:30-119). args is the dict of the API body minus the known keys (heat/api/openstack/v1/stacks.py:164-167): timeout_mins, disable_rollback, adopt_stack_data, tags, existing, clear_parameters, converge (heat/rpc/api.py:17-31).
validate_template returns either the validation result or {"Error": "<message>"}; for "No Template provided." and invalid ignore_errors codes it **returns a webob.exc.HTTPBadRequest object** through RPC (heat/engine/service.py:1337-1347), which the JSON serialiser turns into a string — a quirk the API never sees in practice because it always sends a template.
1.3 Engine ↔ engine
| Method | Topic / server | Mode | Version | Arguments | Timeout | Client | Server |
|---|---|---|---|---|---|---|---|
check_resource | engine_worker, any engine | cast | 1.9 | resource_id, current_traversal, data, is_update, adopt_stack_data, converge, skip_propagate, accumulated_failures, node_type, abandon | — | heat/rpc/worker_client.py:73-87 | heat/engine/worker.py:202 |
check_resource_delete_snapshot | engine_worker, any engine | cast | 1.7 | snapshot_id, resource_name, start_time, is_stack_delete, current_traversal | — | heat/rpc/worker_client.py:58-71 | heat/engine/worker.py:170 |
cancel_check_resource | engine_worker, server = engine id | cast | 1.3 | stack_id | — | heat/rpc/worker_client.py:89-103 | heat/engine/worker.py:369 |
listening | heat-engine-listener, server = engine id | call | 1.0 | — | [DEFAULT]/engine_life_check_timeout (2 s); a MessagingTimeout means "dead" | heat/rpc/listener_client.py:36-48 | heat/engine/service.py:386 |
send | heat-engine-listener, server = engine id | call | 1.0 | stack_identity, message (cancel, cancel_with_rollback) | engine_life_check_timeout | heat/engine/service.py:1295-1298 | heat/engine/service.py:399 |
stop_stack | heat-engine-listener, server = engine id | call | 1.0 | stack_identity | STOP_STACK_TIMEOUT = 30 s (heat/engine/service.py:89) | heat/engine/service.py:1585-1588 | heat/engine/service.py:394 |
check_resource.data is {"input_data": {"tuple:(<id>, True, 'resource')": <node data> | <resource id> | null, ...}} — see § 2.4 and 05 § 2.4. accumulated_failures is {"<resource name>": "<reason>"} or null.
1.4 Sample frame [reconstructed]
check_resource cast as published on the heat exchange, routing key engine_worker (oslo.messaging "message envelope" v2.0; oslo.message is a JSON string, shown decoded):
{"oslo.version": "2.0",
"oslo.message": {
"method": "check_resource",
"args": {"resource_id": 42, "current_traversal": "1d0c…", "is_update": true,
"data": {"input_data": {"tuple:(41, True, 'resource')": {
"id": 41, "name": "a", "uuid": "8b2e…", "reference_id": "a-xyz", "action": "CREATE", "status": "COMPLETE",
"attrs": {"output": "one", "tuple:('show', 'value')": "one"}}}},
"adopt_stack_data": null, "converge": false, "skip_propagate": false,
"accumulated_failures": null, "node_type": "resource", "abandon": false},
"namespace": null, "version": "1.9",
"_context_user_id": "…", "_context_project_id": "…", "_context_auth_token": "gAAAA…",
"_context_auth_token_info": {"token": {"…": "full Keystone token body, catalog included"}},
"_context_trust_id": null, "_context_password": null, "_context_aws_creds": null,
"_context_username": "demo", "_context_project_name": "demo", "_context_roles": ["member", "reader"],
"_context_is_admin": false, "_context_request_id": "req-…", "_context_auth_url": "http://keystone/v3",
"_context_region_name": null, "_context_trustor_user_id": null}}
- The context carries credentials: the user's token and its full Keystone body (
auth_token_info, catalog included) on every message, and the clear-text password whendeferred_auth_method = password(heat/common/context.py:140-154). Workers use this token to call the other services (heat/common/context.py:214-251); only signal handling, scaling and token-refresh paths switch to the stored trust (heat/engine/service.py:1986,heat/engine/stack.py:324-350). RabbitMQ access must be treated as credential access. - A large stack makes large casts: each
check_resourcecarries the resolved attributes of all its predecessors.
1.5 Notifications
| event_type | Priority | Emitted when | Payload | Emitted at |
|---|---|---|---|---|
orchestration.stack.<action>.start | INFO ([DEFAULT]/default_notification_level) | stack state IN_PROGRESS persisted | tenant_id, user_id, user_identity, username, stack_identity, stack_name, state (<ACTION>_<STATUS>), state_reason, create_at, updated_at, tags, description | heat/engine/notification/stack.py:18-39, heat/engine/api.py:402-431 |
orchestration.stack.<action>.end | INFO | COMPLETE | same | same |
orchestration.stack.<action>.error | ERROR | FAILED | same | same |
orchestration.autoscaling.start|end|error | INFO/ERROR | scaling group adjustment | stack payload + adjustment, adjustment_type, capacity, groupname, message | heat/engine/notification/autoscaling.py:18-41 |
2. Database
2.1 Tables
Schema: [_rewrite/schema-sqlite-migrated_stable-2026.2.sql](_rewrite/schema-sqlite-migrated_stable-2026.2.sql) (Alembic head 97b2a986f922, heat/db/migrations/versions/97b2a986f922_make_snapshot_state_aware.py); the ORM models match the migrations (0 differences, [_rewrite/schema-diff_stable-2026.2.md](_rewrite/schema-diff_stable-2026.2.md)). JSON columns use heat.db.types.Json/List: jsonutils.dumps into LONGTEXT on MySQL, TEXT elsewhere; None is stored as the string null (heat/db/types.py:35-65).
| Table | Role | Written by | Read by |
|---|---|---|---|
stack | one row per stack, nested ones included (owner_id); soft delete (deleted_at); current_traversal, current_deps, convergence | create/update/delete, every state change (CAS on current_traversal) | everything |
raw_template | template, environment, legacy files (JSON) | stack store, update (new row per template version) | resource load, show template/environment |
raw_template_files | files dict, shared between template rows (raw_template.files_id) | heat/db/api.py:244 | template load |
stack_tag | tags | stack store | list filters |
stack_lock | legacy engine lock, PK stack_id | heat/db/api.py:1151-1198 | legacy operations |
user_creds | encrypted trust id (or password), trustor, project, auth_url, region_name, decrypt_method | stack store, trust refresh | stored context |
resource | one row per resource version (replacements are new rows); nova_instance = physical id; lock fields engine_id, atomic_key | workers (CAS), signals | everything |
resource_data | per-resource key/value store (redact, decrypt_method): passwords, EC2 keys, signal URLs, hooks, plugin state | plugins | plugins, signals |
resource_properties_data | properties (and resolved attributes) as JSON, optionally encrypted (encrypted) | heat/db/api.py:706-727 | resource load, events |
event | one row per state change; rsrc_prop_data_id, legacy resource_properties (PickleType) | heat/db/api.py:1467-1500 (prunes over max_events_per_stack) | events API |
sync_point | convergence join points, PK (entity_id, traversal_id, is_update) | traversal start, workers (CAS on atomic_key) | workers |
software_config | immutable configs | API | deployments, metadata |
software_deployment | deployments (server_id, values, status) | API, signals | metadata |
snapshot | stack snapshots (data, state) | snapshot/delete | restore |
resource_snapshot | per-resource snapshot data (convergence), migration 88be64598c0f | snapshot traversal | restore, delete |
service | engine heartbeats | heat/engine/service.py:2523-2548 | GET /services |
2.2 Notable statements [reconstructed from the ORM]
-- stack state change in convergence (heat/db/api.py:1003-1028): compare-and-swap on the traversal
UPDATE stack SET action=?, status=?, status_reason=?, updated_at=?
WHERE id=? AND deleted_at IS NULL
AND (tenant=? OR stack_user_project_id=?) -- non-admin contexts only
AND current_traversal=?; -- 0 rows => another traversal took over
-- resource lock acquire / release (heat/db/api.py:536-546, heat/engine/resource.py:2159-2183)
UPDATE resource SET action='UPDATE', status='IN_PROGRESS', engine_id=:me, atomic_key=:n+1, …
WHERE id=:rid AND engine_id IS NULL AND atomic_key=:n;
UPDATE resource SET status='COMPLETE', engine_id=NULL, atomic_key=:n+2, …
WHERE id=:rid AND engine_id=:me AND atomic_key=:n+1;
-- replacement, one transaction (heat/db/api.py:287-309)
INSERT INTO resource (stack_id, name, replaces, action, status, current_template_id, requires, …)
VALUES (?, ?, :old, 'INIT', 'COMPLETE', ?, ?, …);
UPDATE resource SET replaced_by=:new, atomic_key=:n+1 WHERE id=:old AND engine_id IS NULL AND atomic_key=:n;
-- 0 rows => UpdateInProgress, the INSERT is rolled back
-- sync point merge (heat/db/api.py:2120-2135)
SELECT * FROM sync_point WHERE entity_id=? AND traversal_id=? AND is_update=?;
UPDATE sync_point SET input_data=?, extra_data=?, atomic_key=:n+1
WHERE entity_id=? AND traversal_id=? AND is_update=? AND atomic_key=:n;
-- engines holding resource locks of a stack (heat/db/api.py:374-380)
SELECT DISTINCT engine_id FROM resource WHERE stack_id=? AND engine_id IS NOT NULL;
-- legacy stack lock (heat/db/api.py:1151-1198)
INSERT INTO stack_lock (stack_id, engine_id) VALUES (?, ?); -- DBDuplicateEntry => read holder
UPDATE stack_lock SET engine_id=:new WHERE stack_id=? AND engine_id=:old; -- steal
DELETE FROM stack_lock WHERE stack_id=? AND engine_id=?; -- release
- Writers that use
@retry_on_db_errorare retried on deadlock and disconnect ([database]/db_max_retries,heat/db/api.py:143-155). status_reasonis truncated to 65535 UTF-8 bytes on MySQL before writing (heat/db/api.py:100-136);event.resource_status_reasonto 255 characters (heat/db/models.py:228-230).- Stack name uniqueness is not a database constraint:
stack_createinserts, then re-reads the earliest live stack of that name in the project and deletes its own row if it is not the earliest (heat/db/api.py:982-998). heat-manage purge_deleted <age>hard-deletes soft-deleted stacks and their events, resources, templates and credentials in batches (heat/db/api.py:1827-2090).
2.3 Encrypted columns
heat/common/crypt.py:
encrypt()uses Fernet with keybase64(auth_encryption_key[:32])(the key is doubled when shorter than 32 characters) and recordsdecrypt_method = "cryptography_decrypt_v1"(heat/common/crypt.py,encrypt/cryptography_decrypt_v1).- Rows written by older releases may name
oslo_decrypt_v1(AES-256-CBC with the 32-character key, IV prefix, ANSI X9.23 padding, last plaintext byte dropped) orheat_decrypt(AES-CFB, IV prefix): the method name stored in the row is a function name looked up bygetattrat read time (heat/common/crypt.py,decrypt). A rewrite must implement all three to read existing databases; vectors are in [_rewrite/crypt-vectors_stable-2026.2.json](_rewrite/crypt-vectors_stable-2026.2.json). - Encrypted:
user_creds.passwordanduser_creds.trust_id,resource_data.valuewhenredact, hidden template parameters andresource_properties_data.datawhen[DEFAULT]/encrypt_parameters_and_properties(heat/db/api.py:2138-2306,heat-manage update_params).
3. Keystone
| Call | When | Credentials | Defined at |
|---|---|---|---|
token validation (GET /v3/auth/tokens) | every native API request | keystonemiddleware service user ([keystone_authtoken]) | etc/heat/api-paste.ini:69 |
POST /v3/ec2tokens | every CFN API request | [ec2authtoken] adapter | heat/api/aws/ec2token.py:209-222 |
POST /v3/OS-TRUST/trusts (trustor = user, trustee = [trustee] user, impersonation=True, roles = all the token's roles unless trusts_delegated_roles) | stack create; update/delete when the caller differs from the trustor (heat/engine/stack.py:594-619) | the user's token | heat/engine/clients/os/keystone/heat_keystoneclient.py:196-236 |
POST /v3/auth/tokens with a trust scope | every deferred operation (signals, scaling, stored context) | [trustee] user + trust_id | heat/common/context.py:201-212 |
DELETE /v3/OS-TRUST/trusts/{id} | stack delete, trust refresh | user/trust | heat/engine/clients/os/keystone/heat_keystoneclient.py:255-260 |
domain-scoped token for stack_domain_admin | creating stack-domain projects, users, credentials | [DEFAULT]/stack_domain_admin(_password) in stack_user_domain_* | heat/engine/clients/os/keystone/heat_keystoneclient.py:134-167 |
POST /v3/projects (<project id>-<stack id>, truncated to 64) | stack create (stack_user_project_id) | domain admin | heat/engine/clients/os/keystone/heat_keystoneclient.py:437-451 |
POST /v3/users + role grant heat_stack_user on that project | first signal URL / server credentials of a resource | domain admin | heat/engine/clients/os/keystone/heat_keystoneclient.py:344-395 |
POST /v3/credentials (type ec2, blob {"access", "secret"}) | CFN signal URLs, AWS::IAM::AccessKey | domain admin or user | heat/engine/clients/os/keystone/heat_keystoneclient.py:527-556 |
PATCH /v3/users/{id} (enabled) | suspend/resume of stack users | domain admin | heat/engine/clients/os/keystone/heat_keystoneclient.py:570-590 |
OS::Keystone::* resources (projects, users, roles, groups, domains, endpoints, services, regions, role assignments) | resource plugins | stack owner's token | heat/engine/resources/openstack/keystone/ |
Without stack_user_domain_* configured, Heat falls back to creating stack users in the user's own project and domain with the user's token (heat/engine/clients/os/keystone/heat_keystoneclient.py:354-357, :437-442); a deployment for OpenKeel must configure the domain.
4. Other services (resource plugins)
- One client plugin per service, loaded through the
heat.clientsentry points (20 plugins, [_rewrite/client-plugins_stable-2026.2.json](_rewrite/client-plugins_stable-2026.2.json)). Endpoints come from the token's catalog:service_typeof the plugin, interface[clients_<name>]/endpoint_type(defaultpublicURLfrom[clients]), regioncontext.region_nameor[DEFAULT]/region_name_for_services, orregion_name_for_shared_servicesfor the shared services list (heat/engine/clients/client_plugin.py:89-125). - A resource type is offered only if its service is in the catalog (
is_service_available,heat/engine/resource.py:785-828); otherwise templates using it fail validation withResourceTypeUnavailable(400, error code99001). - Retries:
[DEFAULT]/client_retry_limit(2) connection retries for keystoneauth sessions; resource plugins poll their resource incheck_<action>_completewith the scheduler's back-off; 404 on delete is ignored through each plugin'signore_not_found. - The full per-type list, with the porting verdict for OpenKeel, is [
_rewrite/resource-types_stable-2026.2.json](_rewrite/resource-types_stable-2026.2.json) and 07 § 3.
5. Other exchanges
- template_url / get_file / template resources by URL:
GETwithrequests,httpandhttpsonly, timeouttemplate_fetch_timeout(60 s), streaming stopped atmax_template_size(DownloadLimitExceeded) (heat/common/urlfetch.py:38-84). The fetch fortemplate_urlhappens in heat-api; for nested template URLs in heat-engine. - Swift:
files_containerobjects (heat/engine/template_files.py), signal TempURLs and containers, server metadata TempURLs (PUTfrom the engine,heat/engine/service_software_config.py:139-147). - Zaqar: signal queues, metadata queues, the
zaqar-queueevent sink (absent in OpenKeel). - Instance agents → Heat: os-collect-config / heat-config-notify / cfn-signal call heat-api-cfn (
DescribeStackResource,/v1/signal,/v1/waitcondition), heat-api (…/metadata,…/signal), or Swift/Zaqar, with the stack-domain user's credentials (05 § 5).
Source: reverse-spec-heat/03-interactions_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.