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

Reverse-spec · Services

glance — Scenarios

End-to-end scenarios — glance — branch stable/2026.2

Branch stable/2026.2 @ a3e5f490b7a2 — repository https://opendev.org/openstack/glance — analysed on 2026-10-28

Participants: the client (user, Nova, Cinder), glance-api workers (several per deployment, possibly on different hosts), MariaDB, the image store (any glance_store backend), the staging directory of a worker, RabbitMQ (notifications only), Keystone.

1. Create an image and upload its data (classic upload)

The simplest path, used by openstack image create --file and by Nova snapshots on non-RBD backends. Everything happens in one HTTP request; there is no task.

sequenceDiagram
  autonumber
  actor U as Client
  participant A as glance-api
  participant K as Keystone
  participant DB as MariaDB
  participant S as Store
  participant MQ as RabbitMQ
  U->>A: POST /v2/images {name, disk_format, container_format}
  A->>K: validate token (keystonemiddleware, cached)
  A->>DB: INSERT images (status=queued), image_properties, image_tags
  A->>MQ: notify image.create
  A-->>U: 201 + Location + OpenStack-image-import-methods
  U->>A: PUT /v2/images/{id}/file (octet-stream)
  A->>DB: UPDATE images SET status=saving WHERE status=queued
  A->>MQ: notify image.prepare
  A->>S: add(data) streamed, md5 + sha512 + format inspection on the fly
  alt data stored
    A->>DB: INSERT image_locations; UPDATE images SET status=active, size, checksum, os_hash_value, virtual_size WHERE status=saving
    A->>MQ: notify image.upload, image.activate
    A-->>U: 204
  else store full / write denied / quota / format mismatch
    A->>DB: UPDATE images SET status=queued
    A->>MQ: notify image.upload (ERROR)
    A-->>U: 413 / 503 / 413 / 415
  else image deleted during upload
    A->>S: delete written data
    A-->>U: 410
  end

Diagnostics: a queued image after a failed upload is normal and can be re-uploaded; saving that never ends means the worker died during the upload. Format mismatches show "Image disk_format %s does not match stream data %s" in the API log. References: 01 § upload, glance/api/v2/image_data.py:108-328, glance/location.py:574-700.

2. Interoperable import with glance-direct on a multi-worker deployment

The staged data lives on the local disk of the worker that received PUT /stage. Any later request that reaches another worker is proxied to it, using worker_self_reference_url.

