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

Reverse-spec · Services

zun — Interactions

Interactions — zun — branch stable/2026.2

Branch stable/2026.2 @ 34135a563699 — repository https://opendev.org/openstack/zun — analysed on 2026-11-01

All frames below are reconstructed from the code. No capture of a real system was made, since none was available.

RabbitMQ

Library: oslo.messaging (zun/common/rpc_service.py:17). Transport: [DEFAULT]/transport_url. Control exchange: zun (zun/common/config.py:33). allowed_remote_exmods carries zun.common.exception (zun/common/rpc.py:36), so a remote exception keeps its class and status when it reaches the API.

Topology

ExchangeTypeRouting key / queueProducerConsumer
control exchangetopiczun-compute (no named server)zun-api for image_search without host, network_create, network_deleteany zun-compute
control exchangetopiczun-compute.<host>zun-api for all per-node methodszun-compute on <host>
reply_<uuid>directcaller's reply queuezun-computezun-api

Only zun-api sends RPC. zun-compute does not call other services over the bus. There is no conductor: every process with a role writes the database directly.

RPC methods

Client: zun/compute/rpcapi.py. Server: zun/compute/manager.py. Topic and server resolved at zun/common/rpc_service.py:98, zun/common/rpc_service.py:102. The target carries no version (zun/common/rpc_service.py:51). Client history states 1.0 and 1.1 in a comment, but the value is not sent (zun/compute/rpcapi.py:44).

MethodModeHost checkArgumentsResponseClientServer
container_createcastnolimits, requested_networks, requested_volumes, container, run, pci_requests—zun/compute/rpcapi.py:58zun/compute/manager.py:290
container_deletecastyescontainer, force—zun/compute/rpcapi.py:69zun/compute/manager.py:538
container_showcallyescontainercontainerzun/compute/rpcapi.py:74zun/compute/manager.py:618
container_rebuildcastnocontainer, run—zun/compute/rpcapi.py:78zun/compute/manager.py:675
container_rebootcastnocontainer, timeout—zun/compute/rpcapi.py:82zun/compute/manager.py:645
container_stopcastnocontainer, timeout—zun/compute/rpcapi.py:86zun/compute/manager.py:663
container_startcastnocontainer—zun/compute/rpcapi.py:90zun/compute/manager.py:762
container_pausecastnocontainer—zun/compute/rpcapi.py:93zun/compute/manager.py:782
container_unpausecastnocontainer—zun/compute/rpcapi.py:96zun/compute/manager.py:800
container_logscallyescontainer, stdout, stderr, timestamps, tail, sincestringzun/compute/rpcapi.py:100zun/compute/manager.py:808
container_execcallyescontainer, command, run, interactive{output, exit_code, exec_id, token}zun/compute/rpcapi.py:107zun/compute/manager.py:826
container_exec_resizecallyesexec_id, height, width—zun/compute/rpcapi.py:113zun/compute/manager.py:859
container_killcastnocontainer, signal—zun/compute/rpcapi.py:118zun/compute/manager.py:883
container_updatecallyescontainer, patchcontainerzun/compute/rpcapi.py:123zun/compute/manager.py:891
resize_containercastnocontainer, patch—zun/compute/rpcapi.py:127zun/compute/manager.py:1290
container_attachcallyescontainertokenzun/compute/rpcapi.py:132zun/compute/manager.py:928
container_resizecallyescontainer, height, widthcontainerzun/compute/rpcapi.py:137zun/compute/manager.py:945
container_topcallyescontainer, ps_argsdictzun/compute/rpcapi.py:142zun/compute/manager.py:958
container_get_archivecallyescontainer, path, encode_data[data, stat]zun/compute/rpcapi.py:147zun/compute/manager.py:973
container_put_archivecallyescontainer, path, data, decode_data—zun/compute/rpcapi.py:153zun/compute/manager.py:991
container_statscallyescontainerdictzun/compute/rpcapi.py:160zun/compute/manager.py:1009
container_commitcallyescontainer, repository, tag{uuid}zun/compute/rpcapi.py:165zun/compute/manager.py:1023
add_security_group, remove_security_groupcastnocontainer, security_group—zun/compute/rpcapi.py:169, zun/compute/rpcapi.py:173zun/compute/manager.py:578, zun/compute/manager.py:596
image_delete, image_pullcastnoimage—zun/compute/rpcapi.py:177, zun/compute/rpcapi.py:180zun/compute/manager.py:1105, zun/compute/manager.py:1116
image_searchcallnoimage, image_driver_name, exact_match, registrylistzun/compute/rpcapi.py:183zun/compute/manager.py:1165
network_detach, network_attachcastnocontainer, network or requested_network—zun/compute/rpcapi.py:202, zun/compute/rpcapi.py:206zun/compute/manager.py:1246, zun/compute/manager.py:1264
network_create, network_deletecall, no servernoneutron_net_id or networknetworkzun/compute/rpcapi.py:211, zun/compute/rpcapi.py:216zun/compute/manager.py:1282, zun/compute/manager.py:1286
capsule_create, capsule_deletedead———zun/compute/rpcapi.py:190, zun/compute/rpcapi.py:198no such method on the manager: capsules travel through container_create and container_delete

