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

Reverse-spec · Services

nova — Flavors api

Flavors: REST API and behaviour — Nova — branch stable/2026.2

Branch stable/2026.2 @ 8df1922926dd (34.0.0 + 7 commits) — repository https://opendev.org/openstack/nova — analysed on 2026-10-28 Scope: flavors only. Maximum microversion of the branch: 2.104 (nova/api/openstack/api_version_request.py:300).

A flavor is a named, administrator-defined size of server: vCPUs, RAM, disks, plus free-form extra specs that drive scheduling and the hypervisor configuration. Flavors live in the API database (flavors, flavor_extra_specs, flavor_projects, 03). Each server keeps a frozen copy of its flavor (§ 6).

1. Endpoints

Routes: nova/api/openstack/compute/routes.py:394-420.

Method and pathHandlerPolicy (default)VersionsSuccess
GET /flavorsnova/api/openstack/compute/flavors.py:141-150none (any authenticated user)all200, {flavors: [{id, name, links, description≥2.55}]}
GET /flavors/detailnova/api/openstack/compute/flavors.py:152-171none; extra_specs only if os_compute_api:os-flavor-extra-specs:index passes (≥ 2.61)all200
GET /flavors/{id}nova/api/openstack/compute/flavors.py:173-195none; same extra_specs ruleall200
POST /flavorsnova/api/openstack/compute/flavors.py:58-112os_compute_api:os-flavor-manage:create = adminall200 (not 201)
PUT /flavors/{id}nova/api/openstack/compute/flavors.py:114-139os_compute_api:os-flavor-manage:update = admin≥ 2.55, description only200
DELETE /flavors/{id}nova/api/openstack/compute/flavors.py:43-54os_compute_api:os-flavor-manage:delete = adminall202 (not 204)
GET /flavors/{flavor_id}/os-extra_specsnova/api/openstack/compute/flavors_extraspecs.py:57-66…os-flavor-extra-specs:index = project reader or adminall200 {extra_specs: {…}}
GET /flavors/{flavor_id}/os-extra_specs/{key}nova/api/openstack/compute/flavors_extraspecs.py:111-127…:show = project reader or adminall200 {key: value}
POST /flavors/{flavor_id}/os-extra_specsnova/api/openstack/compute/flavors_extraspecs.py:71-88…:create = adminall200, echoes the body
PUT /flavors/{flavor_id}/os-extra_specs/{key}nova/api/openstack/compute/flavors_extraspecs.py:90-109…:update = adminall200
DELETE /flavors/{flavor_id}/os-extra_specs/{key}nova/api/openstack/compute/flavors_extraspecs.py:132-149…:delete = adminall200 (a code comment says it should be 204, nova/api/openstack/compute/flavors_extraspecs.py:129-131)
GET /flavors/{flavor_id}/os-flavor-accessnova/api/openstack/compute/flavor_access.py:44-60os_compute_api:os-flavor-access = adminall200 {flavor_access: [{flavor_id, tenant_id}]}
POST /flavors/{id}/action addTenantAccessnova/api/openstack/compute/flavor_access.py:67-91…os-flavor-access:add_tenant_access = adminall200
POST /flavors/{id}/action removeTenantAccessnova/api/openstack/compute/flavor_access.py:93-123…os-flavor-access:remove_tenant_access = adminall200

Policies: nova/policies/flavor_manage.py:26-58, nova/policies/flavor_extra_specs.py:23-104, nova/policies/flavor_access.py:46-83. All are scope_types=['project']. "admin" is rule:context_is_admin (nova/policies/base.py:45).

2. Visibility: who sees which flavor

  • Admin: every flavor. is_public in the query filters (true, false, or none for all) (nova/api/openstack/compute/flavors.py:220-223).
  • Anyone else: public flavors and private flavors whose access list contains their project (nova/objects/flavor.py:283-288, nova/objects/flavor.py:630-638). In lists, disabled flavors are hidden too (nova/api/openstack/compute/flavors.py:225-226).
  • A private flavor outside the user's access list does not exist for them: GET /flavors/{id} and every sub-resource answer 404 (nova/api/openstack/common.py:491-495).

