Skip to content

How to use Ansible dynamic inventory on Quake AI

How-to · Updated Jun 2026

Coming from another cloud?

▸AWS·EC2 Dynamic Inventory

This Quake AI feature maps to AWS’s EC2 Dynamic Inventory.

▸Azure·RM Dynamic Inventory

This Quake AI feature maps to Azure’s RM Dynamic Inventory.

Before this

How to use Ansible dynamic inventory on Quake AI

This guide replaces hand-maintained inventory files with the openstack.cloud.openstack plugin, which queries Quake AI directly and groups hosts by metadata. By the end you will have an inventory that reflects the live state of your project: new instances appear automatically, terminated instances drop out, and metadata tags become Ansible groups you can target with --limit.

For the wider context (when to provision with OpenTofu and configure with Ansible), read Ansible on Quake AI.

Prerequisites#

  • Ansible setup complete: ansible, openstacksdk >= 1.0.0, the openstack.cloud collection installed
  • A named cloud (rumble) configured in ~/.config/openstack/clouds.yaml
  • One or more Quake AI instances tagged with metadata (environment, role, or any keys you want to group on). If you have nothing running yet, the getting started IaC guide and the OpenTofu plus Ansible workflow both create instances with metadata.

Why dynamic inventory#

Static inventory files become a maintenance burden the moment a fleet grows past a handful of hosts. Every new instance needs a line; every retired instance leaves a stale entry; metadata changes mean editing the file in two places.

The dynamic inventory plugin sidesteps that by making Quake AI the source of truth. Ansible queries the Identity and Compute APIs at runtime, builds the inventory from running instances, and groups them by metadata you set on the instances themselves. The same playbooks then target those groups without anyone editing an inventory file.

Install the plugin requirements#

The plugin ships with the openstack.cloud collection installed in the getting-started guide. Verify both pieces are present:

bash
ansible-galaxy collection list openstack.cloud
python3 -c "import openstack; print(openstack.__version__)"

You should see openstack.cloud 2.2.0 or newer and openstacksdk 1.0.0 or newer. If openstacksdk is missing or older than 1.0.0, upgrade it before continuing:

bash
pip3 install --user --upgrade "openstacksdk>=1.0.0"

Author the inventory configuration#

Create a project directory and an inventory file:

bash
mkdir ansible-inventory-demo && cd ansible-inventory-demo
mkdir inventory

Create inventory/openstack.yml:

YAML
plugin: openstack.cloud.openstack
clouds:
  - rumble
expand_hostvars: false
inventory_hostname: name
compose:
  ansible_user: "'ubuntu'"
keyed_groups:
  - key: openstack.metadata.environment
    prefix: env
  - key: openstack.metadata.role
    prefix: role
  - key: openstack.flavor.name
    prefix: flavor
  - key: openstack.image.name
    prefix: image

The file selects the named cloud (rumble) from your clouds.yaml, sets the Ansible hostname to the instance name, sets ansible_user to ubuntu (quoted as a Jinja string literal) to match the Quake AI Ubuntu cloud images, and produces four groups per instance: env_<environment>, role_<role>, flavor_<flavor>, and image_<image>. The plugin sets ansible_host to the floating IP when one is attached.

The plugin also emits its own auto-generated meta-<key>_<value> groups for every instance metadata pair (e.g. meta-environment_production, meta-role_web). It also builds built-in groups such as flavor-m2a.large (hyphen prefix, dots preserved). Either the explicit env_*/role_* groups or the meta-* siblings work as playbook targets; pick whichever reads more clearly in your hosts: line.

Instances booted from a Cinder volume have an empty openstack.image hostvar, so the image_* keyed group does not form for them even when the instance runs normally.

The credential flow is the same one you already configured for the modules. The plugin reads clouds.yaml and the OS_* environment variable chain. Re-document of that flow lives in the getting-started guide.

Verify the inventory#

List every host the plugin can see:

bash
ansible-inventory -i inventory/openstack.yml --list

The output is a JSON document. Each instance appears under the groups derived from its metadata, flavor, and image. Use --graph for a more readable view:

bash
ansible-inventory -i inventory/openstack.yml --graph

You should see explicit groups like env_production and role_web from keyed_groups, alongside the plugin's auto-generated meta-environment_production, meta-role_web, and built-in groups such as flavor-m2a.large. The image_* keyed group appears only when openstack.image.name is populated (volume-booted instances omit it).

If the inventory comes back empty, common causes are:

  • The named cloud in inventory/openstack.yml does not match an entry in clouds.yaml
  • The application credential lacks read access to the project's compute resources
  • All instances are in a non-ACTIVE state (the plugin filters on running instances by default)