container_rebuild, start, stop and other casts do not check the node, so a request to a dead node gives 202 and is lost until the queue is consumed. "Host check" is check_container_host (zun/compute/rpcapi.py:25). container is a versioned object: Container 1.44, Capsule 1.4, CapsuleContainer 1.4, CapsuleInitContainer 1.4 (zun/objects/container.py:431, zun/objects/container.py:443, zun/objects/container.py:487, zun/objects/container.py:517).

Notifications

None. A search over the non-test code finds no get_notifier and no caller passing notifier= to wrap_exception (zun/common/exception.py:56).

Sample frame

Cast container_start to zun-compute.node1:

{"oslo.version": "2.0",
 "oslo.message": "{\"method\": \"container_start\", \"args\": {\"container\": {\"zun_object.name\": \"Container\", \"zun_object.namespace\": \"zun\", \"zun_object.version\": \"1.44\", \"zun_object.data\": {\"uuid\": \"…\", \"status\": \"Created\", \"host\": \"node1\", \"container_id\": \"…\"}}}, \"namespace\": null, \"_unique_id\": \"…\", \"_context_project_id\": \"…\", \"_context_user_id\": \"…\", \"_context_auth_token\": \"…\", \"_context_request_id\": \"req-…\", \"_context_roles\": [\"member\"]}"}

The zun_object prefix is OBJ_SERIAL_NAMESPACE (zun/objects/base.py:38). The context travels in _context_*, including the user's token, which zun-compute reuses to call Neutron, Cinder and Glance (zun/common/keystone.py:72). A long image pull can outlive that token [inferred]; capsule and auto-remove flows that run later rely on it too.

Database

Library: oslo.db and SQLAlchemy (zun/db/sqlalchemy/api.py:19). Single backend sqlalchemy (zun/db/api.py:27); zun/db/etcd/__init__.py is an empty leftover. Access from zun-api, zun-compute, zun-cni-daemon, zun-wsproxy. Migrations: Alembic, head 3f2b36231bee, 84 revisions, run with zun-db-manage upgrade (zun/cmd/db_manage.py:28). They do not run on SQLite. Schema: [_rewrite/schema-mariadb-migrated_stable-2026.2.sql](_rewrite/schema-mariadb-migrated_stable-2026.2.sql), differences with the models in [_rewrite/schema-diff_stable-2026.2.md](_rewrite/schema-diff_stable-2026.2.md).

Tables