3. The flavor document

nova/api/openstack/compute/views/flavors.py:43-75:

FieldSource (flavors table)VersionsNotes
idflavorid (string)allthe public id; the integer id column is internal
namenameallunique
vcpusvcpusall
rammemory_mballMiB
diskroot_gballGiB; 0 = no local root disk limit (§ 5)
OS-FLV-EXT-DATA:ephemeralephemeral_gballGiB
swapswapallMiB; "" when 0 before 2.75, 0 from 2.75 (nova/api/openstack/compute/views/flavors.py:68-69)
os-flavor-access:is_publicis_publicall
OS-FLV-DISABLED:disableddisabled< 2.102
rxtx_factorrxtx_factor< 2.102"" when null
descriptiondescription≥ 2.55
extra_specsflavor_extra_specs≥ 2.61, if the extra-specs index policy passes
links—allself and bookmark

vcpu_weight is stored but never shown by the API (nova/objects/flavor.py:221).

4. Creating, changing and deleting

Create (POST /flavors)

Request schema nova/api/openstack/compute/schemas/flavors.py:29-95:

  • name (required): 1-255 characters, no leading or trailing spaces, except in v2.0, which allows them (nova/api/openstack/compute/schemas/flavors.py:78-81); stripped (nova/compute/flavors.py:80-81).
  • id (optional): string or number, 1-255 characters of [a-zA-Z0-9. _-], no leading or trailing space (nova/api/openstack/compute/schemas/flavors.py:38-42). Absent or null: a UUID is generated (nova/compute/flavors.py:64-68). A number is stored as a string (nova/compute/flavors.py:85).
  • ram, vcpus (required): integer ≥ 1; disk (required), OS-FLV-EXT-DATA:ephemeral, swap: integer ≥ 0; all ≤ 2,147,483,647 (nova/api/validation/parameter_types.py:470-497, nova/compute/flavors.py:90-101).
  • rxtx_factor: float > 0, default 1.0; refused from 2.102 (nova/api/openstack/compute/schemas/flavors.py:94-95).
  • os-flavor-access:is_public: boolean, default true.
  • description (≥ 2.55): up to 65,535 characters.

The flavor is written with an admin context and a flavor.create notification is sent (nova/compute/flavors.py:124-126, nova/objects/flavor.py:575-596). Creating a private flavor does not give access to the caller's project (comment nova/api/openstack/compute/flavors.py:96). The response carries extra_specs: {} from 2.61 (nova/api/openstack/compute/flavors.py:101-109).

Update (PUT /flavors/{id}, ≥ 2.55)

**Only description can change** (nova/api/openstack/compute/schemas/flavors.py:97-115). The other fields are immutable: changing a size means creating a new flavor. A flavor.update notification is sent (nova/objects/flavor.py:550-552).

Delete (DELETE /flavors/{id})

The flavor, its extra specs and its access list are hard-deleted (nova/objects/flavor.py:179-196), then flavor.delete is notified. Servers using it are not touched: they keep their copy (§ 6), and the server API shows it from that copy (nova/api/openstack/compute/views/servers.py:665-684).

List (GET /flavors, /flavors/detail)

  • Filters: minRam, minDisk (non-integers → 400) (nova/api/openstack/compute/flavors.py:228-241), is_public (admin only), and **name, from 2.102, treated as a regular expression** by the database (nova/objects/flavor.py:620-628).
  • Sort: sort_key (one of 15 columns; rxtx_factor refused from 2.102) and sort_dir, default flavorid asc (nova/api/openstack/compute/schemas/flavors.py:21-27, nova/api/openstack/compute/flavors.py:215-216).
  • Paging: limit and marker (a flavorid; unknown marker → 400) (nova/objects/flavor.py:640-652).
  • Unknown query parameters: ignored before 2.75, refused (400) from 2.75 (nova/api/openstack/compute/schemas/flavors.py:131-139).

