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

Reverse-spec · Services

placement — Scenarios

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

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

Placement is synchronous: each scenario ends with the HTTP response. Periodic processing is done by the clients, Nova first, and is marked [inferred] when it lies outside this repository. Statuses and codes refer to errors.

1. Registering a compute host (nova-compute resource tracker)

sequenceDiagram
  autonumber
  participant NC as nova-compute (resource tracker)
  participant P as placement-api
  participant DB as MariaDB (placement)

  NC->>P: GET /resource_providers?in_tree=<host uuid> (1.14+)
  P->>DB: SELECT resource_providers …
  alt unknown host
    NC->>P: POST /resource_providers {name, uuid}
    P->>DB: INSERT resource_providers (generation = 0, root = itself)
    alt name or uuid already taken
      P-->>NC: 409 placement.duplicate_name
    else
      P-->>NC: 200 {generation: 0, …}
    end
  end
  NC->>P: PUT /resource_providers/{uuid}/inventories {resource_provider_generation: g, inventories: {VCPU, MEMORY_MB, DISK_GB}}
  P->>DB: SELECT inventories; DELETE/INSERT/UPDATE inventories; UPDATE resource_providers SET generation = g+1 WHERE generation = g
  alt stale generation
    P-->>NC: 409 placement.concurrent_update
    Note over NC: Nova re-reads the provider and starts again [inferred]
  else a removed class is still allocated
    P-->>NC: 409 placement.inventory.inuse
  else total reduced below current usage
    P-->>NC: 200, and the Placement log shows WARNING … is now over-capacity
  else
    P-->>NC: 200 {resource_provider_generation: g+1, inventories}
  end
  NC->>P: PUT /resource_providers/{uuid}/traits {resource_provider_generation, traits: [HW_CPU_X86_AVX2, COMPUTE_…]}
  P->>DB: DELETE/INSERT resource_provider_traits; UPDATE generation
  P-->>NC: 200
  NC->>P: PUT /resource_providers/{uuid}/aggregates {aggregates, resource_provider_generation}
  P-->>NC: 200
  loop every ~60 s (Nova's update_available_resource) [inferred]
    NC->>P: PUT …/inventories (only when the inventory changed on the Nova side) [inferred]
  end

Diagnostics:

  • Repeated 409s on inventories: another actor modifies the same provider (Neutron on a child? an administrator?). Look for the X-Openstack-Request-Ids in Placement's access log.
  • The host never appears as a candidate: GET /resource_providers/{uuid}/inventories and /usages give the remaining capacity. Also check the COMPUTE_STATUS_DISABLED trait.
  • 403 with a valid token after upgrading to this branch: the token's scope does not match the policy's scope_types, and scope checks can no longer be disabled (see change history, change 6).

2. Scheduling and reserving an instance (nova-scheduler and nova-conductor)

sequenceDiagram
  autonumber
  participant S as nova-scheduler
  participant P as placement-api
  participant DB as MariaDB

  S->>P: GET /allocation_candidates?resources=VCPU:2,MEMORY_MB:4096,DISK_GB:20&required=!COMPUTE_STATUS_DISABLED&member_of=…&limit=1000
  P->>DB: providers with free capacity (one query per class) + trait/aggregate filters + usages per tree
  P->>P: per-tree overall capacity pre-check (bug 2160721 fix), then Cartesian product
  alt unknown class or trait, invalid syntax
    P-->>S: 400 (placement.query.bad_value…)
  else no possible host
    P-->>S: 200 {allocation_requests: [], provider_summaries: {}}
    Note over S: NoValidHost on the Nova side [inferred]
  else
    P-->>S: 200 {allocation_requests: [...], provider_summaries: {...}}
  end
  Note over S: Nova filters and weighers, choice of one candidate [inferred]
  S->>P: PUT /allocations/{instance_uuid} {allocations of the candidate, project_id, user_id, consumer_generation: null, consumer_type: INSTANCE}
  P->>DB: INSERT projects/users/consumers if needed
  P->>DB: transaction: DELETE consumer allocations; capacity check; INSERT allocations; UPDATE generation (providers, consumer)
  alt another scheduler took the space meanwhile (provider generation changed)
    P->>P: re-read and retry (up to allocation_conflict_retry_count = 10)
    alt capacity still enough
      P-->>S: 204
    else capacity exhausted
      P-->>S: 409 "… would exceed the capacity"
      Note over S: Nova tries the next candidate [inferred]
    end
  else consumer_generation ≠ null for a new consumer
    P-->>S: 409 placement.concurrent_update (no server-side retry)
  else
    P-->>S: 204
  end

Diagnostics:

  • **Slow GET /allocation_candidates** comes from wide trees or many providers. Tune [placement]/max_allocation_candidates and allocation_candidates_generation_strategy=breadth-first. The bug 2160721 fix on this branch removes the slowest case, where a tree almost fits the request.
  • Frequent capacity 409s reveal a race between schedulers. This is normal under load, and Nova retries.
  • **Exceeded retry limit of 10 on allocations write** in the logs signals extreme contention on a single provider.

3. Migrating an instance: atomic allocation swap

sequenceDiagram
  autonumber
  participant C as nova-conductor
  participant P as placement-api
  participant DB as MariaDB

  C->>P: GET /allocations/{instance}
  P-->>C: 200 {allocations: {source: …}, consumer_generation: 5}
  C->>P: POST /allocations {instance: {allocations: {}, consumer_generation: 5}, migration: {allocations: {source: …}, consumer_generation: null, consumer_type: MIGRATION}}
  Note over P: the source allocations move from the instance to the migration
  P->>DB: one transaction: replacement of both consumers' allocations, generations +1
  alt instance modified meanwhile
    P-->>C: 409 placement.concurrent_update
  else
    P-->>C: 204
  end
  Note over C: scheduling of the target (scenario 2) with PUT /allocations/{instance} on the target [inferred]
  Note over C: on confirm: DELETE /allocations/{migration}; on revert: allocations given back to the instance [inferred]

Diagnostics: "doubled" or orphan allocations held by a migration (consumer_type = MIGRATION, from 1.38) reveal a migration interrupted on the Nova side. GET /resource_providers/{uuid}/allocations shows the consumers present on a host.

4. Reorganising a tree (reshaper), for example vGPUs moved to child providers

sequenceDiagram
  autonumber
  participant NC as nova-compute (driver upgrade)
  participant P as placement-api
  participant DB as MariaDB

  NC->>P: POST /resource_providers {name: host_pGPU1, parent_provider_uuid: host} (×N)
  P-->>NC: 200
  NC->>P: POST /reshaper (service role) {inventories: {host: {gen, inventories without VGPU}, pGPU1: {gen, VGPU}}, allocations: {instances: {…on pGPU1…}}}
  alt admin role only
    P-->>NC: 403
  else stale provider generation
    P-->>NC: 409 placement.concurrent_update
  else unknown provider
    P-->>NC: 400 placement.resource_provider.not_found
  else
    P->>DB: one transaction: new inventories, new allocations, generations
    P-->>NC: 204
  end

5. Deleting a host

sequenceDiagram
  autonumber
  participant A as nova-compute / administrator
  participant P as placement-api
  participant DB as MariaDB

  A->>P: DELETE /resource_providers/{host}
  P->>DB: SELECT children; SELECT COUNT allocations
  alt child providers present
    P-->>A: 409 placement.resource_provider.cannot_delete_parent
    Note over A: delete the children first
  else allocations present
    P-->>A: 409 placement.resource_provider.inuse
    Note over A: delete or move the allocations (instances, migrations)
  else
    P->>DB: DELETE inventories, resource_provider_aggregates, resource_provider_traits, resource_providers
    P-->>A: 204
  end

Diagnostics: GET /resource_providers/{uuid}/allocations lists the consumers that block the deletion. For deleted instances whose allocations were left behind, Nova's audit tool finds them [inferred].

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