TableRoleWritten byRead byModel
zun_serviceservice heartbeats, disabled and forced-down flagszun-compute, zun-apizun-apizun/db/sqlalchemy/models.py:103
containercontainers, capsules and capsule members, told apart by container_typezun-api, zun-compute, zun-cni-daemonallzun/db/sqlalchemy/models.py:124
volume, volume_mappinga volume and its mounts per containerzun-api, zun-computezun-computezun/db/sqlalchemy/models.py:208, zun/db/sqlalchemy/models.py:184
exec_instanceexec session tokenszun-computezun-wsproxyzun/db/sqlalchemy/models.py:228
imageimages pulled per node, unique on (repo, tag, host)zun-api, zun-computezun-apizun/db/sqlalchemy/models.py:252
compute_nodenode inventory and usage, NUMA, runtimes, labelszun-computescheduler in zun-api, zun-apizun/db/sqlalchemy/models.py:360
pci_deviceSR-IOV deviceszun-computezun-computezun/db/sqlalchemy/models.py:399
container_actions, container_actions_eventsaudit trailzun-api, zun-computezun-apizun/db/sqlalchemy/models.py:453, zun/db/sqlalchemy/models.py:479
quotas, quota_classeslimits per project and per classzun-apizun-apizun/db/sqlalchemy/models.py:504, zun/db/sqlalchemy/models.py:520
quota_usagesmodel only: usage is counted from containernone foundnone found [inferred]zun/db/sqlalchemy/models.py:533
networkDocker network per Neutron network per node, unique on (neutron_net_id, host)zun-computezun-compute, zun-apizun/db/sqlalchemy/models.py:552
registryprivate registry credentialszun-apizun-computezun/db/sqlalchemy/models.py:572
resource_provider, resource_class, inventory, allocationleftovers of an internal Placement model; Placement is the reference nowno live caller found [inferred]none found [inferred]zun/db/sqlalchemy/models.py:272, zun/db/sqlalchemy/models.py:288, zun/db/sqlalchemy/models.py:301, zun/db/sqlalchemy/models.py:335

Notable transactions

  • Updates: a SELECT … FOR UPDATE then assignment, in the same transaction, with retry_on_deadlock (zun/db/sqlalchemy/api.py:251, zun/db/sqlalchemy/api.py:258). 12 sites. No optimistic generation, no status compare-and-swap: the last writer wins, and task_state is an advisory marker.
  • Container update rejects a name change that breaks uniqueness: SELECT … WHERE name = ? [AND project_id = ?] according to unique_container_name_scope (zun/db/sqlalchemy/api.py:165, zun/db/sqlalchemy/api.py:240). A check then write, so not race-free.
  • Project scope: filter added by _add_project_filters unless admin with all_projects (zun/db/sqlalchemy/api.py:111).
  • Quotas: read-then-check, not reservations: SELECT SUM(...), then compare; two concurrent requests can both pass (zun/api/controllers/v1/containers.py:463, zun/common/quota.py:166, zun/common/quota.py:568).
  • Registry password: Fernet with auth_encryption_key, decrypted on read and copied in clear in the RPC object (zun/db/sqlalchemy/api.py:1402, zun/db/sqlalchemy/api.py:1414, zun/common/crypt.py:23, zun/conf/utils.py:21).
  • Foreign keys: container.capsule_id cascades; volume_mapping.volume_id cascades; container_actions.container_uuid cascades; see _rewrite/schema-mariadb-migrated_stable-2026.2.sql.

Cache

oslo.cache is not used in the code read. keystonemiddleware's own token cache may be configured with [keystone_authtoken]/memcached_servers.

Outbound REST calls

Identity: [keystone_auth] for admin clients, and [keystone_authtoken]/www_authenticate_uri for token auth (zun/conf/keystone.py:16, zun/conf/keystone.py:17). Selection: auth_token_info, else the user's token, else admin credentials (zun/common/keystone.py:72).