5. Extra specs

Extra specs are key: value strings attached to a flavor. They change how and where the server runs: CPU pinning, huge pages, NUMA, devices, Placement resources and traits, scheduler filters.

Writing rules

  • Keys: ^[a-zA-Z0-9-_:. ]{1,255}$; values: strings of up to 255 characters, numbers accepted and converted (nova/api/validation/parameter_types.py:431-439, nova/api/openstack/compute/flavors_extraspecs.py:41-52).
  • POST merges the new keys into the existing ones (nova/api/openstack/compute/flavors_extraspecs.py:82). PUT …/{key} takes exactly one key, which must match the URL (nova/api/openstack/compute/flavors_extraspecs.py:98-100).
  • Validation from 2.86 (nova/api/openstack/compute/flavors_extraspecs.py:39, nova/api/openstack/compute/flavors_extraspecs.py:54-55, nova/api/validation/extra_specs/validators.py:31-63):
    1. a key known to the registry is checked against its type (integer with min and max, boolean "like" strutils.bool_from_string, string with an enumeration or a pattern) (nova/api/validation/extra_specs/base.py:62-120);
    2. a key without : and unknown is accepted as is;
    3. an unknown key in a namespace Nova owns (hw:, quota:, trait…:, resources…:, capabilities:…) is refused: Validation failed; extra spec '<key>' does not appear to be a valid extra spec. (400);
    4. an unknown key in another namespace (my:key) is accepted.
  • Before 2.86, nothing is validated: any key and value are stored. Existing flavors may therefore hold values the API now refuses.

The registry

525 validators are loaded through the stevedore namespace nova.api.extra_spec_validators (nova/api/validation/extra_specs/validators.py:66-82). The full list, with types, allowed values and descriptions, is in _rewrite/extra-spec-validators_stable-2026.2.json.

NamespaceCountDefined inWhat it controls
hw:37nova/api/validation/extra_specs/hw.pyCPU policy and topology, realtime, NUMA, page size, memory encryption, TPM, devices (serial, sound, USB, watchdog, IOMMU), ephemeral encryption
trait{group}:378nova/api/validation/extra_specs/traits.py:24-70one per standard trait plus CUSTOM_*: required / forbidden
resources{group}:22nova/api/validation/extra_specs/resources.py:24-62Placement resource amounts (21 standard classes + CUSTOM_*)
capabilities:45nova/api/validation/extra_specs/capabilities.py:58-108ComputeCapabilitiesFilter
quota:31nova/api/validation/extra_specs/quota.py:24-185libvirt cgroup/disk/VIF QoS (15) and VMware limits/reservations/shares (16)
hw_rng:3nova/api/validation/extra_specs/hw_rng.pyrandom number generator
others9nova/api/validation/extra_specs/accel.py, nova/api/validation/extra_specs/aggregate_instance_extra_specs.py, nova/api/validation/extra_specs/hw_video.py, nova/api/validation/extra_specs/os.py, nova/api/validation/extra_specs/pci_passthrough.py, nova/api/validation/extra_specs/vmware.py, nova/api/validation/extra_specs/null.pyCyborg device profile, aggregate matching, video RAM cap, secure boot, PCI aliases, VMware, group_policy, hide_hypervisor_id