Filter the inventory to the current project#

The openstack.cloud.openstack plugin enumerates every server visible to the application credential. If the credential carries access to a shared project, the inventory enumerates unrelated VMs and ansible-playbook against an unfiltered group can target hosts you do not own.

The safest pattern is the application-credential scoping that the Generate app credentials flow already produces: an app credential created inside a single project lists only that project's servers. Confirm the cloud's auth.application_credential_id in clouds.yaml matches the credential you generated for the project you intend to target.

For a second layer of safety, restrict the inventory to instances tagged with a project-specific metadata key. Add a filters: block to inventory/openstack.yml:

YAML
plugin: openstack.cloud.openstack
clouds:
  - rumble
expand_hostvars: false
inventory_hostname: name
compose:
  ansible_user: "'ubuntu'"
filters:
  metadata:
    project: ansible-demo
keyed_groups:
  - key: openstack.metadata.environment
    prefix: env
  - key: openstack.metadata.role
    prefix: role

Set the project metadata key on every instance you provision (metadata = { project = "ansible-demo" } in OpenTofu, or meta: { project: ansible-demo } on the openstack.cloud.server task). The filter drops any instance that does not match, so an inventory pulled from a shared project surfaces only the matching VMs.

Pair the filter with --limit env_production or --limit role_web on the ansible-playbook invocation so a typo in the inventory cannot fan out a play across the wrong group.

Group instances with keyed_groups#

The keyed_groups block translates instance attributes into Ansible groups. Anything visible to the OpenStack API can become a group key. The most useful ones for Quake AI:

KeyGroup prefix exampleWhen to use
openstack.metadata.environmentenv_productionSeparate dev, staging, and production fleets
openstack.metadata.rolerole_webTarget web hosts vs database hosts
openstack.flavor.nameflavor_m2a_largeApply config that depends on instance size
openstack.image.nameimage_ubuntu_24_04Branch tasks by base image (empty for volume-booted instances)
openstack.metadata.teamteam_platformMulti-team projects sharing one Quake AI project

Set instance metadata at provisioning time. With OpenTofu:

HCL
resource "openstack_compute_instance_v2" "web" {
  name = "web-01"

  metadata = {
    environment = "production"
    role        = "web"
    team        = "platform"
  }
}

Or with the openstack.cloud.server module (see the provisioning guide).

For finer control, combine keyed_groups with groups (boolean expressions) and compose (computed host vars). The full schema lives in the openstack.cloud.openstack plugin documentation.

Run a playbook against the dynamic inventory#

Create site.yml:

YAML
---
- name: Configure production web hosts
  hosts: env_production:&role_web
  become: true

  tasks:
    - name: Wait for SSH
      ansible.builtin.wait_for_connection:
        timeout: 300

    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present

The hosts: env_production:&role_web selector targets instances that are in both groups (production environment and web role). Run it:

bash
ansible-playbook -i inventory/openstack.yml site.yml

To narrow the run further on the command line, use --limit:

bash
ansible-playbook -i inventory/openstack.yml site.yml --limit env_production
ansible-playbook -i inventory/openstack.yml site.yml --limit web-01

The same inventory file works for ad-hoc commands:

bash
ansible -i inventory/openstack.yml env_staging -m ansible.builtin.ping

Tune performance with expand_hostvars#

By default, the plugin returns the basic instance attributes that Ansible needs to connect: name, address, image, flavor, metadata. With expand_hostvars: true, the plugin makes additional API calls per host to fetch volume and port details. That extra information is occasionally useful (for example, when a playbook needs to read the attached volume names), but the per-host API calls become a bottleneck on large fleets.

Recommendation: leave expand_hostvars: false for routine operations. Set it to true only when a playbook explicitly needs Cinder volume or Neutron port details that are not already in metadata.

For multi-step pipelines that re-run the same inventory query many times, enable caching:

YAML
plugin: openstack.cloud.openstack
clouds:
  - rumble
cache: true
cache_plugin: jsonfile
cache_connection: ~/.cache/ansible/inventory
cache_timeout: 300

A 300-second cache eliminates redundant API calls during a single CI pipeline run while still picking up fleet changes between runs.

Tear down#

The dynamic inventory plugin does not create any state. To stop using it, point your ansible-playbook invocations back at a static inventory file or remove the inventory/openstack.yml file from your project. The Quake AI instances are unaffected.

See also#

Usage Guidelines

The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.

For the full policy, see Usage Guidelines.

Last validated: 18.06.2026

Quick answers

Was this page helpful?