all:
  children:
    zuul_unreachable:
      hosts: {}
  hosts:
    debian-bookworm:
      ansible_connection: ssh
      ansible_host: 2a13:1a81:8000:3124:f816:3eff:fe9f:9d59
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      nodepool:
        az: nova
        cloud: regiocloud-a-ci
        external_id: 0873df12-acc1-4d32-b6af-79ad21b6d10d
        host_id: 5afd8fc909abddfe2ce3a956a91bbe56629648b7209fd0acfa541862
        interface_ip: 2a13:1a81:8000:3124:f816:3eff:fe9f:9d59
        label: debian-bookworm
        node_properties: {}
        private_ipv4: 10.128.0.108
        private_ipv6: null
        provider: regiocloud-a
        public_ipv4: 81.163.193.159
        public_ipv6: 2a13:1a81:8000:3124:f816:3eff:fe9f:9d59
        region: null
        slot: null
      tox_environment:
        NOSE_HTML_OUT_FILE: nose_results.html
        NOSE_WITH_HTML_OUTPUT: 1
        NOSE_WITH_XUNIT: 1
      tox_envlist: drift-test
  vars:
    tox_environment:
      NOSE_HTML_OUT_FILE: nose_results.html
      NOSE_WITH_HTML_OUTPUT: 1
      NOSE_WITH_XUNIT: 1
    tox_envlist: drift-test
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-config/zuul.d/jobs.yaml@main#2>'
      - '<Job unittests explicit: None implied: {MatchAny:{ImpliedBranchMatcher:master}}
        source: osism/openinfra-zuul-jobs/zuul.d/general-jobs.yaml@master#9>'
      - '<Job tox explicit: None implied: {MatchAny:{ImpliedBranchMatcher:master}}
        source: osism/openinfra-zuul-jobs/zuul.d/python-jobs.yaml@master#1>'
      - '<Job release-tox-drift-test explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/release/.zuul.yaml@main#2>'
      - '<Job release-tox-drift-test explicit: None implied: None source: osism/release/.zuul.yaml@main#14>'
      ansible_version: '9'
      attempts: 1
      branch: main
      build: c4a0839faf134dcbb39ebaaa6125dcd4
      build_refs:
      - branch: main
        change: '2561'
        change_message: "Add drift-detection tooling (check-drift) to the release
          repo\n\n## What\n\nAdds `check-drift.py`, a plugin-based drift detector
          for OSISM's image and\nversion wiring, and wires it into CI. It catches
          divergence \u2014 *when it is\nintroduced* \u2014 before it breaks a deploy
          or a gate, across two check groups\n(`--group image | kolla | all`).\n\n**Image
          version pins** \u2014 follows an image's version down the chain\nrelease
          `base.yml` \u2192 rendered manager `images.yml` \u2192 role defaults:\n\n-
          `release_vs_manager` \u2014 the rendered manager `images.yml` disagrees
          with the release pin\n- `role_shadows` \u2014 a role-default tag shadows
          the release pin (LIVE = the role default actually deploys; DORMANT = the
          release pin wins)\n- `role_unpinned` \u2014 a role-default tag with no release
          pin\n- `image_orphan` \u2014 a manager image alias is emitted but consumed
          by no role or playbook\n\n**Kolla container-image toolchain** \u2014 divergence
          between the OSISM consumer repos\n(`defaults`, `release`, `generics`, the
          container-image repos) and upstream\n`openstack/kolla` / `openstack/kolla-ansible`:\n\n-
          `kolla_enablement_orphan` \u2014 OSISM enables a service upstream no longer
          defines\n- `kolla_groupvars_missing` \u2014 an upstream `group_vars/all`
          var OSISM's defaults never mirrored, so it is undefined at deploy/upgrade
          (the add-direction mirror of `kolla_enablement_orphan`; the class behind
          the 2025.2 `keystone_listen_port` break)\n- `kolla_orphan_config` \u2014
          dead companion vars / image blocks for an orphaned service\n- `kolla_secrets_orphan`
          \u2014 an OSISM kolla secret with no upstream counterpart\n- `kolla_enablement_build`
          \u2014 an enabled, upstream-buildable service missing from the build set\n-
          `kolla_version_chain_upstream` \u2014 an upstream `docker/` service with
          no version-pin line\n- `kolla_version_chain_inner` \u2014 a referenced version
          pin that resolves to nothing (inert pin)\n- `kolla_inventory` \u2014 an
          upstream inventory group missing from the OSISM `50/51-kolla` files\n\nThe
          framework lands first (empty registry, flags nothing on its own), then the\nplugins
          in lifecycle order, each reviewable in isolation. Sources resolve\nremotely
          (GitHub) by default, so the detector runs anywhere including CI;\n`--base-dir`
          reads local checkouts. An allowlist marks intentional exceptions\n(each
          requiring a reason); entries that match nothing are reported stale, so\nsuppressions
          can't silently rot. Exit codes: 0 clean / 1 drift or stale / 2 input\nerror.
          Full reference: `docs/check-drift-image.md`, `docs/check-drift-kolla.md`\n(incl.
          the `osism/defaults` release/bake model and why the range-aware checks\ncompare
          against the union of supported releases).\n\n## Commits (each independently
          green \u2014 unit tests pass at every commit)\n\n- `drift: add plugin framework
          and driver`\n- `drift: add image-drift plugins`\n- `drift: add kolla-drift
          plugins`\n- `drift: add drift-detection docs`\n- `drift: wire tox and Zuul
          CI`\n- `kolla: read enable flags from all defaults files` \u2014 make the
          enable-flag readers layout-agnostic (read `enable_*` from the union of `all/*.yml`,
          not just `099-kolla.yml`), so a defaults reorganization can't silently drop
          a flag\n- `docs: explain defaults bake model and unions`\n- `kolla: add
          kolla_groupvars_missing drift check` \u2014 the add-direction group_vars
          check (both OSISM delivery paths counted: `osism/defaults` + the rendered
          `container-image-kolla-ansible` `versions.yml`)\n- `kolla: allowlist eutron_external_interface
          typo`\n\n## CI\n\n- **`release-tox-drift-test`** \u2014 the unit suite (`tox
          -e drift-test`, 248 tests).\n  Runs in **check** (gates this PR) and periodic-daily.\n-
          **`release-tox-drift`** \u2014 the detector (`tox -e drift`, `check-drift.py\n
          \ --group all`). Runs **periodic-daily only**: it reads live upstream state,
          so\n  it's unfit as a PR gate. It exits non-zero when drift exists, so the
          daily job\n  goes **red** with the report in its log.\n\n## What this finds
          in practice\n\nRun against the live OSISM repos today, the detector reports
          **73 actionable\nitems, 42 allowlisted, 0 stale** (115 total):\n\n- **image
          pins** \u2014 1 rendered-vs-release disagreement (`ara_server`), 3 LIVE\n
          \ role-default shadows that actually deploy (`dnsmasq`, `scaphandre`, `stepca`),\n
          \ 9 DORMANT lags, 4 unpinned tags (`httpd`, `httpd_data`, `kepler`,\n  `thanos_sidecar`),
          and 3 orphaned manager aliases (`nginx`, `osism_netbox`,\n  `registry`).\n-
          **kolla toolchain** \u2014 now surfaces real drift (it was reported clean
          before\n  this branch). The layout-agnostic enable-flag reader scans `enable_*`
          across\n  **all** `osism/defaults` `all/*.yml` files (not just `099-kolla.yml`),
          surfacing\n  **9 enablement orphans** (`auditd`, `chrony`, `openstack_exporter`,
          `karbor`,\n  `qinling`, `searchlight`, three `horizon_*`) and their **9
          dead companion vars**\n  \u2014 some are removed-upstream projects to drop,
          some are OSISM inventions to\n  allowlist. The new add-direction `kolla_groupvars_missing`
          surfaces **35\n  upstream `group_vars/all` keys OSISM never mirrored** \u2014
          the `osism/defaults`\n  re-sync backlog behind the 2025.2 `keystone_listen_port`
          upgrade break, incl.\n  `enable_ironic_pxe_filter` and the `nova_database_*`/`nova_cell0_database_*`\n
          \ batch. `eutron_external_interface` is allowlisted (upstream typo for\n
          \ `neutron_external_interface`, awaiting\n  https://review.opendev.org/c/openstack/kolla-ansible/+/985031).\n\nEarlier,
          the removal-direction kolla checks already drove a wave of cleanups\nacross
          the consumer repos (kolla actionable-drift 81 \u2192 0 at the time, all
          merged);\nthis branch's layout-agnostic reader and new add-direction check
          now surface a\nfurther backlog \u2014 chiefly the `osism/defaults` re-sync
          the `keystone_listen_port`\nbreak called for:\n\n- osism/defaults#291\n-
          osism/container-image-kolla-ansible#924\n- osism/release#2551\n- osism/container-images-kolla#745\n-
          osism/generics#599\n- osism/generics#601\n\nIt self-audits its allowlist
          too: an entry that no longer suppresses real drift is\na hard error (exit
          1), so suppressions can't silently rot.\n\nBecause the detector is daily-only,
          this output does not appear in this PR's\nchecks. Full `release-tox-drift`
          report against current upstream\n(`check-drift.py --group all`):\n\n```\nChecks
          follow an image's version pin: release base.yml \u2192 rendered manager
          images.yml \u2192 role defaults.\n\nrelease_vs_manager \u2014 1 image tags
          in the rendered manager images.yml\ndisagree with the release base.yml pins:\n\n
          \   ara_server\n\n  Fix: re-render environments/manager/images.yml from
          the current release,\n       or allowlist the entry if the divergence is
          intentional (e.g. a\n       rolling-release image pinned to 'latest').\n
          \ Refs: release/latest/base.yml\n        testbed/environments/manager/images.yml\n\nrole_shadows
          \u2014 9 DORMANT \u2014 overridden by the rendered images.yml; the\nrelease
          pin wins at deploy:\n\n    adminer_tag (4.7 \u2192 5.4.2)   roles/adminer/defaults/main.yml\n
          \   ara_server_mariadb_tag (11.8.3 \u2192 11.8.4)   roles/manager/defaults/main.yml\n
          \   manager_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/manager/defaults/main.yml\n
          \   vault_tag (1.21.0 \u2192 1.21.4)   roles/manager/defaults/main.yml\n
          \   netbox_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/netbox/defaults/main.yml\n
          \   postgres_tag (16.10-alpine \u2192 16.11-alpine)   roles/netbox/defaults/main.yml\n
          \   nexus_tag (3.82.0 \u2192 3.93.1)   roles/nexus/defaults/main.yml\n    phpmyadmin_tag
          (5.2 \u2192 5.2.3)   roles/phpmyadmin/defaults/main.yml\n    traefik_tag
          (v3.4.4 \u2192 v3.5.0)   roles/traefik/defaults/main.yml\n\n  Fix: lower
          priority; sync when convenient.\n  Refs: release/latest/base.yml\n\nrole_shadows
          \u2014 3 LIVE \u2014 no images.yml override; the role default is what\nactually
          deploys:\n\n    dnsmasq_tag (2.90 \u2192 2.91)   roles/dnsmasq/defaults/main.yml\n
          \   scaphandre_tag (1.0.0 \u2192 1.0.2)   roles/scaphandre/defaults/main.yml\n
          \   stepca_tag (0.28.4 \u2192 0.30.2)   roles/stepca/defaults/main.yml\n\n
          \ Fix: add `<alias>_tag`/`<alias>_image` to the manager render template\n
          \      (images.yml.j2) so the latest/base.yml pin governs the deployed\n
          \      version.\n  Refs: release/latest/base.yml\n\nrole_unpinned \u2014
          4 <alias>_tag pins in role defaults with no release base.yml\npin:\n\n    httpd_tag
          (alpine, no release pin)   roles/httpd/defaults/main.yml\n    httpd_data_tag
          (latest, no release pin)   roles/httpd/defaults/main.yml\n    kepler_tag
          (v0.11.2, no release pin)   roles/kepler/defaults/main.yml\n    thanos_sidecar_tag
          (v0.32.5, no release pin)   roles/thanos_sidecar/defaults/main.yml\n\n  Fix:
          add a pin to release base.yml (and wire <alias>_tag into the manager\n       render
          template) to make it release-managed, or allowlist it if the\n       image
          is intentionally role-managed.\n  Refs: release/latest/base.yml\n\nimage_orphan
          \u2014 3 image vars emitted by the manager images.yml that no role\nor playbook
          consumes:\n\n    nginx, osism_netbox, registry\n\n  Fix: remove the orphaned
          <alias>_tag/<alias>_image (and its etc/images.yml\n       + role-default
          remnants), or allowlist it if the image is genuinely\n       consumed in
          a form this scan misses \u2014 it flags an alias only when no\n       literal
          {{ <alias>_image }} reference is found in the scanned\n       roles/playbooks.\n
          \ Refs: ansible-collection-services/roles/, ansible-playbooks-manager/playbooks/\n
          \       generics/environments/manager/images.yml\n\nChecks follow a service's
          path: enabled \u2192 built \u2192 version-pinned \u2192 deployed.\n\nkolla_enablement_orphan
          \u2014 9 OSISM enable flags whose service upstream\nkolla-ansible no longer
          defines at any supported release; the service was\nremoved or renamed upstream,
          leaving the flag orphaned:\n\n    auditd, chrony, horizon_karbor, horizon_qinling,
          horizon_searchlight,\n    karbor, openstack_exporter, qinling, searchlight\n\n
          \ Fix: remove the stale enable_<name> from osism/defaults, or migrate it
          to\n       the upstream replacement. Some flags (e.g. common, kolla_operations)\n
          \      are OSISM inventions with no upstream counterpart \u2014 keep those\n
          \      allowlisted rather than removed.\n  Refs: openstack/kolla-ansible
          group_vars enable-defaults @ supported refs\n        osism/defaults all/*.yml\n\nkolla_groupvars_missing
          \u2014 35 upstream kolla-ansible group_vars OSISM\ndefaults never mirrored;
          each is undefined in the deploy var context and\naborts any role task that
          uses it:\n\n    ceph_cinder_backup_keyring, ceph_cinder_keyring, ceph_glance_keyring,\n
          \   ceph_gnocchi_keyring, ceph_manila_keyring, ceph_nova_keyring,\n    distro_python_version_map,
          enable_cinder_backend_hnas_nfs,\n    enable_ironic_pxe_filter, enable_swift_recon,
          enable_swift_s3api,\n    glance_backend_swift, keystone_listen_port, nova_cell0_database_address,\n
          \   nova_cell0_database_name, nova_cell0_database_password,\n    nova_cell0_database_shard_id,
          nova_cell0_database_user,\n    nova_database_address, nova_database_name,
          nova_database_user,\n    prometheus_msteams_port, prometheus_msteams_webhook_url,\n
          \   swift_account_server_port, swift_container_server_port,\n    swift_external_fqdn,
          swift_internal_base_endpoint,\n    swift_internal_endpoint, swift_internal_fqdn,
          swift_object_server_port,\n    swift_proxy_server_listen_port, swift_proxy_server_port,\n
          \   swift_public_base_endpoint, swift_public_endpoint, swift_rsync_port\n\n
          \ Fix: mirror the missing var into osism/defaults all/*.yml (copying\n       upstream's
          definition \u2014 harmless when the service is off, needed when\n       an
          environment enables it), or allowlist it with a reason if OSISM\n       deliberately
          omits it: the related service is not shipped/supported\n       by OSISM
          at all (so the var is never evaluated anywhere), a var OSISM\n       supplies
          another way, or an upstream typo.\n  Refs: openstack/kolla-ansible group_vars/all
          @ supported refs\n        osism/defaults all/*.yml\n\nkolla_orphan_config
          \u2014 2 dead companion/image config vars for services\nupstream removed
          (their enable_<name> flag is reported separately by\nkolla_enablement_orphan);
          these must be removed too:\n\n    chrony_image, chrony_tag\n\n  Fix: remove
          these vars from the listed osism/defaults file, or allowlist\n       any
          that are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/002-images-kolla.yml\n\nkolla_orphan_config \u2014 2 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    chrony_allowed_subnets, chrony_servers\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-generic.yml\n\nkolla_orphan_config \u2014 5 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    openstack_exporter_clouds_yml_cloud, openstack_exporter_clouds_yml_path,\n
          \   openstack_exporter_secure_yml_path,\n    openstack_exporter_service_monitor_scrape_interval,\n
          \   openstack_exporter_service_monitor_scrape_timeout\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-infrastructure.yml\n\nSummary: 73 to act on, 42 allowlisted, 0 stale
          allowlist entries (115 total)\n```\n\n### How to read it\n- **image pins**
          \u2014 DORMANT `role_shadows` lag but the release pin governs at\n  deploy
          (low priority); LIVE `role_shadows` (`dnsmasq`, `scaphandre`, `stepca`)\n
          \ actually deploy the role default (wire into `images.yml.j2`); `role_unpinned`\n
          \ (`httpd*`, `kepler`, `thanos_sidecar`) have no release pin; `image_orphan`\n
          \ (`nginx`, `osism_netbox`, `registry`) are emitted but consumed nowhere.\n-
          **kolla removal-direction** \u2014 `kolla_enablement_orphan` + `kolla_orphan_config`:\n
          \ stale `enable_*` flags and their companion vars for services upstream
          dropped;\n  drop the removed projects, allowlist the OSISM inventions.\n-
          **kolla add-direction** \u2014 `kolla_groupvars_missing`: the `osism/defaults`\n
          \ re-sync backlog. The right fix for most is to mirror the upstream var
          into\n  `osism/defaults` (harmless when the service is off, correct when
          an environment\n  enables it \u2014 e.g. metalbox turns on ironic, so `enable_ironic_pxe_filter`
          bites\n  there); allowlist only genuine divergences (the `eutron` typo).\n\n>
          The detector runs unauthenticated against GitHub (no token support in\n>
          `source.py`), same as generics' existing daily job. A heavy run can occasionally\n>
          hit API rate limits and fail without a report; adding `GITHUB_TOKEN` support
          is a\n> sensible follow-up.\n"
        change_url: https://github.com/osism/release/pull/2561
        commit_id: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        patchset: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/release
          name: osism/release
          short_name: release
        src_dir: src/github.com/osism/release
        topic: null
      buildset: 8451434d6def4213bad3e07f363fd9c4
      buildset_refs:
      - branch: main
        change: '2561'
        change_message: "Add drift-detection tooling (check-drift) to the release
          repo\n\n## What\n\nAdds `check-drift.py`, a plugin-based drift detector
          for OSISM's image and\nversion wiring, and wires it into CI. It catches
          divergence \u2014 *when it is\nintroduced* \u2014 before it breaks a deploy
          or a gate, across two check groups\n(`--group image | kolla | all`).\n\n**Image
          version pins** \u2014 follows an image's version down the chain\nrelease
          `base.yml` \u2192 rendered manager `images.yml` \u2192 role defaults:\n\n-
          `release_vs_manager` \u2014 the rendered manager `images.yml` disagrees
          with the release pin\n- `role_shadows` \u2014 a role-default tag shadows
          the release pin (LIVE = the role default actually deploys; DORMANT = the
          release pin wins)\n- `role_unpinned` \u2014 a role-default tag with no release
          pin\n- `image_orphan` \u2014 a manager image alias is emitted but consumed
          by no role or playbook\n\n**Kolla container-image toolchain** \u2014 divergence
          between the OSISM consumer repos\n(`defaults`, `release`, `generics`, the
          container-image repos) and upstream\n`openstack/kolla` / `openstack/kolla-ansible`:\n\n-
          `kolla_enablement_orphan` \u2014 OSISM enables a service upstream no longer
          defines\n- `kolla_groupvars_missing` \u2014 an upstream `group_vars/all`
          var OSISM's defaults never mirrored, so it is undefined at deploy/upgrade
          (the add-direction mirror of `kolla_enablement_orphan`; the class behind
          the 2025.2 `keystone_listen_port` break)\n- `kolla_orphan_config` \u2014
          dead companion vars / image blocks for an orphaned service\n- `kolla_secrets_orphan`
          \u2014 an OSISM kolla secret with no upstream counterpart\n- `kolla_enablement_build`
          \u2014 an enabled, upstream-buildable service missing from the build set\n-
          `kolla_version_chain_upstream` \u2014 an upstream `docker/` service with
          no version-pin line\n- `kolla_version_chain_inner` \u2014 a referenced version
          pin that resolves to nothing (inert pin)\n- `kolla_inventory` \u2014 an
          upstream inventory group missing from the OSISM `50/51-kolla` files\n\nThe
          framework lands first (empty registry, flags nothing on its own), then the\nplugins
          in lifecycle order, each reviewable in isolation. Sources resolve\nremotely
          (GitHub) by default, so the detector runs anywhere including CI;\n`--base-dir`
          reads local checkouts. An allowlist marks intentional exceptions\n(each
          requiring a reason); entries that match nothing are reported stale, so\nsuppressions
          can't silently rot. Exit codes: 0 clean / 1 drift or stale / 2 input\nerror.
          Full reference: `docs/check-drift-image.md`, `docs/check-drift-kolla.md`\n(incl.
          the `osism/defaults` release/bake model and why the range-aware checks\ncompare
          against the union of supported releases).\n\n## Commits (each independently
          green \u2014 unit tests pass at every commit)\n\n- `drift: add plugin framework
          and driver`\n- `drift: add image-drift plugins`\n- `drift: add kolla-drift
          plugins`\n- `drift: add drift-detection docs`\n- `drift: wire tox and Zuul
          CI`\n- `kolla: read enable flags from all defaults files` \u2014 make the
          enable-flag readers layout-agnostic (read `enable_*` from the union of `all/*.yml`,
          not just `099-kolla.yml`), so a defaults reorganization can't silently drop
          a flag\n- `docs: explain defaults bake model and unions`\n- `kolla: add
          kolla_groupvars_missing drift check` \u2014 the add-direction group_vars
          check (both OSISM delivery paths counted: `osism/defaults` + the rendered
          `container-image-kolla-ansible` `versions.yml`)\n- `kolla: allowlist eutron_external_interface
          typo`\n\n## CI\n\n- **`release-tox-drift-test`** \u2014 the unit suite (`tox
          -e drift-test`, 248 tests).\n  Runs in **check** (gates this PR) and periodic-daily.\n-
          **`release-tox-drift`** \u2014 the detector (`tox -e drift`, `check-drift.py\n
          \ --group all`). Runs **periodic-daily only**: it reads live upstream state,
          so\n  it's unfit as a PR gate. It exits non-zero when drift exists, so the
          daily job\n  goes **red** with the report in its log.\n\n## What this finds
          in practice\n\nRun against the live OSISM repos today, the detector reports
          **73 actionable\nitems, 42 allowlisted, 0 stale** (115 total):\n\n- **image
          pins** \u2014 1 rendered-vs-release disagreement (`ara_server`), 3 LIVE\n
          \ role-default shadows that actually deploy (`dnsmasq`, `scaphandre`, `stepca`),\n
          \ 9 DORMANT lags, 4 unpinned tags (`httpd`, `httpd_data`, `kepler`,\n  `thanos_sidecar`),
          and 3 orphaned manager aliases (`nginx`, `osism_netbox`,\n  `registry`).\n-
          **kolla toolchain** \u2014 now surfaces real drift (it was reported clean
          before\n  this branch). The layout-agnostic enable-flag reader scans `enable_*`
          across\n  **all** `osism/defaults` `all/*.yml` files (not just `099-kolla.yml`),
          surfacing\n  **9 enablement orphans** (`auditd`, `chrony`, `openstack_exporter`,
          `karbor`,\n  `qinling`, `searchlight`, three `horizon_*`) and their **9
          dead companion vars**\n  \u2014 some are removed-upstream projects to drop,
          some are OSISM inventions to\n  allowlist. The new add-direction `kolla_groupvars_missing`
          surfaces **35\n  upstream `group_vars/all` keys OSISM never mirrored** \u2014
          the `osism/defaults`\n  re-sync backlog behind the 2025.2 `keystone_listen_port`
          upgrade break, incl.\n  `enable_ironic_pxe_filter` and the `nova_database_*`/`nova_cell0_database_*`\n
          \ batch. `eutron_external_interface` is allowlisted (upstream typo for\n
          \ `neutron_external_interface`, awaiting\n  https://review.opendev.org/c/openstack/kolla-ansible/+/985031).\n\nEarlier,
          the removal-direction kolla checks already drove a wave of cleanups\nacross
          the consumer repos (kolla actionable-drift 81 \u2192 0 at the time, all
          merged);\nthis branch's layout-agnostic reader and new add-direction check
          now surface a\nfurther backlog \u2014 chiefly the `osism/defaults` re-sync
          the `keystone_listen_port`\nbreak called for:\n\n- osism/defaults#291\n-
          osism/container-image-kolla-ansible#924\n- osism/release#2551\n- osism/container-images-kolla#745\n-
          osism/generics#599\n- osism/generics#601\n\nIt self-audits its allowlist
          too: an entry that no longer suppresses real drift is\na hard error (exit
          1), so suppressions can't silently rot.\n\nBecause the detector is daily-only,
          this output does not appear in this PR's\nchecks. Full `release-tox-drift`
          report against current upstream\n(`check-drift.py --group all`):\n\n```\nChecks
          follow an image's version pin: release base.yml \u2192 rendered manager
          images.yml \u2192 role defaults.\n\nrelease_vs_manager \u2014 1 image tags
          in the rendered manager images.yml\ndisagree with the release base.yml pins:\n\n
          \   ara_server\n\n  Fix: re-render environments/manager/images.yml from
          the current release,\n       or allowlist the entry if the divergence is
          intentional (e.g. a\n       rolling-release image pinned to 'latest').\n
          \ Refs: release/latest/base.yml\n        testbed/environments/manager/images.yml\n\nrole_shadows
          \u2014 9 DORMANT \u2014 overridden by the rendered images.yml; the\nrelease
          pin wins at deploy:\n\n    adminer_tag (4.7 \u2192 5.4.2)   roles/adminer/defaults/main.yml\n
          \   ara_server_mariadb_tag (11.8.3 \u2192 11.8.4)   roles/manager/defaults/main.yml\n
          \   manager_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/manager/defaults/main.yml\n
          \   vault_tag (1.21.0 \u2192 1.21.4)   roles/manager/defaults/main.yml\n
          \   netbox_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/netbox/defaults/main.yml\n
          \   postgres_tag (16.10-alpine \u2192 16.11-alpine)   roles/netbox/defaults/main.yml\n
          \   nexus_tag (3.82.0 \u2192 3.93.1)   roles/nexus/defaults/main.yml\n    phpmyadmin_tag
          (5.2 \u2192 5.2.3)   roles/phpmyadmin/defaults/main.yml\n    traefik_tag
          (v3.4.4 \u2192 v3.5.0)   roles/traefik/defaults/main.yml\n\n  Fix: lower
          priority; sync when convenient.\n  Refs: release/latest/base.yml\n\nrole_shadows
          \u2014 3 LIVE \u2014 no images.yml override; the role default is what\nactually
          deploys:\n\n    dnsmasq_tag (2.90 \u2192 2.91)   roles/dnsmasq/defaults/main.yml\n
          \   scaphandre_tag (1.0.0 \u2192 1.0.2)   roles/scaphandre/defaults/main.yml\n
          \   stepca_tag (0.28.4 \u2192 0.30.2)   roles/stepca/defaults/main.yml\n\n
          \ Fix: add `<alias>_tag`/`<alias>_image` to the manager render template\n
          \      (images.yml.j2) so the latest/base.yml pin governs the deployed\n
          \      version.\n  Refs: release/latest/base.yml\n\nrole_unpinned \u2014
          4 <alias>_tag pins in role defaults with no release base.yml\npin:\n\n    httpd_tag
          (alpine, no release pin)   roles/httpd/defaults/main.yml\n    httpd_data_tag
          (latest, no release pin)   roles/httpd/defaults/main.yml\n    kepler_tag
          (v0.11.2, no release pin)   roles/kepler/defaults/main.yml\n    thanos_sidecar_tag
          (v0.32.5, no release pin)   roles/thanos_sidecar/defaults/main.yml\n\n  Fix:
          add a pin to release base.yml (and wire <alias>_tag into the manager\n       render
          template) to make it release-managed, or allowlist it if the\n       image
          is intentionally role-managed.\n  Refs: release/latest/base.yml\n\nimage_orphan
          \u2014 3 image vars emitted by the manager images.yml that no role\nor playbook
          consumes:\n\n    nginx, osism_netbox, registry\n\n  Fix: remove the orphaned
          <alias>_tag/<alias>_image (and its etc/images.yml\n       + role-default
          remnants), or allowlist it if the image is genuinely\n       consumed in
          a form this scan misses \u2014 it flags an alias only when no\n       literal
          {{ <alias>_image }} reference is found in the scanned\n       roles/playbooks.\n
          \ Refs: ansible-collection-services/roles/, ansible-playbooks-manager/playbooks/\n
          \       generics/environments/manager/images.yml\n\nChecks follow a service's
          path: enabled \u2192 built \u2192 version-pinned \u2192 deployed.\n\nkolla_enablement_orphan
          \u2014 9 OSISM enable flags whose service upstream\nkolla-ansible no longer
          defines at any supported release; the service was\nremoved or renamed upstream,
          leaving the flag orphaned:\n\n    auditd, chrony, horizon_karbor, horizon_qinling,
          horizon_searchlight,\n    karbor, openstack_exporter, qinling, searchlight\n\n
          \ Fix: remove the stale enable_<name> from osism/defaults, or migrate it
          to\n       the upstream replacement. Some flags (e.g. common, kolla_operations)\n
          \      are OSISM inventions with no upstream counterpart \u2014 keep those\n
          \      allowlisted rather than removed.\n  Refs: openstack/kolla-ansible
          group_vars enable-defaults @ supported refs\n        osism/defaults all/*.yml\n\nkolla_groupvars_missing
          \u2014 35 upstream kolla-ansible group_vars OSISM\ndefaults never mirrored;
          each is undefined in the deploy var context and\naborts any role task that
          uses it:\n\n    ceph_cinder_backup_keyring, ceph_cinder_keyring, ceph_glance_keyring,\n
          \   ceph_gnocchi_keyring, ceph_manila_keyring, ceph_nova_keyring,\n    distro_python_version_map,
          enable_cinder_backend_hnas_nfs,\n    enable_ironic_pxe_filter, enable_swift_recon,
          enable_swift_s3api,\n    glance_backend_swift, keystone_listen_port, nova_cell0_database_address,\n
          \   nova_cell0_database_name, nova_cell0_database_password,\n    nova_cell0_database_shard_id,
          nova_cell0_database_user,\n    nova_database_address, nova_database_name,
          nova_database_user,\n    prometheus_msteams_port, prometheus_msteams_webhook_url,\n
          \   swift_account_server_port, swift_container_server_port,\n    swift_external_fqdn,
          swift_internal_base_endpoint,\n    swift_internal_endpoint, swift_internal_fqdn,
          swift_object_server_port,\n    swift_proxy_server_listen_port, swift_proxy_server_port,\n
          \   swift_public_base_endpoint, swift_public_endpoint, swift_rsync_port\n\n
          \ Fix: mirror the missing var into osism/defaults all/*.yml (copying\n       upstream's
          definition \u2014 harmless when the service is off, needed when\n       an
          environment enables it), or allowlist it with a reason if OSISM\n       deliberately
          omits it: the related service is not shipped/supported\n       by OSISM
          at all (so the var is never evaluated anywhere), a var OSISM\n       supplies
          another way, or an upstream typo.\n  Refs: openstack/kolla-ansible group_vars/all
          @ supported refs\n        osism/defaults all/*.yml\n\nkolla_orphan_config
          \u2014 2 dead companion/image config vars for services\nupstream removed
          (their enable_<name> flag is reported separately by\nkolla_enablement_orphan);
          these must be removed too:\n\n    chrony_image, chrony_tag\n\n  Fix: remove
          these vars from the listed osism/defaults file, or allowlist\n       any
          that are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/002-images-kolla.yml\n\nkolla_orphan_config \u2014 2 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    chrony_allowed_subnets, chrony_servers\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-generic.yml\n\nkolla_orphan_config \u2014 5 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    openstack_exporter_clouds_yml_cloud, openstack_exporter_clouds_yml_path,\n
          \   openstack_exporter_secure_yml_path,\n    openstack_exporter_service_monitor_scrape_interval,\n
          \   openstack_exporter_service_monitor_scrape_timeout\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-infrastructure.yml\n\nSummary: 73 to act on, 42 allowlisted, 0 stale
          allowlist entries (115 total)\n```\n\n### How to read it\n- **image pins**
          \u2014 DORMANT `role_shadows` lag but the release pin governs at\n  deploy
          (low priority); LIVE `role_shadows` (`dnsmasq`, `scaphandre`, `stepca`)\n
          \ actually deploy the role default (wire into `images.yml.j2`); `role_unpinned`\n
          \ (`httpd*`, `kepler`, `thanos_sidecar`) have no release pin; `image_orphan`\n
          \ (`nginx`, `osism_netbox`, `registry`) are emitted but consumed nowhere.\n-
          **kolla removal-direction** \u2014 `kolla_enablement_orphan` + `kolla_orphan_config`:\n
          \ stale `enable_*` flags and their companion vars for services upstream
          dropped;\n  drop the removed projects, allowlist the OSISM inventions.\n-
          **kolla add-direction** \u2014 `kolla_groupvars_missing`: the `osism/defaults`\n
          \ re-sync backlog. The right fix for most is to mirror the upstream var
          into\n  `osism/defaults` (harmless when the service is off, correct when
          an environment\n  enables it \u2014 e.g. metalbox turns on ironic, so `enable_ironic_pxe_filter`
          bites\n  there); allowlist only genuine divergences (the `eutron` typo).\n\n>
          The detector runs unauthenticated against GitHub (no token support in\n>
          `source.py`), same as generics' existing daily job. A heavy run can occasionally\n>
          hit API rate limits and fail without a report; adding `GITHUB_TOKEN` support
          is a\n> sensible follow-up.\n"
        change_url: https://github.com/osism/release/pull/2561
        commit_id: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        patchset: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/release
          name: osism/release
          short_name: release
        src_dir: src/github.com/osism/release
        topic: null
      change: '2561'
      change_message: "Add drift-detection tooling (check-drift) to the release repo\n\n##
        What\n\nAdds `check-drift.py`, a plugin-based drift detector for OSISM's image
        and\nversion wiring, and wires it into CI. It catches divergence \u2014 *when
        it is\nintroduced* \u2014 before it breaks a deploy or a gate, across two
        check groups\n(`--group image | kolla | all`).\n\n**Image version pins** \u2014
        follows an image's version down the chain\nrelease `base.yml` \u2192 rendered
        manager `images.yml` \u2192 role defaults:\n\n- `release_vs_manager` \u2014
        the rendered manager `images.yml` disagrees with the release pin\n- `role_shadows`
        \u2014 a role-default tag shadows the release pin (LIVE = the role default
        actually deploys; DORMANT = the release pin wins)\n- `role_unpinned` \u2014
        a role-default tag with no release pin\n- `image_orphan` \u2014 a manager
        image alias is emitted but consumed by no role or playbook\n\n**Kolla container-image
        toolchain** \u2014 divergence between the OSISM consumer repos\n(`defaults`,
        `release`, `generics`, the container-image repos) and upstream\n`openstack/kolla`
        / `openstack/kolla-ansible`:\n\n- `kolla_enablement_orphan` \u2014 OSISM enables
        a service upstream no longer defines\n- `kolla_groupvars_missing` \u2014 an
        upstream `group_vars/all` var OSISM's defaults never mirrored, so it is undefined
        at deploy/upgrade (the add-direction mirror of `kolla_enablement_orphan`;
        the class behind the 2025.2 `keystone_listen_port` break)\n- `kolla_orphan_config`
        \u2014 dead companion vars / image blocks for an orphaned service\n- `kolla_secrets_orphan`
        \u2014 an OSISM kolla secret with no upstream counterpart\n- `kolla_enablement_build`
        \u2014 an enabled, upstream-buildable service missing from the build set\n-
        `kolla_version_chain_upstream` \u2014 an upstream `docker/` service with no
        version-pin line\n- `kolla_version_chain_inner` \u2014 a referenced version
        pin that resolves to nothing (inert pin)\n- `kolla_inventory` \u2014 an upstream
        inventory group missing from the OSISM `50/51-kolla` files\n\nThe framework
        lands first (empty registry, flags nothing on its own), then the\nplugins
        in lifecycle order, each reviewable in isolation. Sources resolve\nremotely
        (GitHub) by default, so the detector runs anywhere including CI;\n`--base-dir`
        reads local checkouts. An allowlist marks intentional exceptions\n(each requiring
        a reason); entries that match nothing are reported stale, so\nsuppressions
        can't silently rot. Exit codes: 0 clean / 1 drift or stale / 2 input\nerror.
        Full reference: `docs/check-drift-image.md`, `docs/check-drift-kolla.md`\n(incl.
        the `osism/defaults` release/bake model and why the range-aware checks\ncompare
        against the union of supported releases).\n\n## Commits (each independently
        green \u2014 unit tests pass at every commit)\n\n- `drift: add plugin framework
        and driver`\n- `drift: add image-drift plugins`\n- `drift: add kolla-drift
        plugins`\n- `drift: add drift-detection docs`\n- `drift: wire tox and Zuul
        CI`\n- `kolla: read enable flags from all defaults files` \u2014 make the
        enable-flag readers layout-agnostic (read `enable_*` from the union of `all/*.yml`,
        not just `099-kolla.yml`), so a defaults reorganization can't silently drop
        a flag\n- `docs: explain defaults bake model and unions`\n- `kolla: add kolla_groupvars_missing
        drift check` \u2014 the add-direction group_vars check (both OSISM delivery
        paths counted: `osism/defaults` + the rendered `container-image-kolla-ansible`
        `versions.yml`)\n- `kolla: allowlist eutron_external_interface typo`\n\n##
        CI\n\n- **`release-tox-drift-test`** \u2014 the unit suite (`tox -e drift-test`,
        248 tests).\n  Runs in **check** (gates this PR) and periodic-daily.\n- **`release-tox-drift`**
        \u2014 the detector (`tox -e drift`, `check-drift.py\n  --group all`). Runs
        **periodic-daily only**: it reads live upstream state, so\n  it's unfit as
        a PR gate. It exits non-zero when drift exists, so the daily job\n  goes **red**
        with the report in its log.\n\n## What this finds in practice\n\nRun against
        the live OSISM repos today, the detector reports **73 actionable\nitems, 42
        allowlisted, 0 stale** (115 total):\n\n- **image pins** \u2014 1 rendered-vs-release
        disagreement (`ara_server`), 3 LIVE\n  role-default shadows that actually
        deploy (`dnsmasq`, `scaphandre`, `stepca`),\n  9 DORMANT lags, 4 unpinned
        tags (`httpd`, `httpd_data`, `kepler`,\n  `thanos_sidecar`), and 3 orphaned
        manager aliases (`nginx`, `osism_netbox`,\n  `registry`).\n- **kolla toolchain**
        \u2014 now surfaces real drift (it was reported clean before\n  this branch).
        The layout-agnostic enable-flag reader scans `enable_*` across\n  **all**
        `osism/defaults` `all/*.yml` files (not just `099-kolla.yml`), surfacing\n
        \ **9 enablement orphans** (`auditd`, `chrony`, `openstack_exporter`, `karbor`,\n
        \ `qinling`, `searchlight`, three `horizon_*`) and their **9 dead companion
        vars**\n  \u2014 some are removed-upstream projects to drop, some are OSISM
        inventions to\n  allowlist. The new add-direction `kolla_groupvars_missing`
        surfaces **35\n  upstream `group_vars/all` keys OSISM never mirrored** \u2014
        the `osism/defaults`\n  re-sync backlog behind the 2025.2 `keystone_listen_port`
        upgrade break, incl.\n  `enable_ironic_pxe_filter` and the `nova_database_*`/`nova_cell0_database_*`\n
        \ batch. `eutron_external_interface` is allowlisted (upstream typo for\n  `neutron_external_interface`,
        awaiting\n  https://review.opendev.org/c/openstack/kolla-ansible/+/985031).\n\nEarlier,
        the removal-direction kolla checks already drove a wave of cleanups\nacross
        the consumer repos (kolla actionable-drift 81 \u2192 0 at the time, all merged);\nthis
        branch's layout-agnostic reader and new add-direction check now surface a\nfurther
        backlog \u2014 chiefly the `osism/defaults` re-sync the `keystone_listen_port`\nbreak
        called for:\n\n- osism/defaults#291\n- osism/container-image-kolla-ansible#924\n-
        osism/release#2551\n- osism/container-images-kolla#745\n- osism/generics#599\n-
        osism/generics#601\n\nIt self-audits its allowlist too: an entry that no longer
        suppresses real drift is\na hard error (exit 1), so suppressions can't silently
        rot.\n\nBecause the detector is daily-only, this output does not appear in
        this PR's\nchecks. Full `release-tox-drift` report against current upstream\n(`check-drift.py
        --group all`):\n\n```\nChecks follow an image's version pin: release base.yml
        \u2192 rendered manager images.yml \u2192 role defaults.\n\nrelease_vs_manager
        \u2014 1 image tags in the rendered manager images.yml\ndisagree with the
        release base.yml pins:\n\n    ara_server\n\n  Fix: re-render environments/manager/images.yml
        from the current release,\n       or allowlist the entry if the divergence
        is intentional (e.g. a\n       rolling-release image pinned to 'latest').\n
        \ Refs: release/latest/base.yml\n        testbed/environments/manager/images.yml\n\nrole_shadows
        \u2014 9 DORMANT \u2014 overridden by the rendered images.yml; the\nrelease
        pin wins at deploy:\n\n    adminer_tag (4.7 \u2192 5.4.2)   roles/adminer/defaults/main.yml\n
        \   ara_server_mariadb_tag (11.8.3 \u2192 11.8.4)   roles/manager/defaults/main.yml\n
        \   manager_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/manager/defaults/main.yml\n
        \   vault_tag (1.21.0 \u2192 1.21.4)   roles/manager/defaults/main.yml\n    netbox_redis_tag
        (7.4.6-alpine \u2192 7.4.7-alpine)   roles/netbox/defaults/main.yml\n    postgres_tag
        (16.10-alpine \u2192 16.11-alpine)   roles/netbox/defaults/main.yml\n    nexus_tag
        (3.82.0 \u2192 3.93.1)   roles/nexus/defaults/main.yml\n    phpmyadmin_tag
        (5.2 \u2192 5.2.3)   roles/phpmyadmin/defaults/main.yml\n    traefik_tag (v3.4.4
        \u2192 v3.5.0)   roles/traefik/defaults/main.yml\n\n  Fix: lower priority;
        sync when convenient.\n  Refs: release/latest/base.yml\n\nrole_shadows \u2014
        3 LIVE \u2014 no images.yml override; the role default is what\nactually deploys:\n\n
        \   dnsmasq_tag (2.90 \u2192 2.91)   roles/dnsmasq/defaults/main.yml\n    scaphandre_tag
        (1.0.0 \u2192 1.0.2)   roles/scaphandre/defaults/main.yml\n    stepca_tag
        (0.28.4 \u2192 0.30.2)   roles/stepca/defaults/main.yml\n\n  Fix: add `<alias>_tag`/`<alias>_image`
        to the manager render template\n       (images.yml.j2) so the latest/base.yml
        pin governs the deployed\n       version.\n  Refs: release/latest/base.yml\n\nrole_unpinned
        \u2014 4 <alias>_tag pins in role defaults with no release base.yml\npin:\n\n
        \   httpd_tag (alpine, no release pin)   roles/httpd/defaults/main.yml\n    httpd_data_tag
        (latest, no release pin)   roles/httpd/defaults/main.yml\n    kepler_tag (v0.11.2,
        no release pin)   roles/kepler/defaults/main.yml\n    thanos_sidecar_tag (v0.32.5,
        no release pin)   roles/thanos_sidecar/defaults/main.yml\n\n  Fix: add a pin
        to release base.yml (and wire <alias>_tag into the manager\n       render
        template) to make it release-managed, or allowlist it if the\n       image
        is intentionally role-managed.\n  Refs: release/latest/base.yml\n\nimage_orphan
        \u2014 3 image vars emitted by the manager images.yml that no role\nor playbook
        consumes:\n\n    nginx, osism_netbox, registry\n\n  Fix: remove the orphaned
        <alias>_tag/<alias>_image (and its etc/images.yml\n       + role-default remnants),
        or allowlist it if the image is genuinely\n       consumed in a form this
        scan misses \u2014 it flags an alias only when no\n       literal {{ <alias>_image
        }} reference is found in the scanned\n       roles/playbooks.\n  Refs: ansible-collection-services/roles/,
        ansible-playbooks-manager/playbooks/\n        generics/environments/manager/images.yml\n\nChecks
        follow a service's path: enabled \u2192 built \u2192 version-pinned \u2192
        deployed.\n\nkolla_enablement_orphan \u2014 9 OSISM enable flags whose service
        upstream\nkolla-ansible no longer defines at any supported release; the service
        was\nremoved or renamed upstream, leaving the flag orphaned:\n\n    auditd,
        chrony, horizon_karbor, horizon_qinling, horizon_searchlight,\n    karbor,
        openstack_exporter, qinling, searchlight\n\n  Fix: remove the stale enable_<name>
        from osism/defaults, or migrate it to\n       the upstream replacement. Some
        flags (e.g. common, kolla_operations)\n       are OSISM inventions with no
        upstream counterpart \u2014 keep those\n       allowlisted rather than removed.\n
        \ Refs: openstack/kolla-ansible group_vars enable-defaults @ supported refs\n
        \       osism/defaults all/*.yml\n\nkolla_groupvars_missing \u2014 35 upstream
        kolla-ansible group_vars OSISM\ndefaults never mirrored; each is undefined
        in the deploy var context and\naborts any role task that uses it:\n\n    ceph_cinder_backup_keyring,
        ceph_cinder_keyring, ceph_glance_keyring,\n    ceph_gnocchi_keyring, ceph_manila_keyring,
        ceph_nova_keyring,\n    distro_python_version_map, enable_cinder_backend_hnas_nfs,\n
        \   enable_ironic_pxe_filter, enable_swift_recon, enable_swift_s3api,\n    glance_backend_swift,
        keystone_listen_port, nova_cell0_database_address,\n    nova_cell0_database_name,
        nova_cell0_database_password,\n    nova_cell0_database_shard_id, nova_cell0_database_user,\n
        \   nova_database_address, nova_database_name, nova_database_user,\n    prometheus_msteams_port,
        prometheus_msteams_webhook_url,\n    swift_account_server_port, swift_container_server_port,\n
        \   swift_external_fqdn, swift_internal_base_endpoint,\n    swift_internal_endpoint,
        swift_internal_fqdn, swift_object_server_port,\n    swift_proxy_server_listen_port,
        swift_proxy_server_port,\n    swift_public_base_endpoint, swift_public_endpoint,
        swift_rsync_port\n\n  Fix: mirror the missing var into osism/defaults all/*.yml
        (copying\n       upstream's definition \u2014 harmless when the service is
        off, needed when\n       an environment enables it), or allowlist it with
        a reason if OSISM\n       deliberately omits it: the related service is not
        shipped/supported\n       by OSISM at all (so the var is never evaluated anywhere),
        a var OSISM\n       supplies another way, or an upstream typo.\n  Refs: openstack/kolla-ansible
        group_vars/all @ supported refs\n        osism/defaults all/*.yml\n\nkolla_orphan_config
        \u2014 2 dead companion/image config vars for services\nupstream removed (their
        enable_<name> flag is reported separately by\nkolla_enablement_orphan); these
        must be removed too:\n\n    chrony_image, chrony_tag\n\n  Fix: remove these
        vars from the listed osism/defaults file, or allowlist\n       any that are
        intentionally kept (an OSISM invention with no upstream\n       service).\n
        \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
        all/002-images-kolla.yml\n\nkolla_orphan_config \u2014 2 dead companion/image
        config vars for services\nupstream removed (their enable_<name> flag is reported
        separately by\nkolla_enablement_orphan); these must be removed too:\n\n    chrony_allowed_subnets,
        chrony_servers\n\n  Fix: remove these vars from the listed osism/defaults
        file, or allowlist\n       any that are intentionally kept (an OSISM invention
        with no upstream\n       service).\n  Refs: openstack/kolla-ansible (service
        removed upstream)\n        osism/defaults all/099-generic.yml\n\nkolla_orphan_config
        \u2014 5 dead companion/image config vars for services\nupstream removed (their
        enable_<name> flag is reported separately by\nkolla_enablement_orphan); these
        must be removed too:\n\n    openstack_exporter_clouds_yml_cloud, openstack_exporter_clouds_yml_path,\n
        \   openstack_exporter_secure_yml_path,\n    openstack_exporter_service_monitor_scrape_interval,\n
        \   openstack_exporter_service_monitor_scrape_timeout\n\n  Fix: remove these
        vars from the listed osism/defaults file, or allowlist\n       any that are
        intentionally kept (an OSISM invention with no upstream\n       service).\n
        \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
        all/099-infrastructure.yml\n\nSummary: 73 to act on, 42 allowlisted, 0 stale
        allowlist entries (115 total)\n```\n\n### How to read it\n- **image pins**
        \u2014 DORMANT `role_shadows` lag but the release pin governs at\n  deploy
        (low priority); LIVE `role_shadows` (`dnsmasq`, `scaphandre`, `stepca`)\n
        \ actually deploy the role default (wire into `images.yml.j2`); `role_unpinned`\n
        \ (`httpd*`, `kepler`, `thanos_sidecar`) have no release pin; `image_orphan`\n
        \ (`nginx`, `osism_netbox`, `registry`) are emitted but consumed nowhere.\n-
        **kolla removal-direction** \u2014 `kolla_enablement_orphan` + `kolla_orphan_config`:\n
        \ stale `enable_*` flags and their companion vars for services upstream dropped;\n
        \ drop the removed projects, allowlist the OSISM inventions.\n- **kolla add-direction**
        \u2014 `kolla_groupvars_missing`: the `osism/defaults`\n  re-sync backlog.
        The right fix for most is to mirror the upstream var into\n  `osism/defaults`
        (harmless when the service is off, correct when an environment\n  enables
        it \u2014 e.g. metalbox turns on ironic, so `enable_ironic_pxe_filter` bites\n
        \ there); allowlist only genuine divergences (the `eutron` typo).\n\n> The
        detector runs unauthenticated against GitHub (no token support in\n> `source.py`),
        same as generics' existing daily job. A heavy run can occasionally\n> hit
        API rate limits and fail without a report; adding `GITHUB_TOKEN` support is
        a\n> sensible follow-up.\n"
      change_url: https://github.com/osism/release/pull/2561
      child_jobs: []
      commit_id: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
      event_id: 879c1830-7a12-11f1-84a0-a505c2b0c3d5
      executor:
        hostname: 521e9411259a
        inventory_file: /var/lib/zuul/builds/c4a0839faf134dcbb39ebaaa6125dcd4/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/c4a0839faf134dcbb39ebaaa6125dcd4/work/logs
        result_data_file: /var/lib/zuul/builds/c4a0839faf134dcbb39ebaaa6125dcd4/work/results.json
        src_root: /var/lib/zuul/builds/c4a0839faf134dcbb39ebaaa6125dcd4/work/src
        work_root: /var/lib/zuul/builds/c4a0839faf134dcbb39ebaaa6125dcd4/work
      include_vars: []
      items:
      - branch: main
        change: '2561'
        change_message: "Add drift-detection tooling (check-drift) to the release
          repo\n\n## What\n\nAdds `check-drift.py`, a plugin-based drift detector
          for OSISM's image and\nversion wiring, and wires it into CI. It catches
          divergence \u2014 *when it is\nintroduced* \u2014 before it breaks a deploy
          or a gate, across two check groups\n(`--group image | kolla | all`).\n\n**Image
          version pins** \u2014 follows an image's version down the chain\nrelease
          `base.yml` \u2192 rendered manager `images.yml` \u2192 role defaults:\n\n-
          `release_vs_manager` \u2014 the rendered manager `images.yml` disagrees
          with the release pin\n- `role_shadows` \u2014 a role-default tag shadows
          the release pin (LIVE = the role default actually deploys; DORMANT = the
          release pin wins)\n- `role_unpinned` \u2014 a role-default tag with no release
          pin\n- `image_orphan` \u2014 a manager image alias is emitted but consumed
          by no role or playbook\n\n**Kolla container-image toolchain** \u2014 divergence
          between the OSISM consumer repos\n(`defaults`, `release`, `generics`, the
          container-image repos) and upstream\n`openstack/kolla` / `openstack/kolla-ansible`:\n\n-
          `kolla_enablement_orphan` \u2014 OSISM enables a service upstream no longer
          defines\n- `kolla_groupvars_missing` \u2014 an upstream `group_vars/all`
          var OSISM's defaults never mirrored, so it is undefined at deploy/upgrade
          (the add-direction mirror of `kolla_enablement_orphan`; the class behind
          the 2025.2 `keystone_listen_port` break)\n- `kolla_orphan_config` \u2014
          dead companion vars / image blocks for an orphaned service\n- `kolla_secrets_orphan`
          \u2014 an OSISM kolla secret with no upstream counterpart\n- `kolla_enablement_build`
          \u2014 an enabled, upstream-buildable service missing from the build set\n-
          `kolla_version_chain_upstream` \u2014 an upstream `docker/` service with
          no version-pin line\n- `kolla_version_chain_inner` \u2014 a referenced version
          pin that resolves to nothing (inert pin)\n- `kolla_inventory` \u2014 an
          upstream inventory group missing from the OSISM `50/51-kolla` files\n\nThe
          framework lands first (empty registry, flags nothing on its own), then the\nplugins
          in lifecycle order, each reviewable in isolation. Sources resolve\nremotely
          (GitHub) by default, so the detector runs anywhere including CI;\n`--base-dir`
          reads local checkouts. An allowlist marks intentional exceptions\n(each
          requiring a reason); entries that match nothing are reported stale, so\nsuppressions
          can't silently rot. Exit codes: 0 clean / 1 drift or stale / 2 input\nerror.
          Full reference: `docs/check-drift-image.md`, `docs/check-drift-kolla.md`\n(incl.
          the `osism/defaults` release/bake model and why the range-aware checks\ncompare
          against the union of supported releases).\n\n## Commits (each independently
          green \u2014 unit tests pass at every commit)\n\n- `drift: add plugin framework
          and driver`\n- `drift: add image-drift plugins`\n- `drift: add kolla-drift
          plugins`\n- `drift: add drift-detection docs`\n- `drift: wire tox and Zuul
          CI`\n- `kolla: read enable flags from all defaults files` \u2014 make the
          enable-flag readers layout-agnostic (read `enable_*` from the union of `all/*.yml`,
          not just `099-kolla.yml`), so a defaults reorganization can't silently drop
          a flag\n- `docs: explain defaults bake model and unions`\n- `kolla: add
          kolla_groupvars_missing drift check` \u2014 the add-direction group_vars
          check (both OSISM delivery paths counted: `osism/defaults` + the rendered
          `container-image-kolla-ansible` `versions.yml`)\n- `kolla: allowlist eutron_external_interface
          typo`\n\n## CI\n\n- **`release-tox-drift-test`** \u2014 the unit suite (`tox
          -e drift-test`, 248 tests).\n  Runs in **check** (gates this PR) and periodic-daily.\n-
          **`release-tox-drift`** \u2014 the detector (`tox -e drift`, `check-drift.py\n
          \ --group all`). Runs **periodic-daily only**: it reads live upstream state,
          so\n  it's unfit as a PR gate. It exits non-zero when drift exists, so the
          daily job\n  goes **red** with the report in its log.\n\n## What this finds
          in practice\n\nRun against the live OSISM repos today, the detector reports
          **73 actionable\nitems, 42 allowlisted, 0 stale** (115 total):\n\n- **image
          pins** \u2014 1 rendered-vs-release disagreement (`ara_server`), 3 LIVE\n
          \ role-default shadows that actually deploy (`dnsmasq`, `scaphandre`, `stepca`),\n
          \ 9 DORMANT lags, 4 unpinned tags (`httpd`, `httpd_data`, `kepler`,\n  `thanos_sidecar`),
          and 3 orphaned manager aliases (`nginx`, `osism_netbox`,\n  `registry`).\n-
          **kolla toolchain** \u2014 now surfaces real drift (it was reported clean
          before\n  this branch). The layout-agnostic enable-flag reader scans `enable_*`
          across\n  **all** `osism/defaults` `all/*.yml` files (not just `099-kolla.yml`),
          surfacing\n  **9 enablement orphans** (`auditd`, `chrony`, `openstack_exporter`,
          `karbor`,\n  `qinling`, `searchlight`, three `horizon_*`) and their **9
          dead companion vars**\n  \u2014 some are removed-upstream projects to drop,
          some are OSISM inventions to\n  allowlist. The new add-direction `kolla_groupvars_missing`
          surfaces **35\n  upstream `group_vars/all` keys OSISM never mirrored** \u2014
          the `osism/defaults`\n  re-sync backlog behind the 2025.2 `keystone_listen_port`
          upgrade break, incl.\n  `enable_ironic_pxe_filter` and the `nova_database_*`/`nova_cell0_database_*`\n
          \ batch. `eutron_external_interface` is allowlisted (upstream typo for\n
          \ `neutron_external_interface`, awaiting\n  https://review.opendev.org/c/openstack/kolla-ansible/+/985031).\n\nEarlier,
          the removal-direction kolla checks already drove a wave of cleanups\nacross
          the consumer repos (kolla actionable-drift 81 \u2192 0 at the time, all
          merged);\nthis branch's layout-agnostic reader and new add-direction check
          now surface a\nfurther backlog \u2014 chiefly the `osism/defaults` re-sync
          the `keystone_listen_port`\nbreak called for:\n\n- osism/defaults#291\n-
          osism/container-image-kolla-ansible#924\n- osism/release#2551\n- osism/container-images-kolla#745\n-
          osism/generics#599\n- osism/generics#601\n\nIt self-audits its allowlist
          too: an entry that no longer suppresses real drift is\na hard error (exit
          1), so suppressions can't silently rot.\n\nBecause the detector is daily-only,
          this output does not appear in this PR's\nchecks. Full `release-tox-drift`
          report against current upstream\n(`check-drift.py --group all`):\n\n```\nChecks
          follow an image's version pin: release base.yml \u2192 rendered manager
          images.yml \u2192 role defaults.\n\nrelease_vs_manager \u2014 1 image tags
          in the rendered manager images.yml\ndisagree with the release base.yml pins:\n\n
          \   ara_server\n\n  Fix: re-render environments/manager/images.yml from
          the current release,\n       or allowlist the entry if the divergence is
          intentional (e.g. a\n       rolling-release image pinned to 'latest').\n
          \ Refs: release/latest/base.yml\n        testbed/environments/manager/images.yml\n\nrole_shadows
          \u2014 9 DORMANT \u2014 overridden by the rendered images.yml; the\nrelease
          pin wins at deploy:\n\n    adminer_tag (4.7 \u2192 5.4.2)   roles/adminer/defaults/main.yml\n
          \   ara_server_mariadb_tag (11.8.3 \u2192 11.8.4)   roles/manager/defaults/main.yml\n
          \   manager_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/manager/defaults/main.yml\n
          \   vault_tag (1.21.0 \u2192 1.21.4)   roles/manager/defaults/main.yml\n
          \   netbox_redis_tag (7.4.6-alpine \u2192 7.4.7-alpine)   roles/netbox/defaults/main.yml\n
          \   postgres_tag (16.10-alpine \u2192 16.11-alpine)   roles/netbox/defaults/main.yml\n
          \   nexus_tag (3.82.0 \u2192 3.93.1)   roles/nexus/defaults/main.yml\n    phpmyadmin_tag
          (5.2 \u2192 5.2.3)   roles/phpmyadmin/defaults/main.yml\n    traefik_tag
          (v3.4.4 \u2192 v3.5.0)   roles/traefik/defaults/main.yml\n\n  Fix: lower
          priority; sync when convenient.\n  Refs: release/latest/base.yml\n\nrole_shadows
          \u2014 3 LIVE \u2014 no images.yml override; the role default is what\nactually
          deploys:\n\n    dnsmasq_tag (2.90 \u2192 2.91)   roles/dnsmasq/defaults/main.yml\n
          \   scaphandre_tag (1.0.0 \u2192 1.0.2)   roles/scaphandre/defaults/main.yml\n
          \   stepca_tag (0.28.4 \u2192 0.30.2)   roles/stepca/defaults/main.yml\n\n
          \ Fix: add `<alias>_tag`/`<alias>_image` to the manager render template\n
          \      (images.yml.j2) so the latest/base.yml pin governs the deployed\n
          \      version.\n  Refs: release/latest/base.yml\n\nrole_unpinned \u2014
          4 <alias>_tag pins in role defaults with no release base.yml\npin:\n\n    httpd_tag
          (alpine, no release pin)   roles/httpd/defaults/main.yml\n    httpd_data_tag
          (latest, no release pin)   roles/httpd/defaults/main.yml\n    kepler_tag
          (v0.11.2, no release pin)   roles/kepler/defaults/main.yml\n    thanos_sidecar_tag
          (v0.32.5, no release pin)   roles/thanos_sidecar/defaults/main.yml\n\n  Fix:
          add a pin to release base.yml (and wire <alias>_tag into the manager\n       render
          template) to make it release-managed, or allowlist it if the\n       image
          is intentionally role-managed.\n  Refs: release/latest/base.yml\n\nimage_orphan
          \u2014 3 image vars emitted by the manager images.yml that no role\nor playbook
          consumes:\n\n    nginx, osism_netbox, registry\n\n  Fix: remove the orphaned
          <alias>_tag/<alias>_image (and its etc/images.yml\n       + role-default
          remnants), or allowlist it if the image is genuinely\n       consumed in
          a form this scan misses \u2014 it flags an alias only when no\n       literal
          {{ <alias>_image }} reference is found in the scanned\n       roles/playbooks.\n
          \ Refs: ansible-collection-services/roles/, ansible-playbooks-manager/playbooks/\n
          \       generics/environments/manager/images.yml\n\nChecks follow a service's
          path: enabled \u2192 built \u2192 version-pinned \u2192 deployed.\n\nkolla_enablement_orphan
          \u2014 9 OSISM enable flags whose service upstream\nkolla-ansible no longer
          defines at any supported release; the service was\nremoved or renamed upstream,
          leaving the flag orphaned:\n\n    auditd, chrony, horizon_karbor, horizon_qinling,
          horizon_searchlight,\n    karbor, openstack_exporter, qinling, searchlight\n\n
          \ Fix: remove the stale enable_<name> from osism/defaults, or migrate it
          to\n       the upstream replacement. Some flags (e.g. common, kolla_operations)\n
          \      are OSISM inventions with no upstream counterpart \u2014 keep those\n
          \      allowlisted rather than removed.\n  Refs: openstack/kolla-ansible
          group_vars enable-defaults @ supported refs\n        osism/defaults all/*.yml\n\nkolla_groupvars_missing
          \u2014 35 upstream kolla-ansible group_vars OSISM\ndefaults never mirrored;
          each is undefined in the deploy var context and\naborts any role task that
          uses it:\n\n    ceph_cinder_backup_keyring, ceph_cinder_keyring, ceph_glance_keyring,\n
          \   ceph_gnocchi_keyring, ceph_manila_keyring, ceph_nova_keyring,\n    distro_python_version_map,
          enable_cinder_backend_hnas_nfs,\n    enable_ironic_pxe_filter, enable_swift_recon,
          enable_swift_s3api,\n    glance_backend_swift, keystone_listen_port, nova_cell0_database_address,\n
          \   nova_cell0_database_name, nova_cell0_database_password,\n    nova_cell0_database_shard_id,
          nova_cell0_database_user,\n    nova_database_address, nova_database_name,
          nova_database_user,\n    prometheus_msteams_port, prometheus_msteams_webhook_url,\n
          \   swift_account_server_port, swift_container_server_port,\n    swift_external_fqdn,
          swift_internal_base_endpoint,\n    swift_internal_endpoint, swift_internal_fqdn,
          swift_object_server_port,\n    swift_proxy_server_listen_port, swift_proxy_server_port,\n
          \   swift_public_base_endpoint, swift_public_endpoint, swift_rsync_port\n\n
          \ Fix: mirror the missing var into osism/defaults all/*.yml (copying\n       upstream's
          definition \u2014 harmless when the service is off, needed when\n       an
          environment enables it), or allowlist it with a reason if OSISM\n       deliberately
          omits it: the related service is not shipped/supported\n       by OSISM
          at all (so the var is never evaluated anywhere), a var OSISM\n       supplies
          another way, or an upstream typo.\n  Refs: openstack/kolla-ansible group_vars/all
          @ supported refs\n        osism/defaults all/*.yml\n\nkolla_orphan_config
          \u2014 2 dead companion/image config vars for services\nupstream removed
          (their enable_<name> flag is reported separately by\nkolla_enablement_orphan);
          these must be removed too:\n\n    chrony_image, chrony_tag\n\n  Fix: remove
          these vars from the listed osism/defaults file, or allowlist\n       any
          that are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/002-images-kolla.yml\n\nkolla_orphan_config \u2014 2 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    chrony_allowed_subnets, chrony_servers\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-generic.yml\n\nkolla_orphan_config \u2014 5 dead companion/image
          config vars for services\nupstream removed (their enable_<name> flag is
          reported separately by\nkolla_enablement_orphan); these must be removed
          too:\n\n    openstack_exporter_clouds_yml_cloud, openstack_exporter_clouds_yml_path,\n
          \   openstack_exporter_secure_yml_path,\n    openstack_exporter_service_monitor_scrape_interval,\n
          \   openstack_exporter_service_monitor_scrape_timeout\n\n  Fix: remove these
          vars from the listed osism/defaults file, or allowlist\n       any that
          are intentionally kept (an OSISM invention with no upstream\n       service).\n
          \ Refs: openstack/kolla-ansible (service removed upstream)\n        osism/defaults
          all/099-infrastructure.yml\n\nSummary: 73 to act on, 42 allowlisted, 0 stale
          allowlist entries (115 total)\n```\n\n### How to read it\n- **image pins**
          \u2014 DORMANT `role_shadows` lag but the release pin governs at\n  deploy
          (low priority); LIVE `role_shadows` (`dnsmasq`, `scaphandre`, `stepca`)\n
          \ actually deploy the role default (wire into `images.yml.j2`); `role_unpinned`\n
          \ (`httpd*`, `kepler`, `thanos_sidecar`) have no release pin; `image_orphan`\n
          \ (`nginx`, `osism_netbox`, `registry`) are emitted but consumed nowhere.\n-
          **kolla removal-direction** \u2014 `kolla_enablement_orphan` + `kolla_orphan_config`:\n
          \ stale `enable_*` flags and their companion vars for services upstream
          dropped;\n  drop the removed projects, allowlist the OSISM inventions.\n-
          **kolla add-direction** \u2014 `kolla_groupvars_missing`: the `osism/defaults`\n
          \ re-sync backlog. The right fix for most is to mirror the upstream var
          into\n  `osism/defaults` (harmless when the service is off, correct when
          an environment\n  enables it \u2014 e.g. metalbox turns on ironic, so `enable_ironic_pxe_filter`
          bites\n  there); allowlist only genuine divergences (the `eutron` typo).\n\n>
          The detector runs unauthenticated against GitHub (no token support in\n>
          `source.py`), same as generics' existing daily job. A heavy run can occasionally\n>
          hit API rate limits and fail without a report; adding `GITHUB_TOKEN` support
          is a\n> sensible follow-up.\n"
        change_url: https://github.com/osism/release/pull/2561
        commit_id: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        patchset: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/release
          name: osism/release
          short_name: release
          src_dir: src/github.com/osism/release
        topic: null
      job: release-tox-drift-test
      jobtags: []
      max_attempts: 3
      message: QWRkIGRyaWZ0LWRldGVjdGlvbiB0b29saW5nIChjaGVjay1kcmlmdCkgdG8gdGhlIHJlbGVhc2UgcmVwbwoKIyMgV2hhdAoKQWRkcyBgY2hlY2stZHJpZnQucHlgLCBhIHBsdWdpbi1iYXNlZCBkcmlmdCBkZXRlY3RvciBmb3IgT1NJU00ncyBpbWFnZSBhbmQKdmVyc2lvbiB3aXJpbmcsIGFuZCB3aXJlcyBpdCBpbnRvIENJLiBJdCBjYXRjaGVzIGRpdmVyZ2VuY2Ug4oCUICp3aGVuIGl0IGlzCmludHJvZHVjZWQqIOKAlCBiZWZvcmUgaXQgYnJlYWtzIGEgZGVwbG95IG9yIGEgZ2F0ZSwgYWNyb3NzIHR3byBjaGVjayBncm91cHMKKGAtLWdyb3VwIGltYWdlIHwga29sbGEgfCBhbGxgKS4KCioqSW1hZ2UgdmVyc2lvbiBwaW5zKiog4oCUIGZvbGxvd3MgYW4gaW1hZ2UncyB2ZXJzaW9uIGRvd24gdGhlIGNoYWluCnJlbGVhc2UgYGJhc2UueW1sYCDihpIgcmVuZGVyZWQgbWFuYWdlciBgaW1hZ2VzLnltbGAg4oaSIHJvbGUgZGVmYXVsdHM6CgotIGByZWxlYXNlX3ZzX21hbmFnZXJgIOKAlCB0aGUgcmVuZGVyZWQgbWFuYWdlciBgaW1hZ2VzLnltbGAgZGlzYWdyZWVzIHdpdGggdGhlIHJlbGVhc2UgcGluCi0gYHJvbGVfc2hhZG93c2Ag4oCUIGEgcm9sZS1kZWZhdWx0IHRhZyBzaGFkb3dzIHRoZSByZWxlYXNlIHBpbiAoTElWRSA9IHRoZSByb2xlIGRlZmF1bHQgYWN0dWFsbHkgZGVwbG95czsgRE9STUFOVCA9IHRoZSByZWxlYXNlIHBpbiB3aW5zKQotIGByb2xlX3VucGlubmVkYCDigJQgYSByb2xlLWRlZmF1bHQgdGFnIHdpdGggbm8gcmVsZWFzZSBwaW4KLSBgaW1hZ2Vfb3JwaGFuYCDigJQgYSBtYW5hZ2VyIGltYWdlIGFsaWFzIGlzIGVtaXR0ZWQgYnV0IGNvbnN1bWVkIGJ5IG5vIHJvbGUgb3IgcGxheWJvb2sKCioqS29sbGEgY29udGFpbmVyLWltYWdlIHRvb2xjaGFpbioqIOKAlCBkaXZlcmdlbmNlIGJldHdlZW4gdGhlIE9TSVNNIGNvbnN1bWVyIHJlcG9zCihgZGVmYXVsdHNgLCBgcmVsZWFzZWAsIGBnZW5lcmljc2AsIHRoZSBjb250YWluZXItaW1hZ2UgcmVwb3MpIGFuZCB1cHN0cmVhbQpgb3BlbnN0YWNrL2tvbGxhYCAvIGBvcGVuc3RhY2sva29sbGEtYW5zaWJsZWA6CgotIGBrb2xsYV9lbmFibGVtZW50X29ycGhhbmAg4oCUIE9TSVNNIGVuYWJsZXMgYSBzZXJ2aWNlIHVwc3RyZWFtIG5vIGxvbmdlciBkZWZpbmVzCi0gYGtvbGxhX2dyb3VwdmFyc19taXNzaW5nYCDigJQgYW4gdXBzdHJlYW0gYGdyb3VwX3ZhcnMvYWxsYCB2YXIgT1NJU00ncyBkZWZhdWx0cyBuZXZlciBtaXJyb3JlZCwgc28gaXQgaXMgdW5kZWZpbmVkIGF0IGRlcGxveS91cGdyYWRlICh0aGUgYWRkLWRpcmVjdGlvbiBtaXJyb3Igb2YgYGtvbGxhX2VuYWJsZW1lbnRfb3JwaGFuYDsgdGhlIGNsYXNzIGJlaGluZCB0aGUgMjAyNS4yIGBrZXlzdG9uZV9saXN0ZW5fcG9ydGAgYnJlYWspCi0gYGtvbGxhX29ycGhhbl9jb25maWdgIOKAlCBkZWFkIGNvbXBhbmlvbiB2YXJzIC8gaW1hZ2UgYmxvY2tzIGZvciBhbiBvcnBoYW5lZCBzZXJ2aWNlCi0gYGtvbGxhX3NlY3JldHNfb3JwaGFuYCDigJQgYW4gT1NJU00ga29sbGEgc2VjcmV0IHdpdGggbm8gdXBzdHJlYW0gY291bnRlcnBhcnQKLSBga29sbGFfZW5hYmxlbWVudF9idWlsZGAg4oCUIGFuIGVuYWJsZWQsIHVwc3RyZWFtLWJ1aWxkYWJsZSBzZXJ2aWNlIG1pc3NpbmcgZnJvbSB0aGUgYnVpbGQgc2V0Ci0gYGtvbGxhX3ZlcnNpb25fY2hhaW5fdXBzdHJlYW1gIOKAlCBhbiB1cHN0cmVhbSBgZG9ja2VyL2Agc2VydmljZSB3aXRoIG5vIHZlcnNpb24tcGluIGxpbmUKLSBga29sbGFfdmVyc2lvbl9jaGFpbl9pbm5lcmAg4oCUIGEgcmVmZXJlbmNlZCB2ZXJzaW9uIHBpbiB0aGF0IHJlc29sdmVzIHRvIG5vdGhpbmcgKGluZXJ0IHBpbikKLSBga29sbGFfaW52ZW50b3J5YCDigJQgYW4gdXBzdHJlYW0gaW52ZW50b3J5IGdyb3VwIG1pc3NpbmcgZnJvbSB0aGUgT1NJU00gYDUwLzUxLWtvbGxhYCBmaWxlcwoKVGhlIGZyYW1ld29yayBsYW5kcyBmaXJzdCAoZW1wdHkgcmVnaXN0cnksIGZsYWdzIG5vdGhpbmcgb24gaXRzIG93biksIHRoZW4gdGhlCnBsdWdpbnMgaW4gbGlmZWN5Y2xlIG9yZGVyLCBlYWNoIHJldmlld2FibGUgaW4gaXNvbGF0aW9uLiBTb3VyY2VzIHJlc29sdmUKcmVtb3RlbHkgKEdpdEh1YikgYnkgZGVmYXVsdCwgc28gdGhlIGRldGVjdG9yIHJ1bnMgYW55d2hlcmUgaW5jbHVkaW5nIENJOwpgLS1iYXNlLWRpcmAgcmVhZHMgbG9jYWwgY2hlY2tvdXRzLiBBbiBhbGxvd2xpc3QgbWFya3MgaW50ZW50aW9uYWwgZXhjZXB0aW9ucwooZWFjaCByZXF1aXJpbmcgYSByZWFzb24pOyBlbnRyaWVzIHRoYXQgbWF0Y2ggbm90aGluZyBhcmUgcmVwb3J0ZWQgc3RhbGUsIHNvCnN1cHByZXNzaW9ucyBjYW4ndCBzaWxlbnRseSByb3QuIEV4aXQgY29kZXM6IDAgY2xlYW4gLyAxIGRyaWZ0IG9yIHN0YWxlIC8gMiBpbnB1dAplcnJvci4gRnVsbCByZWZlcmVuY2U6IGBkb2NzL2NoZWNrLWRyaWZ0LWltYWdlLm1kYCwgYGRvY3MvY2hlY2stZHJpZnQta29sbGEubWRgCihpbmNsLiB0aGUgYG9zaXNtL2RlZmF1bHRzYCByZWxlYXNlL2Jha2UgbW9kZWwgYW5kIHdoeSB0aGUgcmFuZ2UtYXdhcmUgY2hlY2tzCmNvbXBhcmUgYWdhaW5zdCB0aGUgdW5pb24gb2Ygc3VwcG9ydGVkIHJlbGVhc2VzKS4KCiMjIENvbW1pdHMgKGVhY2ggaW5kZXBlbmRlbnRseSBncmVlbiDigJQgdW5pdCB0ZXN0cyBwYXNzIGF0IGV2ZXJ5IGNvbW1pdCkKCi0gYGRyaWZ0OiBhZGQgcGx1Z2luIGZyYW1ld29yayBhbmQgZHJpdmVyYAotIGBkcmlmdDogYWRkIGltYWdlLWRyaWZ0IHBsdWdpbnNgCi0gYGRyaWZ0OiBhZGQga29sbGEtZHJpZnQgcGx1Z2luc2AKLSBgZHJpZnQ6IGFkZCBkcmlmdC1kZXRlY3Rpb24gZG9jc2AKLSBgZHJpZnQ6IHdpcmUgdG94IGFuZCBadXVsIENJYAotIGBrb2xsYTogcmVhZCBlbmFibGUgZmxhZ3MgZnJvbSBhbGwgZGVmYXVsdHMgZmlsZXNgIOKAlCBtYWtlIHRoZSBlbmFibGUtZmxhZyByZWFkZXJzIGxheW91dC1hZ25vc3RpYyAocmVhZCBgZW5hYmxlXypgIGZyb20gdGhlIHVuaW9uIG9mIGBhbGwvKi55bWxgLCBub3QganVzdCBgMDk5LWtvbGxhLnltbGApLCBzbyBhIGRlZmF1bHRzIHJlb3JnYW5pemF0aW9uIGNhbid0IHNpbGVudGx5IGRyb3AgYSBmbGFnCi0gYGRvY3M6IGV4cGxhaW4gZGVmYXVsdHMgYmFrZSBtb2RlbCBhbmQgdW5pb25zYAotIGBrb2xsYTogYWRkIGtvbGxhX2dyb3VwdmFyc19taXNzaW5nIGRyaWZ0IGNoZWNrYCDigJQgdGhlIGFkZC1kaXJlY3Rpb24gZ3JvdXBfdmFycyBjaGVjayAoYm90aCBPU0lTTSBkZWxpdmVyeSBwYXRocyBjb3VudGVkOiBgb3Npc20vZGVmYXVsdHNgICsgdGhlIHJlbmRlcmVkIGBjb250YWluZXItaW1hZ2Uta29sbGEtYW5zaWJsZWAgYHZlcnNpb25zLnltbGApCi0gYGtvbGxhOiBhbGxvd2xpc3QgZXV0cm9uX2V4dGVybmFsX2ludGVyZmFjZSB0eXBvYAoKIyMgQ0kKCi0gKipgcmVsZWFzZS10b3gtZHJpZnQtdGVzdGAqKiDigJQgdGhlIHVuaXQgc3VpdGUgKGB0b3ggLWUgZHJpZnQtdGVzdGAsIDI0OCB0ZXN0cykuCiAgUnVucyBpbiAqKmNoZWNrKiogKGdhdGVzIHRoaXMgUFIpIGFuZCBwZXJpb2RpYy1kYWlseS4KLSAqKmByZWxlYXNlLXRveC1kcmlmdGAqKiDigJQgdGhlIGRldGVjdG9yIChgdG94IC1lIGRyaWZ0YCwgYGNoZWNrLWRyaWZ0LnB5CiAgLS1ncm91cCBhbGxgKS4gUnVucyAqKnBlcmlvZGljLWRhaWx5IG9ubHkqKjogaXQgcmVhZHMgbGl2ZSB1cHN0cmVhbSBzdGF0ZSwgc28KICBpdCdzIHVuZml0IGFzIGEgUFIgZ2F0ZS4gSXQgZXhpdHMgbm9uLXplcm8gd2hlbiBkcmlmdCBleGlzdHMsIHNvIHRoZSBkYWlseSBqb2IKICBnb2VzICoqcmVkKiogd2l0aCB0aGUgcmVwb3J0IGluIGl0cyBsb2cuCgojIyBXaGF0IHRoaXMgZmluZHMgaW4gcHJhY3RpY2UKClJ1biBhZ2FpbnN0IHRoZSBsaXZlIE9TSVNNIHJlcG9zIHRvZGF5LCB0aGUgZGV0ZWN0b3IgcmVwb3J0cyAqKjczIGFjdGlvbmFibGUKaXRlbXMsIDQyIGFsbG93bGlzdGVkLCAwIHN0YWxlKiogKDExNSB0b3RhbCk6CgotICoqaW1hZ2UgcGlucyoqIOKAlCAxIHJlbmRlcmVkLXZzLXJlbGVhc2UgZGlzYWdyZWVtZW50IChgYXJhX3NlcnZlcmApLCAzIExJVkUKICByb2xlLWRlZmF1bHQgc2hhZG93cyB0aGF0IGFjdHVhbGx5IGRlcGxveSAoYGRuc21hc3FgLCBgc2NhcGhhbmRyZWAsIGBzdGVwY2FgKSwKICA5IERPUk1BTlQgbGFncywgNCB1bnBpbm5lZCB0YWdzIChgaHR0cGRgLCBgaHR0cGRfZGF0YWAsIGBrZXBsZXJgLAogIGB0aGFub3Nfc2lkZWNhcmApLCBhbmQgMyBvcnBoYW5lZCBtYW5hZ2VyIGFsaWFzZXMgKGBuZ2lueGAsIGBvc2lzbV9uZXRib3hgLAogIGByZWdpc3RyeWApLgotICoqa29sbGEgdG9vbGNoYWluKiog4oCUIG5vdyBzdXJmYWNlcyByZWFsIGRyaWZ0IChpdCB3YXMgcmVwb3J0ZWQgY2xlYW4gYmVmb3JlCiAgdGhpcyBicmFuY2gpLiBUaGUgbGF5b3V0LWFnbm9zdGljIGVuYWJsZS1mbGFnIHJlYWRlciBzY2FucyBgZW5hYmxlXypgIGFjcm9zcwogICoqYWxsKiogYG9zaXNtL2RlZmF1bHRzYCBgYWxsLyoueW1sYCBmaWxlcyAobm90IGp1c3QgYDA5OS1rb2xsYS55bWxgKSwgc3VyZmFjaW5nCiAgKio5IGVuYWJsZW1lbnQgb3JwaGFucyoqIChgYXVkaXRkYCwgYGNocm9ueWAsIGBvcGVuc3RhY2tfZXhwb3J0ZXJgLCBga2FyYm9yYCwKICBgcWlubGluZ2AsIGBzZWFyY2hsaWdodGAsIHRocmVlIGBob3Jpem9uXypgKSBhbmQgdGhlaXIgKio5IGRlYWQgY29tcGFuaW9uIHZhcnMqKgogIOKAlCBzb21lIGFyZSByZW1vdmVkLXVwc3RyZWFtIHByb2plY3RzIHRvIGRyb3AsIHNvbWUgYXJlIE9TSVNNIGludmVudGlvbnMgdG8KICBhbGxvd2xpc3QuIFRoZSBuZXcgYWRkLWRpcmVjdGlvbiBga29sbGFfZ3JvdXB2YXJzX21pc3NpbmdgIHN1cmZhY2VzICoqMzUKICB1cHN0cmVhbSBgZ3JvdXBfdmFycy9hbGxgIGtleXMgT1NJU00gbmV2ZXIgbWlycm9yZWQqKiDigJQgdGhlIGBvc2lzbS9kZWZhdWx0c2AKICByZS1zeW5jIGJhY2tsb2cgYmVoaW5kIHRoZSAyMDI1LjIgYGtleXN0b25lX2xpc3Rlbl9wb3J0YCB1cGdyYWRlIGJyZWFrLCBpbmNsLgogIGBlbmFibGVfaXJvbmljX3B4ZV9maWx0ZXJgIGFuZCB0aGUgYG5vdmFfZGF0YWJhc2VfKmAvYG5vdmFfY2VsbDBfZGF0YWJhc2VfKmAKICBiYXRjaC4gYGV1dHJvbl9leHRlcm5hbF9pbnRlcmZhY2VgIGlzIGFsbG93bGlzdGVkICh1cHN0cmVhbSB0eXBvIGZvcgogIGBuZXV0cm9uX2V4dGVybmFsX2ludGVyZmFjZWAsIGF3YWl0aW5nCiAgaHR0cHM6Ly9yZXZpZXcub3BlbmRldi5vcmcvYy9vcGVuc3RhY2sva29sbGEtYW5zaWJsZS8rLzk4NTAzMSkuCgpFYXJsaWVyLCB0aGUgcmVtb3ZhbC1kaXJlY3Rpb24ga29sbGEgY2hlY2tzIGFscmVhZHkgZHJvdmUgYSB3YXZlIG9mIGNsZWFudXBzCmFjcm9zcyB0aGUgY29uc3VtZXIgcmVwb3MgKGtvbGxhIGFjdGlvbmFibGUtZHJpZnQgODEg4oaSIDAgYXQgdGhlIHRpbWUsIGFsbCBtZXJnZWQpOwp0aGlzIGJyYW5jaCdzIGxheW91dC1hZ25vc3RpYyByZWFkZXIgYW5kIG5ldyBhZGQtZGlyZWN0aW9uIGNoZWNrIG5vdyBzdXJmYWNlIGEKZnVydGhlciBiYWNrbG9nIOKAlCBjaGllZmx5IHRoZSBgb3Npc20vZGVmYXVsdHNgIHJlLXN5bmMgdGhlIGBrZXlzdG9uZV9saXN0ZW5fcG9ydGAKYnJlYWsgY2FsbGVkIGZvcjoKCi0gb3Npc20vZGVmYXVsdHMjMjkxCi0gb3Npc20vY29udGFpbmVyLWltYWdlLWtvbGxhLWFuc2libGUjOTI0Ci0gb3Npc20vcmVsZWFzZSMyNTUxCi0gb3Npc20vY29udGFpbmVyLWltYWdlcy1rb2xsYSM3NDUKLSBvc2lzbS9nZW5lcmljcyM1OTkKLSBvc2lzbS9nZW5lcmljcyM2MDEKCkl0IHNlbGYtYXVkaXRzIGl0cyBhbGxvd2xpc3QgdG9vOiBhbiBlbnRyeSB0aGF0IG5vIGxvbmdlciBzdXBwcmVzc2VzIHJlYWwgZHJpZnQgaXMKYSBoYXJkIGVycm9yIChleGl0IDEpLCBzbyBzdXBwcmVzc2lvbnMgY2FuJ3Qgc2lsZW50bHkgcm90LgoKQmVjYXVzZSB0aGUgZGV0ZWN0b3IgaXMgZGFpbHktb25seSwgdGhpcyBvdXRwdXQgZG9lcyBub3QgYXBwZWFyIGluIHRoaXMgUFIncwpjaGVja3MuIEZ1bGwgYHJlbGVhc2UtdG94LWRyaWZ0YCByZXBvcnQgYWdhaW5zdCBjdXJyZW50IHVwc3RyZWFtCihgY2hlY2stZHJpZnQucHkgLS1ncm91cCBhbGxgKToKCmBgYApDaGVja3MgZm9sbG93IGFuIGltYWdlJ3MgdmVyc2lvbiBwaW46IHJlbGVhc2UgYmFzZS55bWwg4oaSIHJlbmRlcmVkIG1hbmFnZXIgaW1hZ2VzLnltbCDihpIgcm9sZSBkZWZhdWx0cy4KCnJlbGVhc2VfdnNfbWFuYWdlciDigJQgMSBpbWFnZSB0YWdzIGluIHRoZSByZW5kZXJlZCBtYW5hZ2VyIGltYWdlcy55bWwKZGlzYWdyZWUgd2l0aCB0aGUgcmVsZWFzZSBiYXNlLnltbCBwaW5zOgoKICAgIGFyYV9zZXJ2ZXIKCiAgRml4OiByZS1yZW5kZXIgZW52aXJvbm1lbnRzL21hbmFnZXIvaW1hZ2VzLnltbCBmcm9tIHRoZSBjdXJyZW50IHJlbGVhc2UsCiAgICAgICBvciBhbGxvd2xpc3QgdGhlIGVudHJ5IGlmIHRoZSBkaXZlcmdlbmNlIGlzIGludGVudGlvbmFsIChlLmcuIGEKICAgICAgIHJvbGxpbmctcmVsZWFzZSBpbWFnZSBwaW5uZWQgdG8gJ2xhdGVzdCcpLgogIFJlZnM6IHJlbGVhc2UvbGF0ZXN0L2Jhc2UueW1sCiAgICAgICAgdGVzdGJlZC9lbnZpcm9ubWVudHMvbWFuYWdlci9pbWFnZXMueW1sCgpyb2xlX3NoYWRvd3Mg4oCUIDkgRE9STUFOVCDigJQgb3ZlcnJpZGRlbiBieSB0aGUgcmVuZGVyZWQgaW1hZ2VzLnltbDsgdGhlCnJlbGVhc2UgcGluIHdpbnMgYXQgZGVwbG95OgoKICAgIGFkbWluZXJfdGFnICg0Ljcg4oaSIDUuNC4yKSAgIHJvbGVzL2FkbWluZXIvZGVmYXVsdHMvbWFpbi55bWwKICAgIGFyYV9zZXJ2ZXJfbWFyaWFkYl90YWcgKDExLjguMyDihpIgMTEuOC40KSAgIHJvbGVzL21hbmFnZXIvZGVmYXVsdHMvbWFpbi55bWwKICAgIG1hbmFnZXJfcmVkaXNfdGFnICg3LjQuNi1hbHBpbmUg4oaSIDcuNC43LWFscGluZSkgICByb2xlcy9tYW5hZ2VyL2RlZmF1bHRzL21haW4ueW1sCiAgICB2YXVsdF90YWcgKDEuMjEuMCDihpIgMS4yMS40KSAgIHJvbGVzL21hbmFnZXIvZGVmYXVsdHMvbWFpbi55bWwKICAgIG5ldGJveF9yZWRpc190YWcgKDcuNC42LWFscGluZSDihpIgNy40LjctYWxwaW5lKSAgIHJvbGVzL25ldGJveC9kZWZhdWx0cy9tYWluLnltbAogICAgcG9zdGdyZXNfdGFnICgxNi4xMC1hbHBpbmUg4oaSIDE2LjExLWFscGluZSkgICByb2xlcy9uZXRib3gvZGVmYXVsdHMvbWFpbi55bWwKICAgIG5leHVzX3RhZyAoMy44Mi4wIOKGkiAzLjkzLjEpICAgcm9sZXMvbmV4dXMvZGVmYXVsdHMvbWFpbi55bWwKICAgIHBocG15YWRtaW5fdGFnICg1LjIg4oaSIDUuMi4zKSAgIHJvbGVzL3BocG15YWRtaW4vZGVmYXVsdHMvbWFpbi55bWwKICAgIHRyYWVmaWtfdGFnICh2My40LjQg4oaSIHYzLjUuMCkgICByb2xlcy90cmFlZmlrL2RlZmF1bHRzL21haW4ueW1sCgogIEZpeDogbG93ZXIgcHJpb3JpdHk7IHN5bmMgd2hlbiBjb252ZW5pZW50LgogIFJlZnM6IHJlbGVhc2UvbGF0ZXN0L2Jhc2UueW1sCgpyb2xlX3NoYWRvd3Mg4oCUIDMgTElWRSDigJQgbm8gaW1hZ2VzLnltbCBvdmVycmlkZTsgdGhlIHJvbGUgZGVmYXVsdCBpcyB3aGF0CmFjdHVhbGx5IGRlcGxveXM6CgogICAgZG5zbWFzcV90YWcgKDIuOTAg4oaSIDIuOTEpICAgcm9sZXMvZG5zbWFzcS9kZWZhdWx0cy9tYWluLnltbAogICAgc2NhcGhhbmRyZV90YWcgKDEuMC4wIOKGkiAxLjAuMikgICByb2xlcy9zY2FwaGFuZHJlL2RlZmF1bHRzL21haW4ueW1sCiAgICBzdGVwY2FfdGFnICgwLjI4LjQg4oaSIDAuMzAuMikgICByb2xlcy9zdGVwY2EvZGVmYXVsdHMvbWFpbi55bWwKCiAgRml4OiBhZGQgYDxhbGlhcz5fdGFnYC9gPGFsaWFzPl9pbWFnZWAgdG8gdGhlIG1hbmFnZXIgcmVuZGVyIHRlbXBsYXRlCiAgICAgICAoaW1hZ2VzLnltbC5qMikgc28gdGhlIGxhdGVzdC9iYXNlLnltbCBwaW4gZ292ZXJucyB0aGUgZGVwbG95ZWQKICAgICAgIHZlcnNpb24uCiAgUmVmczogcmVsZWFzZS9sYXRlc3QvYmFzZS55bWwKCnJvbGVfdW5waW5uZWQg4oCUIDQgPGFsaWFzPl90YWcgcGlucyBpbiByb2xlIGRlZmF1bHRzIHdpdGggbm8gcmVsZWFzZSBiYXNlLnltbApwaW46CgogICAgaHR0cGRfdGFnIChhbHBpbmUsIG5vIHJlbGVhc2UgcGluKSAgIHJvbGVzL2h0dHBkL2RlZmF1bHRzL21haW4ueW1sCiAgICBodHRwZF9kYXRhX3RhZyAobGF0ZXN0LCBubyByZWxlYXNlIHBpbikgICByb2xlcy9odHRwZC9kZWZhdWx0cy9tYWluLnltbAogICAga2VwbGVyX3RhZyAodjAuMTEuMiwgbm8gcmVsZWFzZSBwaW4pICAgcm9sZXMva2VwbGVyL2RlZmF1bHRzL21haW4ueW1sCiAgICB0aGFub3Nfc2lkZWNhcl90YWcgKHYwLjMyLjUsIG5vIHJlbGVhc2UgcGluKSAgIHJvbGVzL3RoYW5vc19zaWRlY2FyL2RlZmF1bHRzL21haW4ueW1sCgogIEZpeDogYWRkIGEgcGluIHRvIHJlbGVhc2UgYmFzZS55bWwgKGFuZCB3aXJlIDxhbGlhcz5fdGFnIGludG8gdGhlIG1hbmFnZXIKICAgICAgIHJlbmRlciB0ZW1wbGF0ZSkgdG8gbWFrZSBpdCByZWxlYXNlLW1hbmFnZWQsIG9yIGFsbG93bGlzdCBpdCBpZiB0aGUKICAgICAgIGltYWdlIGlzIGludGVudGlvbmFsbHkgcm9sZS1tYW5hZ2VkLgogIFJlZnM6IHJlbGVhc2UvbGF0ZXN0L2Jhc2UueW1sCgppbWFnZV9vcnBoYW4g4oCUIDMgaW1hZ2UgdmFycyBlbWl0dGVkIGJ5IHRoZSBtYW5hZ2VyIGltYWdlcy55bWwgdGhhdCBubyByb2xlCm9yIHBsYXlib29rIGNvbnN1bWVzOgoKICAgIG5naW54LCBvc2lzbV9uZXRib3gsIHJlZ2lzdHJ5CgogIEZpeDogcmVtb3ZlIHRoZSBvcnBoYW5lZCA8YWxpYXM+X3RhZy88YWxpYXM+X2ltYWdlIChhbmQgaXRzIGV0Yy9pbWFnZXMueW1sCiAgICAgICArIHJvbGUtZGVmYXVsdCByZW1uYW50cyksIG9yIGFsbG93bGlzdCBpdCBpZiB0aGUgaW1hZ2UgaXMgZ2VudWluZWx5CiAgICAgICBjb25zdW1lZCBpbiBhIGZvcm0gdGhpcyBzY2FuIG1pc3NlcyDigJQgaXQgZmxhZ3MgYW4gYWxpYXMgb25seSB3aGVuIG5vCiAgICAgICBsaXRlcmFsIHt7IDxhbGlhcz5faW1hZ2UgfX0gcmVmZXJlbmNlIGlzIGZvdW5kIGluIHRoZSBzY2FubmVkCiAgICAgICByb2xlcy9wbGF5Ym9va3MuCiAgUmVmczogYW5zaWJsZS1jb2xsZWN0aW9uLXNlcnZpY2VzL3JvbGVzLywgYW5zaWJsZS1wbGF5Ym9va3MtbWFuYWdlci9wbGF5Ym9va3MvCiAgICAgICAgZ2VuZXJpY3MvZW52aXJvbm1lbnRzL21hbmFnZXIvaW1hZ2VzLnltbAoKQ2hlY2tzIGZvbGxvdyBhIHNlcnZpY2UncyBwYXRoOiBlbmFibGVkIOKGkiBidWlsdCDihpIgdmVyc2lvbi1waW5uZWQg4oaSIGRlcGxveWVkLgoKa29sbGFfZW5hYmxlbWVudF9vcnBoYW4g4oCUIDkgT1NJU00gZW5hYmxlIGZsYWdzIHdob3NlIHNlcnZpY2UgdXBzdHJlYW0Ka29sbGEtYW5zaWJsZSBubyBsb25nZXIgZGVmaW5lcyBhdCBhbnkgc3VwcG9ydGVkIHJlbGVhc2U7IHRoZSBzZXJ2aWNlIHdhcwpyZW1vdmVkIG9yIHJlbmFtZWQgdXBzdHJlYW0sIGxlYXZpbmcgdGhlIGZsYWcgb3JwaGFuZWQ6CgogICAgYXVkaXRkLCBjaHJvbnksIGhvcml6b25fa2FyYm9yLCBob3Jpem9uX3FpbmxpbmcsIGhvcml6b25fc2VhcmNobGlnaHQsCiAgICBrYXJib3IsIG9wZW5zdGFja19leHBvcnRlciwgcWlubGluZywgc2VhcmNobGlnaHQKCiAgRml4OiByZW1vdmUgdGhlIHN0YWxlIGVuYWJsZV88bmFtZT4gZnJvbSBvc2lzbS9kZWZhdWx0cywgb3IgbWlncmF0ZSBpdCB0bwogICAgICAgdGhlIHVwc3RyZWFtIHJlcGxhY2VtZW50LiBTb21lIGZsYWdzIChlLmcuIGNvbW1vbiwga29sbGFfb3BlcmF0aW9ucykKICAgICAgIGFyZSBPU0lTTSBpbnZlbnRpb25zIHdpdGggbm8gdXBzdHJlYW0gY291bnRlcnBhcnQg4oCUIGtlZXAgdGhvc2UKICAgICAgIGFsbG93bGlzdGVkIHJhdGhlciB0aGFuIHJlbW92ZWQuCiAgUmVmczogb3BlbnN0YWNrL2tvbGxhLWFuc2libGUgZ3JvdXBfdmFycyBlbmFibGUtZGVmYXVsdHMgQCBzdXBwb3J0ZWQgcmVmcwogICAgICAgIG9zaXNtL2RlZmF1bHRzIGFsbC8qLnltbAoKa29sbGFfZ3JvdXB2YXJzX21pc3Npbmcg4oCUIDM1IHVwc3RyZWFtIGtvbGxhLWFuc2libGUgZ3JvdXBfdmFycyBPU0lTTQpkZWZhdWx0cyBuZXZlciBtaXJyb3JlZDsgZWFjaCBpcyB1bmRlZmluZWQgaW4gdGhlIGRlcGxveSB2YXIgY29udGV4dCBhbmQKYWJvcnRzIGFueSByb2xlIHRhc2sgdGhhdCB1c2VzIGl0OgoKICAgIGNlcGhfY2luZGVyX2JhY2t1cF9rZXlyaW5nLCBjZXBoX2NpbmRlcl9rZXlyaW5nLCBjZXBoX2dsYW5jZV9rZXlyaW5nLAogICAgY2VwaF9nbm9jY2hpX2tleXJpbmcsIGNlcGhfbWFuaWxhX2tleXJpbmcsIGNlcGhfbm92YV9rZXlyaW5nLAogICAgZGlzdHJvX3B5dGhvbl92ZXJzaW9uX21hcCwgZW5hYmxlX2NpbmRlcl9iYWNrZW5kX2huYXNfbmZzLAogICAgZW5hYmxlX2lyb25pY19weGVfZmlsdGVyLCBlbmFibGVfc3dpZnRfcmVjb24sIGVuYWJsZV9zd2lmdF9zM2FwaSwKICAgIGdsYW5jZV9iYWNrZW5kX3N3aWZ0LCBrZXlzdG9uZV9saXN0ZW5fcG9ydCwgbm92YV9jZWxsMF9kYXRhYmFzZV9hZGRyZXNzLAogICAgbm92YV9jZWxsMF9kYXRhYmFzZV9uYW1lLCBub3ZhX2NlbGwwX2RhdGFiYXNlX3Bhc3N3b3JkLAogICAgbm92YV9jZWxsMF9kYXRhYmFzZV9zaGFyZF9pZCwgbm92YV9jZWxsMF9kYXRhYmFzZV91c2VyLAogICAgbm92YV9kYXRhYmFzZV9hZGRyZXNzLCBub3ZhX2RhdGFiYXNlX25hbWUsIG5vdmFfZGF0YWJhc2VfdXNlciwKICAgIHByb21ldGhldXNfbXN0ZWFtc19wb3J0LCBwcm9tZXRoZXVzX21zdGVhbXNfd2ViaG9va191cmwsCiAgICBzd2lmdF9hY2NvdW50X3NlcnZlcl9wb3J0LCBzd2lmdF9jb250YWluZXJfc2VydmVyX3BvcnQsCiAgICBzd2lmdF9leHRlcm5hbF9mcWRuLCBzd2lmdF9pbnRlcm5hbF9iYXNlX2VuZHBvaW50LAogICAgc3dpZnRfaW50ZXJuYWxfZW5kcG9pbnQsIHN3aWZ0X2ludGVybmFsX2ZxZG4sIHN3aWZ0X29iamVjdF9zZXJ2ZXJfcG9ydCwKICAgIHN3aWZ0X3Byb3h5X3NlcnZlcl9saXN0ZW5fcG9ydCwgc3dpZnRfcHJveHlfc2VydmVyX3BvcnQsCiAgICBzd2lmdF9wdWJsaWNfYmFzZV9lbmRwb2ludCwgc3dpZnRfcHVibGljX2VuZHBvaW50LCBzd2lmdF9yc3luY19wb3J0CgogIEZpeDogbWlycm9yIHRoZSBtaXNzaW5nIHZhciBpbnRvIG9zaXNtL2RlZmF1bHRzIGFsbC8qLnltbCAoY29weWluZwogICAgICAgdXBzdHJlYW0ncyBkZWZpbml0aW9uIOKAlCBoYXJtbGVzcyB3aGVuIHRoZSBzZXJ2aWNlIGlzIG9mZiwgbmVlZGVkIHdoZW4KICAgICAgIGFuIGVudmlyb25tZW50IGVuYWJsZXMgaXQpLCBvciBhbGxvd2xpc3QgaXQgd2l0aCBhIHJlYXNvbiBpZiBPU0lTTQogICAgICAgZGVsaWJlcmF0ZWx5IG9taXRzIGl0OiB0aGUgcmVsYXRlZCBzZXJ2aWNlIGlzIG5vdCBzaGlwcGVkL3N1cHBvcnRlZAogICAgICAgYnkgT1NJU00gYXQgYWxsIChzbyB0aGUgdmFyIGlzIG5ldmVyIGV2YWx1YXRlZCBhbnl3aGVyZSksIGEgdmFyIE9TSVNNCiAgICAgICBzdXBwbGllcyBhbm90aGVyIHdheSwgb3IgYW4gdXBzdHJlYW0gdHlwby4KICBSZWZzOiBvcGVuc3RhY2sva29sbGEtYW5zaWJsZSBncm91cF92YXJzL2FsbCBAIHN1cHBvcnRlZCByZWZzCiAgICAgICAgb3Npc20vZGVmYXVsdHMgYWxsLyoueW1sCgprb2xsYV9vcnBoYW5fY29uZmlnIOKAlCAyIGRlYWQgY29tcGFuaW9uL2ltYWdlIGNvbmZpZyB2YXJzIGZvciBzZXJ2aWNlcwp1cHN0cmVhbSByZW1vdmVkICh0aGVpciBlbmFibGVfPG5hbWU+IGZsYWcgaXMgcmVwb3J0ZWQgc2VwYXJhdGVseSBieQprb2xsYV9lbmFibGVtZW50X29ycGhhbik7IHRoZXNlIG11c3QgYmUgcmVtb3ZlZCB0b286CgogICAgY2hyb255X2ltYWdlLCBjaHJvbnlfdGFnCgogIEZpeDogcmVtb3ZlIHRoZXNlIHZhcnMgZnJvbSB0aGUgbGlzdGVkIG9zaXNtL2RlZmF1bHRzIGZpbGUsIG9yIGFsbG93bGlzdAogICAgICAgYW55IHRoYXQgYXJlIGludGVudGlvbmFsbHkga2VwdCAoYW4gT1NJU00gaW52ZW50aW9uIHdpdGggbm8gdXBzdHJlYW0KICAgICAgIHNlcnZpY2UpLgogIFJlZnM6IG9wZW5zdGFjay9rb2xsYS1hbnNpYmxlIChzZXJ2aWNlIHJlbW92ZWQgdXBzdHJlYW0pCiAgICAgICAgb3Npc20vZGVmYXVsdHMgYWxsLzAwMi1pbWFnZXMta29sbGEueW1sCgprb2xsYV9vcnBoYW5fY29uZmlnIOKAlCAyIGRlYWQgY29tcGFuaW9uL2ltYWdlIGNvbmZpZyB2YXJzIGZvciBzZXJ2aWNlcwp1cHN0cmVhbSByZW1vdmVkICh0aGVpciBlbmFibGVfPG5hbWU+IGZsYWcgaXMgcmVwb3J0ZWQgc2VwYXJhdGVseSBieQprb2xsYV9lbmFibGVtZW50X29ycGhhbik7IHRoZXNlIG11c3QgYmUgcmVtb3ZlZCB0b286CgogICAgY2hyb255X2FsbG93ZWRfc3VibmV0cywgY2hyb255X3NlcnZlcnMKCiAgRml4OiByZW1vdmUgdGhlc2UgdmFycyBmcm9tIHRoZSBsaXN0ZWQgb3Npc20vZGVmYXVsdHMgZmlsZSwgb3IgYWxsb3dsaXN0CiAgICAgICBhbnkgdGhhdCBhcmUgaW50ZW50aW9uYWxseSBrZXB0IChhbiBPU0lTTSBpbnZlbnRpb24gd2l0aCBubyB1cHN0cmVhbQogICAgICAgc2VydmljZSkuCiAgUmVmczogb3BlbnN0YWNrL2tvbGxhLWFuc2libGUgKHNlcnZpY2UgcmVtb3ZlZCB1cHN0cmVhbSkKICAgICAgICBvc2lzbS9kZWZhdWx0cyBhbGwvMDk5LWdlbmVyaWMueW1sCgprb2xsYV9vcnBoYW5fY29uZmlnIOKAlCA1IGRlYWQgY29tcGFuaW9uL2ltYWdlIGNvbmZpZyB2YXJzIGZvciBzZXJ2aWNlcwp1cHN0cmVhbSByZW1vdmVkICh0aGVpciBlbmFibGVfPG5hbWU+IGZsYWcgaXMgcmVwb3J0ZWQgc2VwYXJhdGVseSBieQprb2xsYV9lbmFibGVtZW50X29ycGhhbik7IHRoZXNlIG11c3QgYmUgcmVtb3ZlZCB0b286CgogICAgb3BlbnN0YWNrX2V4cG9ydGVyX2Nsb3Vkc195bWxfY2xvdWQsIG9wZW5zdGFja19leHBvcnRlcl9jbG91ZHNfeW1sX3BhdGgsCiAgICBvcGVuc3RhY2tfZXhwb3J0ZXJfc2VjdXJlX3ltbF9wYXRoLAogICAgb3BlbnN0YWNrX2V4cG9ydGVyX3NlcnZpY2VfbW9uaXRvcl9zY3JhcGVfaW50ZXJ2YWwsCiAgICBvcGVuc3RhY2tfZXhwb3J0ZXJfc2VydmljZV9tb25pdG9yX3NjcmFwZV90aW1lb3V0CgogIEZpeDogcmVtb3ZlIHRoZXNlIHZhcnMgZnJvbSB0aGUgbGlzdGVkIG9zaXNtL2RlZmF1bHRzIGZpbGUsIG9yIGFsbG93bGlzdAogICAgICAgYW55IHRoYXQgYXJlIGludGVudGlvbmFsbHkga2VwdCAoYW4gT1NJU00gaW52ZW50aW9uIHdpdGggbm8gdXBzdHJlYW0KICAgICAgIHNlcnZpY2UpLgogIFJlZnM6IG9wZW5zdGFjay9rb2xsYS1hbnNpYmxlIChzZXJ2aWNlIHJlbW92ZWQgdXBzdHJlYW0pCiAgICAgICAgb3Npc20vZGVmYXVsdHMgYWxsLzA5OS1pbmZyYXN0cnVjdHVyZS55bWwKClN1bW1hcnk6IDczIHRvIGFjdCBvbiwgNDIgYWxsb3dsaXN0ZWQsIDAgc3RhbGUgYWxsb3dsaXN0IGVudHJpZXMgKDExNSB0b3RhbCkKYGBgCgojIyMgSG93IHRvIHJlYWQgaXQKLSAqKmltYWdlIHBpbnMqKiDigJQgRE9STUFOVCBgcm9sZV9zaGFkb3dzYCBsYWcgYnV0IHRoZSByZWxlYXNlIHBpbiBnb3Zlcm5zIGF0CiAgZGVwbG95IChsb3cgcHJpb3JpdHkpOyBMSVZFIGByb2xlX3NoYWRvd3NgIChgZG5zbWFzcWAsIGBzY2FwaGFuZHJlYCwgYHN0ZXBjYWApCiAgYWN0dWFsbHkgZGVwbG95IHRoZSByb2xlIGRlZmF1bHQgKHdpcmUgaW50byBgaW1hZ2VzLnltbC5qMmApOyBgcm9sZV91bnBpbm5lZGAKICAoYGh0dHBkKmAsIGBrZXBsZXJgLCBgdGhhbm9zX3NpZGVjYXJgKSBoYXZlIG5vIHJlbGVhc2UgcGluOyBgaW1hZ2Vfb3JwaGFuYAogIChgbmdpbnhgLCBgb3Npc21fbmV0Ym94YCwgYHJlZ2lzdHJ5YCkgYXJlIGVtaXR0ZWQgYnV0IGNvbnN1bWVkIG5vd2hlcmUuCi0gKiprb2xsYSByZW1vdmFsLWRpcmVjdGlvbioqIOKAlCBga29sbGFfZW5hYmxlbWVudF9vcnBoYW5gICsgYGtvbGxhX29ycGhhbl9jb25maWdgOgogIHN0YWxlIGBlbmFibGVfKmAgZmxhZ3MgYW5kIHRoZWlyIGNvbXBhbmlvbiB2YXJzIGZvciBzZXJ2aWNlcyB1cHN0cmVhbSBkcm9wcGVkOwogIGRyb3AgdGhlIHJlbW92ZWQgcHJvamVjdHMsIGFsbG93bGlzdCB0aGUgT1NJU00gaW52ZW50aW9ucy4KLSAqKmtvbGxhIGFkZC1kaXJlY3Rpb24qKiDigJQgYGtvbGxhX2dyb3VwdmFyc19taXNzaW5nYDogdGhlIGBvc2lzbS9kZWZhdWx0c2AKICByZS1zeW5jIGJhY2tsb2cuIFRoZSByaWdodCBmaXggZm9yIG1vc3QgaXMgdG8gbWlycm9yIHRoZSB1cHN0cmVhbSB2YXIgaW50bwogIGBvc2lzbS9kZWZhdWx0c2AgKGhhcm1sZXNzIHdoZW4gdGhlIHNlcnZpY2UgaXMgb2ZmLCBjb3JyZWN0IHdoZW4gYW4gZW52aXJvbm1lbnQKICBlbmFibGVzIGl0IOKAlCBlLmcuIG1ldGFsYm94IHR1cm5zIG9uIGlyb25pYywgc28gYGVuYWJsZV9pcm9uaWNfcHhlX2ZpbHRlcmAgYml0ZXMKICB0aGVyZSk7IGFsbG93bGlzdCBvbmx5IGdlbnVpbmUgZGl2ZXJnZW5jZXMgKHRoZSBgZXV0cm9uYCB0eXBvKS4KCj4gVGhlIGRldGVjdG9yIHJ1bnMgdW5hdXRoZW50aWNhdGVkIGFnYWluc3QgR2l0SHViIChubyB0b2tlbiBzdXBwb3J0IGluCj4gYHNvdXJjZS5weWApLCBzYW1lIGFzIGdlbmVyaWNzJyBleGlzdGluZyBkYWlseSBqb2IuIEEgaGVhdnkgcnVuIGNhbiBvY2Nhc2lvbmFsbHkKPiBoaXQgQVBJIHJhdGUgbGltaXRzIGFuZCBmYWlsIHdpdGhvdXQgYSByZXBvcnQ7IGFkZGluZyBgR0lUSFVCX1RPS0VOYCBzdXBwb3J0IGlzIGEKPiBzZW5zaWJsZSBmb2xsb3ctdXAuCg==
      patchset: fc13e299d64508bc30d9936cf050c5a87f4ee7f1
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          trusted/project_1/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          trusted/project_2/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: 1d71e58c93442038e695a5d883d57a603182c7ae
          untrusted/project_0/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          untrusted/project_1/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          untrusted/project_2/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: 1d71e58c93442038e695a5d883d57a603182c7ae
        playbooks:
        - path: untrusted/project_0/github.com/osism/openinfra-zuul-jobs/playbooks/tox/run.yaml
          roles:
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_0/zuul-config
            link_target: untrusted/project_1/github.com/osism/zuul-config
            role_path: ansible/playbook_0/role_0/zuul-config/roles
          - checkout: master
            checkout_description: playbook branch
            link_name: ansible/playbook_0/role_1/openinfra-zuul-jobs
            link_target: untrusted/project_0/github.com/osism/openinfra-zuul-jobs
            role_path: ansible/playbook_0/role_1/openinfra-zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_2/zuul-jobs
            link_target: untrusted/project_2/github.com/osism/zuul-jobs
            role_path: ansible/playbook_0/role_2/zuul-jobs/roles
      post_review: false
      post_timeout: 1800
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/osism/release
        name: osism/release
        short_name: release
        src_dir: src/github.com/osism/release
      projects:
        github.com/osism/release:
          canonical_hostname: github.com
          canonical_name: github.com/osism/release
          checkout: main
          checkout_description: zuul branch
          commit: 17ca0bac00c6ecbba2a854eb392602bdf14f0a1b
          name: osism/release
          required: false
          short_name: release
          src_dir: src/github.com/osism/release
      ref: refs/pull/2561/head
      resources: {}
      tenant: osism
      timeout: 1800
      topic: null
      voting: true