What reads extra specs

  • Placement request (nova/scheduler/utils.py:80-203): resources*: and trait*: keys are taken as is (nova/scheduler/utils.py:233-257). Then VCPU, MEMORY_MB and DISK_GB are added unless overridden; disk = ephemeral + swap (rounded up to GiB) + root, the root being left out for boot-from-volume (nova/scheduler/utils.py:171-185). Several extra specs become resources or traits:
    • hw:cpu_policy=dedicated → PCPU instead of VCPU, plus one PCPU for hw:emulator_threads_policy=isolate (nova/scheduler/utils.py:363-417);
    • hw:cpu_thread_policy → trait HW_CPU_HYPERTHREADING forbidden or required (nova/scheduler/utils.py:419-427);
    • hw:mem_encryption → MEM_ENCRYPTION_CONTEXT=1 plus the model's trait (nova/scheduler/utils.py:320-342);
    • os:secure_boot, the vTPM and hw:pmem → traits or resources (nova/scheduler/utils.py:269-361).
  • Flavor and image properties: most hw:<x> keys have an image twin hw_<x> (nova/virt/hardware.py:1344-1358). Conflicts are refused, with three rules:
    • an image cannot request CPU pinning that the flavor did not grant (ImageCPUPinningForbidden, nova/virt/hardware.py:1776-1823);
    • image topology limits must stay within the flavor's (nova/virt/hardware.py:532-566);
    • several keys must be equal on both sides (FlavorImageConflict, nova/virt/hardware.py:1361-1390).
  • Scheduler filters: aggregate_instance_extra_specs:* and capabilities:*.
  • The libvirt driver: devices, cgroups (quota:*), firmware.

6. The flavor inside a server

  • At boot, the flavor must exist and not be disabled (nova/compute/api.py:1131-1132). Its RAM and disk must fit the image's min_ram and min_disk, and the image size when disk > 0 (nova/compute/api.py:814-885). disk = 0 with an image-backed root may be refused (BootFromVolumeRequiredForZeroDiskFlavor, 403).
  • The server stores its own copy in instance_extra.flavor, a JSON {"cur": …, "old": …, "new": …} of versioned Flavor objects (nova/objects/instance.py:749-762):
    • cur is the current size;
    • old and new are only set during a resize;
    • the description is blanked in the copy (nova/objects/instance.py:745-747). Changing or deleting the flavor afterwards has no effect on existing servers. Very old instances miss disabled and is_public, which are defaulted when the copy is loaded (nova/objects/instance.py:349-379).
  • The server document shows the flavor from 2.47 as an embedded object: vcpus, ram, disk, ephemeral, swap, original_name, and extra_specs if allowed. Before 2.47 it shows only the flavor id and link (nova/api/openstack/compute/views/servers.py:652-684).

7. Resize

nova/compute/api.py:4295-4440:

  1. The new flavor is looked up without deleted rows, and only among flavors visible to the caller (nova/compute/api.py:4345-4346).
  2. A flavor with disk = 0 is refused for an image-backed server: Resize to zero disk flavor is not allowed. (nova/compute/api.py:4355-4361).
  3. A disabled target flavor is refused, unless it is the current flavor (cold migration) (nova/compute/api.py:4371-4376). Resizing to the same flavor is refused (CannotResizeToSameFlavor, nova/compute/api.py:4378-4379).
  4. The quota for the size increase is checked, then the image constraints are checked again against the new flavor (nova/compute/api.py:4389-4419). The NUMA topology and PCI requests are recomputed from the new flavor (nova/compute/api.py:4425-4438).
  5. During the resize, the server holds old_flavor and new_flavor. Confirm keeps new as cur; revert restores old [inferred: done by the compute manager, not read here].

The HTTP translation is in nova/api/openstack/compute/servers.py:1200-1209: flavor not found, same flavor and disk refusal → 400; flavor/image conflicts (INVALID_FLAVOR_IMAGE_EXCEPTIONS, nova/api/openstack/compute/servers.py:55-93) → 400.

8. Lifecycle

stateDiagram-v2
    [*] --> Public: POST /flavors (is_public true)
    [*] --> Private: POST /flavors (is_public false)
    Private --> Private: addTenantAccess / removeTenantAccess
    Public --> Public: PUT description, extra specs changes
    Private --> Private: PUT description, extra specs changes
    Public --> [*]: DELETE (servers keep their copy)
    Private --> [*]: DELETE (servers keep their copy)

disabled cannot be set through the API: it only exists in the database (and was only shown before 2.102).

Source: reverse-spec-nova/01-flavors-api_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.