Target serviceMethod + pathWhenAuthenticationTimeout / retryErrors handledDefined at
KeystoneGET /v3/auth/tokenseach API requestservice tokenkeystonemiddleware401etc/zun/api-paste.ini:12
PlacementGET /allocation_candidates?resources=…&required=!CUSTOM_ZUN_COMPUTE_STATUS_DISABLED&limit=1000scheduling, microversion 1.31admin@safe_connect, keystoneauthno candidate gives NoValidHostzun/scheduler/client/report.py:282, zun/scheduler/client/query.py:61
PlacementGET then PUT /allocations/{uuid}claim, in scheduling; consumer generation 1.28admin@retries on conflict (zun/scheduler/client/report.py:1470)conflict gives the next hostzun/scheduler/client/report.py:1471, zun/scheduler/utils.py:395
PlacementGET, PUT /allocations/{uuid} with empty bodydeleteadminas aboveAllocationDeleteFailedzun/scheduler/client/report.py:1897
PlacementPOST /resource_providers, PUT /resource_providers/{id}/inventories, .../traits, .../aggregates, GET /traits, PUT /traits/{t}each inventory_host when changedadminup to 4 tries on conflictResourceProviderUpdateConflictzun/scheduler/client/report.py:501, zun/scheduler/client/report.py:888, zun/scheduler/client/report.py:1009, zun/scheduler/client/report.py:1092, zun/scheduler/client/report.py:959, zun/compute/compute_node_tracker.py:338
NeutronGET /v2.0/networks, /subnets, /ports, /security-groupscreate, attachuserclient404 mapped to NetworkNotFound, PortNotFoundzun/network/neutron.py:274, zun/network/neutron.py:285, zun/network/neutron.py:301
NeutronPOST /v2.0/ports, PUT /v2.0/ports/{id} with device_id, optional binding:host_id, device_owner: compute:zunport creationadminclientcleanup on Docker failurezun/network/neutron.py:79, zun/common/consts.py:80
NeutronDELETE /v2.0/ports/{id}, or PUT to unbind for a user-supplied portdeleteadminclientPortNotFoundClient loggedzun/network/neutron.py:148, zun/network/neutron.py:161
NeutronPOST /v2.0/security-groups, /security-group-rules, DELETEexposed_portsuserclientCould not create required security group ruleszun/container/docker/driver.py:382, zun/network/neutron.py:340
NeutronGET /v2.0/ports?binding:host_id=VIF polling, each secondadmin—exception in the timerzun/cni/daemon/service.py:199
CinderGET /volumes, POST /volumes, DELETE /volumes/{id}, POST /volumes/{id}/action (os-reserve, os-initialize_connection, os-attach, os-begin_detaching, os-detach, os-terminate_connection, os-unreserve)mountsuser, elevated on attachclientVolumeNotFound, VolumeInUse, VolumeNotUsable, VolumeCreateFailedzun/volume/cinder_api.py:38, zun/volume/cinder_workflow.py:89
GlanceGET /v2/images, GET /v2/images/{id}, GET /v2/images/{id}/file, POST /v2/images, PATCH, PUT .../file, DELETEGlance driver, commituserclientImageNotFoundzun/image/glance/utils.py:55, zun/image/glance/utils.py:63, zun/image/glance/utils.py:102, zun/image/glance/utils.py:80, zun/image/glance/utils.py:95
Registrydocker pull by the daemon, with auth_configDocker driverregistry credentialsDockerImageNotFound, DockerErrorzun/image/docker/driver.py:66

Port creation is the sensitive one: tenant_id is the caller's project and the call is admin, while the requested network was first read with the user's token (zun/common/utils.py:508).