sequenceDiagram
  autonumber
  actor U as Client
  participant W1 as glance-api (worker 1)
  participant W2 as glance-api (worker 2)
  participant DB as MariaDB
  participant ST as Staging dir of worker 1
  participant S as Store(s)
  U->>W1: POST /v2/images
  W1->>DB: INSERT images (queued)
  W1-->>U: 201
  U->>W1: PUT /v2/images/{id}/stage
  W1->>DB: UPDATE status=uploading
  W1->>ST: write <id>
  W1->>DB: SET os_glance_stage_host = http://worker1
  W1-->>U: 204
  U->>W2: POST /v2/images/{id}/import {method: glance-direct, stores: [a, b]}
  W2->>DB: read image (os_glance_stage_host != self)
  W2->>W1: POST /v2/images/{id}/import (same body, user's token, 60 s)
  W1->>DB: INSERT tasks (pending); INSERT image_properties os_glance_import_task (unique)
  W1-->>W2: 202
  W2-->>U: 202
  Note over W1: tasks_pool thread, taskflow
  W1->>DB: task processing; image importing; os_glance_importing_to_stores=a,b
  loop each store
    W1->>ST: read <id>
    W1->>S: add(data)
    W1->>DB: INSERT image_locations; remove store from os_glance_importing_to_stores
  end
  W1->>ST: delete <id>
  W1->>DB: image active; task success; DELETE os_glance_import_task
  U->>W2: GET /v2/images/{id} (poll)
  W2-->>U: status=active

Diagnostics: 504 "Stage host is unavailable" on import means worker 1 cannot be reached at its worker_self_reference_url; the data must then be staged again. A 409 "Image has active task" means another import holds the lock: it expires after 60 minutes (120 for a pending task, glance/api/v2/images.py:171-220). Stores that failed are listed in os_glance_failed_import. References: 05 § api_image_import, glance/api/v2/images.py:246-291.

3. web-download import into several stores, one of them failing

sequenceDiagram
  autonumber
  actor U as Client
  participant A as glance-api
  participant DB as MariaDB
  participant H as Remote HTTP server
  participant ST as Staging
  participant SA as Store a
  participant SB as Store b
  U->>A: POST …/import {method: {name: web-download, uri}, stores: [a, b], all_stores_must_succeed: false}
  A->>A: validate uri against import_filtering_opts
  alt uri refused
    A-->>U: 400 "URI for web-download does not pass filtering"
  else accepted
    A->>DB: task pending + import lock
    A-->>U: 202
    A->>DB: image importing; quotas image_stage_total, image_count_uploading
    A->>H: GET uri (IP pinned, redirects re-validated)
    H-->>A: image bytes
    A->>ST: write <id>
    A->>SA: add(data)
    A->>DB: location a added
    A->>SB: add(data)
    SB--xA: StorageFull
    A->>DB: os_glance_failed_import=b (flow continues because all_stores_must_succeed=false)
    A->>ST: delete <id>
    A->>DB: image active (a only); task success
  end

Diagnostics: with the default all_stores_must_succeed = true, the failure of store b would revert the whole import: location a removed, image back to queued, task failure with the store error in message. References: glance/async_/flows/api_image_import.py:596-720.

4. Delete with delayed delete and the scrubber

sequenceDiagram
  autonumber
  actor U as Client
  participant A as glance-api
  participant DB as MariaDB
  participant SC as glance-scrubber
  participant S as Store
  U->>A: DELETE /v2/images/{id}
  alt protected
    A-->>U: 403
  else delayed_delete = true
    A->>DB: images.status=pending_delete, deleted=1; image_locations.status=pending_delete
    A-->>U: 204
    loop every wakeup_time (300 s)
      SC->>DB: SELECT images WHERE status=pending_delete
      SC->>SC: keep those with deleted_at + scrub_time past
      SC->>S: delete(location)
      SC->>DB: image_locations.status=deleted; images.status=deleted
    end
  else delayed_delete = false (default)
    A->>S: delete each location
    A->>DB: images.status=deleted, soft-delete rows
    A-->>U: 204
  end

Diagnostics: images stuck in pending_delete mean the scrubber is not running or cannot reach the store; glance-scrubber --restore <id> brings one back to active. A 409 on delete with RBD comes from clones of the image (HasSnapshot/InUseByStore). References: glance/domain/__init__.py:249-255, glance/scrubber.py:309-414.

5. Nova snapshot published through the locations API (service role)

Deployments where Nova and Glance share a store (e.g. Ceph RBD) avoid copying data: Nova creates the snapshot in the store itself and registers its location.

sequenceDiagram
  autonumber
  participant N as nova-compute
  participant A as glance-api
  participant DB as MariaDB
  participant S as Store (shared with Nova)
  N->>A: POST /v2/images (snapshot metadata)
  A-->>N: 201 (queued)
  N->>S: create snapshot/clone directly
  N->>A: POST /v2/images/{id}/locations {url, validation_data?} (service token)
  A->>DB: task location_import + import lock
  A-->>N: 202
  A->>S: get_size / read data to compute os_hash_value (do_secure_hash)
  A->>DB: INSERT image_locations; image active; hash values; task success
  N->>A: GET /v2/images/{id} (poll until active)

Diagnostics: 403 "http store must be enabled to use location API by normal user." means the caller lacks the service role; a mismatch between validation_data and the computed hash puts the image back to queued. References: 05 § location_import, glance/api/v2/images.py:1161-1257.

Source: reverse-spec-glance/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.