Docker daemon

  • Protocol: Docker Engine API, version [docker]/docker_remote_api_version, 1.40 by default (zun/conf/docker.py:20). Library: docker-py (zun/container/docker/utils.py:52).
  • Endpoints: unix:///var/run/docker.sock for zun-compute, and tcp://$my_ip:2375 for zun-wsproxy (zun/conf/docker.py:28, zun/conf/docker.py:31, zun/conf/docker.py:49). The install guide opens 2375 on the node with no TLS setting (doc/source/install/compute-install.rst:224). TLS exists through api_insecure, ca_file, cert_file, key_file (zun/container/docker/utils.py:35).
  • Errors: is_not_found is '404' in str(e), so an error whose text merely contains 404 counts as not found (zun/container/docker/driver.py:47). errors.APIError is rewritten to DockerError (zun/container/docker/utils.py:48); wrap_docker_error turns "not found" into state changes and "not running", "not paused", "paused" into Conflict (zun/container/docker/driver.py:62, zun/container/docker/driver.py:87).
  • Time-out: [docker]/default_timeout, 60 s.
  • Names: zun-<uuid>.
  • Calls: create_container, start, stop, restart, kill, pause, unpause, remove_container, inspect_container, list_containers, logs, exec_create, exec_start, exec_inspect, exec_resize, resize, top, stats, update_container, get_archive, put_archive, commit, pull, load_image, inspect_image, get_image, images, info, create_network, remove_network, networks, connect_container_to_network, disconnect_container_from_network.

Isolation applied through Docker and CRI

SettingSourceWhere
runtime from the request, else container_runtime, default runc; RuntimeFilter keeps nodes that list itadmin or operatorzun/container/docker/driver.py:288, zun/conf/container_driver.py:55, zun/scheduler/filters/runtime_filter.py:28
privilegedpolicy denied by defaultzun/container/docker/driver.py:291, zun/common/policies/container.py:43
mem_limit, cpu_shares, cpuset_cpus, cpuset_mems, storage_opt.sizerequest or defaultszun/container/docker/driver.py:307, zun/container/docker/driver.py:320
no cap_drop, seccomp, security_opt, userns, pids_limit, read_only setabsentsearch over zun/container/
capsule: shared network and IPC, PID left outsandboxzun/container/docker/driver.py:1255

Other

  • Images: selection image_driver per container, default_image_driver default docker, list image_driver_list glance, docker (zun/conf/image_driver.py:20, zun/conf/image_driver.py:32). Pull policy always when unset (zun/common/utils.py:252). With Docker, policies ifnotpresent and never look at the node's store, with no project check (zun/image/docker/driver.py:54, zun/image/docker/driver.py:90). With Glance: image located with the user's token, container_format: docker, tar written to [glance]/images_directory, docker load (zun/image/glance/utils.py:56, zun/image/glance/driver.py:74, zun/container/docker/driver.py:128).
  • Capsule images under CRI: PullImage only; the registry is not usable (zun/container/cri/driver.py:194).
  • containerd CRI: gRPC over unix:///run/containerd/containerd.sock, runtime.v1alpha2, insecure channel (zun/container/cri/driver.py:45, zun/criapi/api_pb2.py:31).
  • CNI: zun-cni binary called with CNI_* environment and config on stdin (zun/network/cni_network.py:132); talks HTTP to zun-cni-daemon at 127.0.0.1:9036 (zun/conf/cni_daemon.py:24). Spec version 0.3.1 (zun/cni/api.py:35). Bindings through os-vif: VIFBridge, VIFOpenVSwitch (setup.cfg:82).
  • Kuryr: kuryr-libnetwork is a Docker network plugin, called by Docker rather than by Zun. Zun names the driver with [network]/driver_name (zun/conf/network.py:23).
  • Privileged commands: mount, umount, mkfs through rootwrap or privsep (zun/common/mount.py:44, zun/common/utils.py:343). Filter file: etc/zun/rootwrap.d/zun.filters:1. os-brick connects the Cinder device (zun/volume/cinder_workflow.py:62).
  • Files: volumes under [volume]/volume_dir, $state_path/mnt/<volume uuid> (zun/common/mount.py:168, zun/conf/volume.py:35); Docker root measured through psutil (zun/container/docker/driver.py:1091).
  • Host capabilities: NUMA, memory and PCI information read locally (zun/container/os_capability/linux/os_capability_linux.py:1).
  • WebSocket: see 05 § zun-wsproxy.

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