all:
  children:
    tempest:
      hosts:
        controller: null
    zuul_unreachable:
      hosts: {}
  hosts:
    controller:
      ansible_connection: ssh
      ansible_host: 199.204.45.78
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      configure_swap_size: 8192
      devstack_local_conf:
        post-config:
          $NEUTRON_CONF:
            DEFAULT:
              global_physnet_mtu: '{{ external_bridge_mtu }}'
          /etc/magnum/magnum.conf:
            cluster_template:
              kubernetes_allowed_network_drivers: calico,cilium
              kubernetes_default_network_driver: calico
            nova_client:
              api_version: 2.15
          /etc/manila/manila.conf:
            generic:
              connect_share_server_to_tenant_network: true
              driver_handles_share_servers: true
      devstack_localrc:
        ADMIN_PASSWORD: secretadmin
        DATABASE_PASSWORD: secretdatabase
        DEBUG_LIBVIRT_COREDUMPS: true
        DISABLE_AMP_IMAGE_BUILD: true
        ENABLE_SYSCTL_MEM_TUNING: true
        ENABLE_SYSCTL_NET_TUNING: true
        ENABLE_ZSWAP: true
        ERROR_ON_CLONE: true
        FIXED_RANGE: 10.1.0.0/20
        FLOATING_RANGE: 172.24.5.0/24
        GIT_BASE: https://github.com
        HOST_IP: '{{ hostvars[''controller''][''nodepool''][''private_ipv4''] }}'
        IPV4_ADDRS_SAFE_TO_USE: 10.1.0.0/20
        LIBVIRT_TYPE: '{{ devstack_libvirt_type | default("qemu") }}'
        LOGFILE: /opt/stack/logs/devstacklog.txt
        LOG_COLOR: false
        MAGNUM_GUEST_IMAGE_URL: '{{ image_url }}'
        MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: snapshot_support=True create_share_from_snapshot_support=True
        MANILA_ENABLED_BACKENDS: generic
        MANILA_USE_SERVICE_INSTANCE_PASSWORD: true
        NETWORK_GATEWAY: 10.1.0.1
        NOVA_LIBVIRT_TB_CACHE_SIZE: 128
        NOVA_VNC_ENABLED: true
        OCTAVIA_NODE: api
        OVN_DBS_LOG_LEVEL: dbg
        PUBLIC_BRIDGE_MTU: '{{ external_bridge_mtu }}'
        PUBLIC_NETWORK_GATEWAY: 172.24.5.1
        RABBIT_PASSWORD: secretrabbit
        SERVICE_HOST: '{{ hostvars[''controller''][''nodepool''][''private_ipv4'']
          }}'
        SERVICE_PASSWORD: secretservice
        SWIFT_HASH: 1234123412341234
        SWIFT_REPLICAS: 1
        SWIFT_START_ALL_SERVICES: false
        VERBOSE: true
        VERBOSE_NO_TIMESTAMP: true
      devstack_plugins:
        barbican: https://github.com/openstack/barbican
        magnum: https://review.opendev.org/openstack/magnum
        magnum-cluster-api: https://github.com/vexxhost/magnum-cluster-api
        manila: https://github.com/openstack/manila
        octavia: https://github.com/openstack/octavia
        ovn-octavia-provider: https://github.com/openstack/ovn-octavia-provider
      devstack_services:
        base: false
        c-api: true
        c-bak: true
        c-sch: true
        c-vol: true
        dstat: false
        etcd3: true
        file_tracker: true
        g-api: true
        horizon: false
        key: true
        memory_tracker: true
        mysql: true
        n-api: true
        n-api-meta: true
        n-cond: true
        n-cpu: true
        n-novnc: true
        n-sch: true
        o-api: true
        o-da: true
        o-hk: true
        octavia: true
        openstack-cli-server: true
        ovn-controller: true
        ovn-northd: true
        ovs-vswitchd: true
        ovsdb-server: true
        placement-api: true
        q-ovn-agent: true
        q-svc: true
        rabbit: true
        s-account: false
        s-container: false
        s-object: false
        s-proxy: false
        tempest: false
        tls-proxy: true
      extensions_to_txt:
        auto: true
        conf: true
        localrc: true
        log: true
        stackenv: true
      image_url: https://github.com/vexxhost/capo-image-elements/releases/latest/download/ubuntu-22.04-{{
        kube_tag }}.qcow2
      kube_tag: v1.33.11
      network_driver: calico
      nodepool:
        az: nova
        cloud: public
        external_id: 9da4a2e4-a827-4420-9277-54700bc257f0
        host_id: 6782d36b49969b8e83d63aec7c00c7f3fbf4fe3934e38116e588005f
        interface_ip: 199.204.45.78
        label: ubuntu-noble-16
        node_properties: {}
        private_ipv4: 199.204.45.78
        private_ipv6: null
        provider: yul1
        public_ipv4: 199.204.45.78
        public_ipv6: 2604:e100:1:0:f816:3eff:fe25:c308
        region: ca-ymq-1
        slot: null
      zuul_copy_output:
        /etc/ceph: logs
        /etc/glusterfs/glusterd.vol: logs
        /etc/libvirt: logs
        /etc/lvm: logs
        /etc/resolv.conf: logs
        /etc/sudoers: logs
        /etc/sudoers.d: logs
        /var/log/ceph: logs
        /var/log/glusterfs: logs
        /var/log/libvirt: logs
        /var/log/mysql: logs
        /var/log/openvswitch: logs
        /var/log/postgresql: logs
        /var/log/rabbitmq: logs
        /var/log/unbound.log: logs
        '{{ devstack_conf_dir }}/.localrc.auto': logs
        '{{ devstack_conf_dir }}/.stackenv': logs
        '{{ devstack_conf_dir }}/local.conf': logs
        '{{ devstack_conf_dir }}/localrc': logs
        '{{ devstack_full_log}}': logs
        '{{ devstack_log_dir }}/atop': logs
        '{{ devstack_log_dir }}/devstacklog.txt': logs
        '{{ devstack_log_dir }}/devstacklog.txt.summary': logs
        '{{ devstack_log_dir }}/dstat-csv.log': logs
        '{{ devstack_log_dir }}/qemu.coredump': logs
        '{{ devstack_log_dir }}/tcpdump.pcap': logs
        '{{ devstack_log_dir }}/worlddump-latest.txt': logs
        '{{ stage_dir }}/apache': logs
        '{{ stage_dir }}/apache_config': logs
        '{{ stage_dir }}/audit.log': logs
        '{{ stage_dir }}/core': logs
        '{{ stage_dir }}/deprecations.log': logs
        '{{ stage_dir }}/df.txt': logs
        '{{ stage_dir }}/dpkg-l.txt': logs
        '{{ stage_dir }}/etc': logs
        '{{ stage_dir }}/iptables.txt': logs
        '{{ stage_dir }}/listen53.txt': logs
        '{{ stage_dir }}/mount.txt': logs
        '{{ stage_dir }}/performance.json': logs
        '{{ stage_dir }}/pip2-freeze.txt': logs
        '{{ stage_dir }}/pip3-freeze.txt': logs
        '{{ stage_dir }}/rpm-qa.txt': logs
        '{{ stage_dir }}/services.txt': logs
        '{{ stage_dir }}/verify_tempest_conf.log': logs
      zuul_node:
        az: nova
        cloud: public
        external_id: 9da4a2e4-a827-4420-9277-54700bc257f0
        host_id: 6782d36b49969b8e83d63aec7c00c7f3fbf4fe3934e38116e588005f
        interface_ip: 199.204.45.78
        label: ubuntu-noble-16
        node_properties: {}
        private_ipv4: 199.204.45.78
        private_ipv6: null
        provider: yul1
        public_ipv4: 199.204.45.78
        public_ipv6: 2604:e100:1:0:f816:3eff:fe25:c308
        region: ca-ymq-1
        slot: null
        uuid: null
  vars:
    configure_swap_size: 8192
    devstack_local_conf:
      post-config:
        $NEUTRON_CONF:
          DEFAULT:
            global_physnet_mtu: '{{ external_bridge_mtu }}'
        /etc/magnum/magnum.conf:
          cluster_template:
            kubernetes_allowed_network_drivers: calico,cilium
            kubernetes_default_network_driver: calico
          nova_client:
            api_version: 2.15
        /etc/manila/manila.conf:
          generic:
            connect_share_server_to_tenant_network: true
            driver_handles_share_servers: true
    devstack_localrc:
      ADMIN_PASSWORD: secretadmin
      DATABASE_PASSWORD: secretdatabase
      DEBUG_LIBVIRT_COREDUMPS: true
      DISABLE_AMP_IMAGE_BUILD: true
      ENABLE_SYSCTL_MEM_TUNING: true
      ENABLE_SYSCTL_NET_TUNING: true
      ENABLE_ZSWAP: true
      ERROR_ON_CLONE: true
      FIXED_RANGE: 10.1.0.0/20
      FLOATING_RANGE: 172.24.5.0/24
      GIT_BASE: https://github.com
      HOST_IP: '{{ hostvars[''controller''][''nodepool''][''private_ipv4''] }}'
      IPV4_ADDRS_SAFE_TO_USE: 10.1.0.0/20
      LIBVIRT_TYPE: '{{ devstack_libvirt_type | default("qemu") }}'
      LOGFILE: /opt/stack/logs/devstacklog.txt
      LOG_COLOR: false
      MAGNUM_GUEST_IMAGE_URL: '{{ image_url }}'
      MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: snapshot_support=True create_share_from_snapshot_support=True
      MANILA_ENABLED_BACKENDS: generic
      MANILA_USE_SERVICE_INSTANCE_PASSWORD: true
      NETWORK_GATEWAY: 10.1.0.1
      NOVA_LIBVIRT_TB_CACHE_SIZE: 128
      NOVA_VNC_ENABLED: true
      OCTAVIA_NODE: api
      OVN_DBS_LOG_LEVEL: dbg
      PUBLIC_BRIDGE_MTU: '{{ external_bridge_mtu }}'
      PUBLIC_NETWORK_GATEWAY: 172.24.5.1
      RABBIT_PASSWORD: secretrabbit
      SERVICE_HOST: '{{ hostvars[''controller''][''nodepool''][''private_ipv4''] }}'
      SERVICE_PASSWORD: secretservice
      SWIFT_HASH: 1234123412341234
      SWIFT_REPLICAS: 1
      SWIFT_START_ALL_SERVICES: false
      VERBOSE: true
      VERBOSE_NO_TIMESTAMP: true
    devstack_plugins:
      barbican: https://github.com/openstack/barbican
      magnum: https://review.opendev.org/openstack/magnum
      magnum-cluster-api: https://github.com/vexxhost/magnum-cluster-api
      manila: https://github.com/openstack/manila
      octavia: https://github.com/openstack/octavia
      ovn-octavia-provider: https://github.com/openstack/ovn-octavia-provider
    devstack_services:
      base: false
      c-api: true
      c-bak: true
      c-sch: true
      c-vol: true
      dstat: false
      etcd3: true
      file_tracker: true
      g-api: true
      horizon: false
      key: true
      memory_tracker: true
      mysql: true
      n-api: true
      n-api-meta: true
      n-cond: true
      n-cpu: true
      n-novnc: true
      n-sch: true
      o-api: true
      o-da: true
      o-hk: true
      octavia: true
      openstack-cli-server: true
      ovn-controller: true
      ovn-northd: true
      ovs-vswitchd: true
      ovsdb-server: true
      placement-api: true
      q-ovn-agent: true
      q-svc: true
      rabbit: true
      s-account: false
      s-container: false
      s-object: false
      s-proxy: false
      tempest: false
      tls-proxy: true
    extensions_to_txt:
      auto: true
      conf: true
      localrc: true
      log: true
      stackenv: true
    image_url: https://github.com/vexxhost/capo-image-elements/releases/latest/download/ubuntu-22.04-{{
      kube_tag }}.qcow2
    kube_tag: v1.33.11
    network_driver: calico
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/zuul-config/zuul.d/jobs.yaml@main#1>'
      - '<Job openstack-multinode-fips explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/zuul-config/zuul.d/jobs.yaml@main#17>'
      - '<Job devstack-base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:master}}
        source: openstack/devstack/.zuul.yaml@master#368>'
      - '<Job devstack-minimal explicit: None implied: {MatchAny:{ImpliedBranchMatcher:master}}
        source: openstack/devstack/.zuul.yaml@master#502>'
      - '<Job devstack explicit: None implied: {MatchAny:{ImpliedBranchMatcher:master}}
        source: openstack/devstack/.zuul.yaml@master#545>'
      - '<Job magnum-cluster-api-devstack explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/magnum-cluster-api/zuul.d/jobs.yaml@main#1>'
      - '<Job magnum-cluster-api-hydrophone explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/magnum-cluster-api/zuul.d/jobs.yaml@main#60>'
      - '<Job magnum-cluster-api-hydrophone-v1.33.11 explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: vexxhost/magnum-cluster-api/zuul.d/jobs.yaml@main#72>'
      - '<Job magnum-cluster-api-hydrophone-v1.33.11-calico explicit: None implied:
        {MatchAny:{ImpliedBranchMatcher:main}} source: vexxhost/magnum-cluster-api/zuul.d/jobs.yaml@main#78>'
      - '<Job magnum-cluster-api-hydrophone-v1.33.11-calico explicit: None implied:
        None source: vexxhost/magnum-cluster-api/zuul.d/project.yaml@main#1>'
      ansible_version: '9'
      attempts: 1
      branch: main
      build: cb697379681a4493be3aea334d69de62
      build_refs:
      - branch: main
        change: '1013'
        change_message: "Support config profiles\n\n## Usage\n\nOperators define reusable
          profiles in the management Kubernetes cluster using `ConfigMap/mcapi-config-profiles`,
          normally in the `magnum-system` namespace. Users select those profiles through
          cluster template labels.\n\nCluster-wide profile:\n\n```bash\nopenstack
          coe cluster template create k8s-bm-gpu \\\n  ...same base template options...
          \\\n  --labels config_profile=profile-gpu\n\nopenstack coe cluster create
          bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nCluster default plus
          a GPU nodegroup layout:\n\n```bash\nopenstack coe cluster template create
          k8s-bm-gpu \\\n  ...same base template options... \\\n  --labels config_profile=profile-standard
          \\\n  --labels nodegroup_config_profile_set=profile-bm-gpu-layout\n\nopenstack
          coe cluster create bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nPassing
          `config_profile` or `nodegroup_config_profile_set` directly through `openstack
          coe cluster create --labels` is rejected unless the value exactly matches
          the selected cluster template. These are bootstrap-time selectors; changing
          the cluster row label later would not rerun cloud-init or kubeadm on existing
          nodes. To change these labels after cluster creation, create a new cluster
          template that selects the desired profile and run Magnum cluster upgrade.
          Magnum cluster update does not support real label changes.\n\n## Problem\n\nBaremetal
          and GPU-oriented Kubernetes clusters need declarative node bootstrap and
          kubelet tuning that survives node replacement, scale-up, and rolling upgrades.
          The older options are not good production interfaces:\n\n- post-create SSH
          edits to node files are not declarative and do not survive node replacement;\n-
          `kubeletExtraArgs` is a CLI flag path, while these settings belong in structured
          kubelet configuration;\n- arbitrary JSON/YAML in Magnum labels would make
          labels an untyped config transport and would be difficult to validate safely;\n-
          exposing every kubelet or cloud-init field as an individual Magnum label
          would create a large and unstable user-facing label surface.\n\n## Solution\n\nPR
          #1013 exposes node bootstrap customization through operator-managed config
          profiles.\n\nA profile can include:\n\n- `kubeletConfig`\n- `files`\n- `preKubeadmCommands`\n-
          `postKubeadmCommands`\n\nMagnum renders kubelet config as `/etc/kubernetes/patches/kubeletconfiguration+merge.yaml`,
          adds the kubeadm patch directory, and appends profile files/commands into
          CAPI kubeadm bootstrap fields. Magnum owns `apiVersion`, `kind`, and `nodegroups`;
          profiles must not set those fields. Profiles must use the structured wrapper
          fields above. Unwrapped kubelet-only YAML such as `maxPods: 250` is rejected.\n\n```yaml\napiVersion:
          v1\nkind: ConfigMap\nmetadata:\n  name: mcapi-config-profiles\n  namespace:
          magnum-system\ndata:\n  profile-standard: |\n    kubeletConfig:\n      maxPods:
          110\n    files:\n      - path: /etc/atmosphere/profile-role\n        content:
          standard\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-standard.sh\n
          \   postKubeadmCommands:\n      - echo standard-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-gpu: |\n    kubeletConfig:\n      cpuManagerPolicy: static\n      cpuManagerPolicyOptions:\n
          \       full-pcpus-only: \"true\"\n      memoryManagerPolicy: Static\n      topologyManagerPolicy:
          single-numa-node\n      topologyManagerScope: pod\n      reservedSystemCPUs:
          0-1\n      maxPods: 250\n    files:\n      - path: /etc/atmosphere/profile-role\n
          \       content: gpu\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-gpu.sh\n
          \   postKubeadmCommands:\n      - echo gpu-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-bm-gpu-layout: |\n    nodegroups:\n      gpu-workers:\n        profile:
          profile-gpu\n```\n\nUsers select predefined profiles with two cluster template
          labels:\n\n| Label | Example | Effect |\n|---|---|---|\n| `config_profile`
          | `profile-standard` | Selects the cluster-wide default profile rendered
          as the `configProfile` ClusterClass topology variable. |\n| `nodegroup_config_profile_set`
          | `profile-bm-gpu-layout` | Selects an operator-defined nodegroup layout
          that renders MachineDeployment-level `configProfile` overrides. |\n\nThere
          are no per-field kubelet labels, no arbitrary user JSON passthrough, and
          no compatibility aliases for names that only existed in the unmerged PR
          history. The user-facing surface is named profile selection, and the operator
          controls the profile contents. Missing profiles, invalid YAML, reserved
          fields, invalid file entries, unknown nodegroup profile references, and
          cluster-create selector overrides fail validation before CAPI rendering.\n\nThis
          covers the PR #1015 use case through profiles. A single operator-managed
          profile can write files and run pre/post kubeadm commands for every node
          in the cluster, and a nodegroup layout can apply a different profile only
          to GPU workers while controllers and other workers keep the standard profile.
          This avoids exposing `extra_files` or command labels directly to users.\n\n##
          Documentation\n\n- `docs/user/labels.md` documents the selector labels,
          their cluster-template source of truth, and the supported profile fields.\n-
          `docs/user/use-cases.md` shows operator-defined profiles and user selection
          through cluster templates.\n- `docs/developer/cluster-topology.md` documents
          how profiles map to topology variables and MachineDeployment overrides.\n-
          The internal validation note under `notes/logs/mcapi` documents the detailed
          OVS/OVN AIO runbook and recovery details.\n\n## Validation\n\nLocal validation
          after the latest fixes:\n\n```bash\ncargo test kubelet_config --quiet\n#
          2 passed\n\ncargo test containerd_config --quiet\n# 2 passed\n\nuv run --python
          3.10 pytest magnum_cluster_api/tests/unit/test_resources.py magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 66 passed, 7 warnings\n```\n\nBroader local validation previously
          run on this branch:\n\n```bash\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 55 passed, 7 warnings\n\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_resources.py
          magnum_cluster_api/tests/unit/test_utils.py magnum_cluster_api/tests/unit/test_integrations_common.py
          magnum_cluster_api/tests/unit/test_integrations_cinder.py -q\n# 72 passed,
          7 warnings\n\nKUBECONFIG=<temporary test kubeconfig> uv run --python 3.10
          pytest magnum_cluster_api/tests/unit/test_driver.py -q\n# 56 passed, 15
          warnings\n\nrustfmt --check src/features/kubelet_config.rs\n# passed\n\ngit
          diff --check\n# passed\n```\n\nReal-case AIO validation on the PR code path:\n\n-
          Rolled Magnum API/conductor/proxy images built from the PR code path into
          one OVS AIO and one OVN AIO. The branch was later rewritten only to normalize
          DCO trailers and add the Black formatting fix; latest pushed head is `9593f3b`.\n-
          Verified cluster-create selector override rejection on both backends: trying
          to pass `config_profile=profile-gpu` at cluster create time against a template
          selecting another value failed before CAPI rendering.\n- Verified fresh
          cluster create with selector labels only on the cluster template passed
          PR #1013 validation/rendering on both backends; the validation environments
          then stopped on exhausted OpenStack server-group quota before node creation.\n-
          Verified an upgrade/update path on both backends through Magnum cluster
          upgrade to a template selecting `profile-safe`. Both clusters reached `UPDATE_COMPLETE`;
          CAPI reported `TopologyReconciled=True` and `RollingOut=False`; workload
          control-plane and worker nodes were Ready.\n- Verified rendered topology
          content for the converged profile, including `maxPods: 111`, profile-managed
          files, profile `preKubeadmCommands`, and profile `postKubeadmCommands` while
          preserving existing bootstrap commands.\n- Verified the aggressive `profile-gpu`
          content renders the expected kubelet patch, files, and pre/post commands.
          It is not a valid runtime profile for the non-GPU VM AIO workers, so convergence
          testing used `profile-safe`; final GPU runtime settings still need validation
          on real GPU or bare-metal nodes.\n- The live upgrade test found and fixed
          a worker bootstrap edge case: Cluster API can prune empty worker command
          arrays, so the first worker profile command must create the list before
          later commands append to it. That fix remains included in the current stack
          as `fix: seed worker config profile command arrays`.\n\nPR #1015 is already
          closed and superseded by this config-profile model.\n\nSigned-off-by: Rico
          Lin <rico@vexxhost.com>"
        change_url: https://github.com/vexxhost/magnum-cluster-api/pull/1013
        commit_id: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        patchset: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/magnum-cluster-api
          name: vexxhost/magnum-cluster-api
          short_name: magnum-cluster-api
          src_dir: src/github.com/vexxhost/magnum-cluster-api
        src_dir: src/github.com/vexxhost/magnum-cluster-api
        topic: null
      buildset: c9d11c974eb24e34ad7a9b692aea34b6
      buildset_refs:
      - branch: main
        change: '1013'
        change_message: "Support config profiles\n\n## Usage\n\nOperators define reusable
          profiles in the management Kubernetes cluster using `ConfigMap/mcapi-config-profiles`,
          normally in the `magnum-system` namespace. Users select those profiles through
          cluster template labels.\n\nCluster-wide profile:\n\n```bash\nopenstack
          coe cluster template create k8s-bm-gpu \\\n  ...same base template options...
          \\\n  --labels config_profile=profile-gpu\n\nopenstack coe cluster create
          bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nCluster default plus
          a GPU nodegroup layout:\n\n```bash\nopenstack coe cluster template create
          k8s-bm-gpu \\\n  ...same base template options... \\\n  --labels config_profile=profile-standard
          \\\n  --labels nodegroup_config_profile_set=profile-bm-gpu-layout\n\nopenstack
          coe cluster create bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nPassing
          `config_profile` or `nodegroup_config_profile_set` directly through `openstack
          coe cluster create --labels` is rejected unless the value exactly matches
          the selected cluster template. These are bootstrap-time selectors; changing
          the cluster row label later would not rerun cloud-init or kubeadm on existing
          nodes. To change these labels after cluster creation, create a new cluster
          template that selects the desired profile and run Magnum cluster upgrade.
          Magnum cluster update does not support real label changes.\n\n## Problem\n\nBaremetal
          and GPU-oriented Kubernetes clusters need declarative node bootstrap and
          kubelet tuning that survives node replacement, scale-up, and rolling upgrades.
          The older options are not good production interfaces:\n\n- post-create SSH
          edits to node files are not declarative and do not survive node replacement;\n-
          `kubeletExtraArgs` is a CLI flag path, while these settings belong in structured
          kubelet configuration;\n- arbitrary JSON/YAML in Magnum labels would make
          labels an untyped config transport and would be difficult to validate safely;\n-
          exposing every kubelet or cloud-init field as an individual Magnum label
          would create a large and unstable user-facing label surface.\n\n## Solution\n\nPR
          #1013 exposes node bootstrap customization through operator-managed config
          profiles.\n\nA profile can include:\n\n- `kubeletConfig`\n- `files`\n- `preKubeadmCommands`\n-
          `postKubeadmCommands`\n\nMagnum renders kubelet config as `/etc/kubernetes/patches/kubeletconfiguration+merge.yaml`,
          adds the kubeadm patch directory, and appends profile files/commands into
          CAPI kubeadm bootstrap fields. Magnum owns `apiVersion`, `kind`, and `nodegroups`;
          profiles must not set those fields. Profiles must use the structured wrapper
          fields above. Unwrapped kubelet-only YAML such as `maxPods: 250` is rejected.\n\n```yaml\napiVersion:
          v1\nkind: ConfigMap\nmetadata:\n  name: mcapi-config-profiles\n  namespace:
          magnum-system\ndata:\n  profile-standard: |\n    kubeletConfig:\n      maxPods:
          110\n    files:\n      - path: /etc/atmosphere/profile-role\n        content:
          standard\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-standard.sh\n
          \   postKubeadmCommands:\n      - echo standard-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-gpu: |\n    kubeletConfig:\n      cpuManagerPolicy: static\n      cpuManagerPolicyOptions:\n
          \       full-pcpus-only: \"true\"\n      memoryManagerPolicy: Static\n      topologyManagerPolicy:
          single-numa-node\n      topologyManagerScope: pod\n      reservedSystemCPUs:
          0-1\n      maxPods: 250\n    files:\n      - path: /etc/atmosphere/profile-role\n
          \       content: gpu\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-gpu.sh\n
          \   postKubeadmCommands:\n      - echo gpu-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-bm-gpu-layout: |\n    nodegroups:\n      gpu-workers:\n        profile:
          profile-gpu\n```\n\nUsers select predefined profiles with two cluster template
          labels:\n\n| Label | Example | Effect |\n|---|---|---|\n| `config_profile`
          | `profile-standard` | Selects the cluster-wide default profile rendered
          as the `configProfile` ClusterClass topology variable. |\n| `nodegroup_config_profile_set`
          | `profile-bm-gpu-layout` | Selects an operator-defined nodegroup layout
          that renders MachineDeployment-level `configProfile` overrides. |\n\nThere
          are no per-field kubelet labels, no arbitrary user JSON passthrough, and
          no compatibility aliases for names that only existed in the unmerged PR
          history. The user-facing surface is named profile selection, and the operator
          controls the profile contents. Missing profiles, invalid YAML, reserved
          fields, invalid file entries, unknown nodegroup profile references, and
          cluster-create selector overrides fail validation before CAPI rendering.\n\nThis
          covers the PR #1015 use case through profiles. A single operator-managed
          profile can write files and run pre/post kubeadm commands for every node
          in the cluster, and a nodegroup layout can apply a different profile only
          to GPU workers while controllers and other workers keep the standard profile.
          This avoids exposing `extra_files` or command labels directly to users.\n\n##
          Documentation\n\n- `docs/user/labels.md` documents the selector labels,
          their cluster-template source of truth, and the supported profile fields.\n-
          `docs/user/use-cases.md` shows operator-defined profiles and user selection
          through cluster templates.\n- `docs/developer/cluster-topology.md` documents
          how profiles map to topology variables and MachineDeployment overrides.\n-
          The internal validation note under `notes/logs/mcapi` documents the detailed
          OVS/OVN AIO runbook and recovery details.\n\n## Validation\n\nLocal validation
          after the latest fixes:\n\n```bash\ncargo test kubelet_config --quiet\n#
          2 passed\n\ncargo test containerd_config --quiet\n# 2 passed\n\nuv run --python
          3.10 pytest magnum_cluster_api/tests/unit/test_resources.py magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 66 passed, 7 warnings\n```\n\nBroader local validation previously
          run on this branch:\n\n```bash\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 55 passed, 7 warnings\n\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_resources.py
          magnum_cluster_api/tests/unit/test_utils.py magnum_cluster_api/tests/unit/test_integrations_common.py
          magnum_cluster_api/tests/unit/test_integrations_cinder.py -q\n# 72 passed,
          7 warnings\n\nKUBECONFIG=<temporary test kubeconfig> uv run --python 3.10
          pytest magnum_cluster_api/tests/unit/test_driver.py -q\n# 56 passed, 15
          warnings\n\nrustfmt --check src/features/kubelet_config.rs\n# passed\n\ngit
          diff --check\n# passed\n```\n\nReal-case AIO validation on the PR code path:\n\n-
          Rolled Magnum API/conductor/proxy images built from the PR code path into
          one OVS AIO and one OVN AIO. The branch was later rewritten only to normalize
          DCO trailers and add the Black formatting fix; latest pushed head is `9593f3b`.\n-
          Verified cluster-create selector override rejection on both backends: trying
          to pass `config_profile=profile-gpu` at cluster create time against a template
          selecting another value failed before CAPI rendering.\n- Verified fresh
          cluster create with selector labels only on the cluster template passed
          PR #1013 validation/rendering on both backends; the validation environments
          then stopped on exhausted OpenStack server-group quota before node creation.\n-
          Verified an upgrade/update path on both backends through Magnum cluster
          upgrade to a template selecting `profile-safe`. Both clusters reached `UPDATE_COMPLETE`;
          CAPI reported `TopologyReconciled=True` and `RollingOut=False`; workload
          control-plane and worker nodes were Ready.\n- Verified rendered topology
          content for the converged profile, including `maxPods: 111`, profile-managed
          files, profile `preKubeadmCommands`, and profile `postKubeadmCommands` while
          preserving existing bootstrap commands.\n- Verified the aggressive `profile-gpu`
          content renders the expected kubelet patch, files, and pre/post commands.
          It is not a valid runtime profile for the non-GPU VM AIO workers, so convergence
          testing used `profile-safe`; final GPU runtime settings still need validation
          on real GPU or bare-metal nodes.\n- The live upgrade test found and fixed
          a worker bootstrap edge case: Cluster API can prune empty worker command
          arrays, so the first worker profile command must create the list before
          later commands append to it. That fix remains included in the current stack
          as `fix: seed worker config profile command arrays`.\n\nPR #1015 is already
          closed and superseded by this config-profile model.\n\nSigned-off-by: Rico
          Lin <rico@vexxhost.com>"
        change_url: https://github.com/vexxhost/magnum-cluster-api/pull/1013
        commit_id: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        patchset: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/magnum-cluster-api
          name: vexxhost/magnum-cluster-api
          short_name: magnum-cluster-api
          src_dir: src/github.com/vexxhost/magnum-cluster-api
        src_dir: src/github.com/vexxhost/magnum-cluster-api
        topic: null
      change: '1013'
      change_message: "Support config profiles\n\n## Usage\n\nOperators define reusable
        profiles in the management Kubernetes cluster using `ConfigMap/mcapi-config-profiles`,
        normally in the `magnum-system` namespace. Users select those profiles through
        cluster template labels.\n\nCluster-wide profile:\n\n```bash\nopenstack coe
        cluster template create k8s-bm-gpu \\\n  ...same base template options...
        \\\n  --labels config_profile=profile-gpu\n\nopenstack coe cluster create
        bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nCluster default plus a
        GPU nodegroup layout:\n\n```bash\nopenstack coe cluster template create k8s-bm-gpu
        \\\n  ...same base template options... \\\n  --labels config_profile=profile-standard
        \\\n  --labels nodegroup_config_profile_set=profile-bm-gpu-layout\n\nopenstack
        coe cluster create bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nPassing
        `config_profile` or `nodegroup_config_profile_set` directly through `openstack
        coe cluster create --labels` is rejected unless the value exactly matches
        the selected cluster template. These are bootstrap-time selectors; changing
        the cluster row label later would not rerun cloud-init or kubeadm on existing
        nodes. To change these labels after cluster creation, create a new cluster
        template that selects the desired profile and run Magnum cluster upgrade.
        Magnum cluster update does not support real label changes.\n\n## Problem\n\nBaremetal
        and GPU-oriented Kubernetes clusters need declarative node bootstrap and kubelet
        tuning that survives node replacement, scale-up, and rolling upgrades. The
        older options are not good production interfaces:\n\n- post-create SSH edits
        to node files are not declarative and do not survive node replacement;\n-
        `kubeletExtraArgs` is a CLI flag path, while these settings belong in structured
        kubelet configuration;\n- arbitrary JSON/YAML in Magnum labels would make
        labels an untyped config transport and would be difficult to validate safely;\n-
        exposing every kubelet or cloud-init field as an individual Magnum label would
        create a large and unstable user-facing label surface.\n\n## Solution\n\nPR
        #1013 exposes node bootstrap customization through operator-managed config
        profiles.\n\nA profile can include:\n\n- `kubeletConfig`\n- `files`\n- `preKubeadmCommands`\n-
        `postKubeadmCommands`\n\nMagnum renders kubelet config as `/etc/kubernetes/patches/kubeletconfiguration+merge.yaml`,
        adds the kubeadm patch directory, and appends profile files/commands into
        CAPI kubeadm bootstrap fields. Magnum owns `apiVersion`, `kind`, and `nodegroups`;
        profiles must not set those fields. Profiles must use the structured wrapper
        fields above. Unwrapped kubelet-only YAML such as `maxPods: 250` is rejected.\n\n```yaml\napiVersion:
        v1\nkind: ConfigMap\nmetadata:\n  name: mcapi-config-profiles\n  namespace:
        magnum-system\ndata:\n  profile-standard: |\n    kubeletConfig:\n      maxPods:
        110\n    files:\n      - path: /etc/atmosphere/profile-role\n        content:
        standard\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-standard.sh\n
        \   postKubeadmCommands:\n      - echo standard-post >/var/log/atmosphere/config-profile-post\n\n
        \ profile-gpu: |\n    kubeletConfig:\n      cpuManagerPolicy: static\n      cpuManagerPolicyOptions:\n
        \       full-pcpus-only: \"true\"\n      memoryManagerPolicy: Static\n      topologyManagerPolicy:
        single-numa-node\n      topologyManagerScope: pod\n      reservedSystemCPUs:
        0-1\n      maxPods: 250\n    files:\n      - path: /etc/atmosphere/profile-role\n
        \       content: gpu\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-gpu.sh\n
        \   postKubeadmCommands:\n      - echo gpu-post >/var/log/atmosphere/config-profile-post\n\n
        \ profile-bm-gpu-layout: |\n    nodegroups:\n      gpu-workers:\n        profile:
        profile-gpu\n```\n\nUsers select predefined profiles with two cluster template
        labels:\n\n| Label | Example | Effect |\n|---|---|---|\n| `config_profile`
        | `profile-standard` | Selects the cluster-wide default profile rendered as
        the `configProfile` ClusterClass topology variable. |\n| `nodegroup_config_profile_set`
        | `profile-bm-gpu-layout` | Selects an operator-defined nodegroup layout that
        renders MachineDeployment-level `configProfile` overrides. |\n\nThere are
        no per-field kubelet labels, no arbitrary user JSON passthrough, and no compatibility
        aliases for names that only existed in the unmerged PR history. The user-facing
        surface is named profile selection, and the operator controls the profile
        contents. Missing profiles, invalid YAML, reserved fields, invalid file entries,
        unknown nodegroup profile references, and cluster-create selector overrides
        fail validation before CAPI rendering.\n\nThis covers the PR #1015 use case
        through profiles. A single operator-managed profile can write files and run
        pre/post kubeadm commands for every node in the cluster, and a nodegroup layout
        can apply a different profile only to GPU workers while controllers and other
        workers keep the standard profile. This avoids exposing `extra_files` or command
        labels directly to users.\n\n## Documentation\n\n- `docs/user/labels.md` documents
        the selector labels, their cluster-template source of truth, and the supported
        profile fields.\n- `docs/user/use-cases.md` shows operator-defined profiles
        and user selection through cluster templates.\n- `docs/developer/cluster-topology.md`
        documents how profiles map to topology variables and MachineDeployment overrides.\n-
        The internal validation note under `notes/logs/mcapi` documents the detailed
        OVS/OVN AIO runbook and recovery details.\n\n## Validation\n\nLocal validation
        after the latest fixes:\n\n```bash\ncargo test kubelet_config --quiet\n# 2
        passed\n\ncargo test containerd_config --quiet\n# 2 passed\n\nuv run --python
        3.10 pytest magnum_cluster_api/tests/unit/test_resources.py magnum_cluster_api/tests/unit/test_utils.py
        -q\n# 66 passed, 7 warnings\n```\n\nBroader local validation previously run
        on this branch:\n\n```bash\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_utils.py
        -q\n# 55 passed, 7 warnings\n\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_resources.py
        magnum_cluster_api/tests/unit/test_utils.py magnum_cluster_api/tests/unit/test_integrations_common.py
        magnum_cluster_api/tests/unit/test_integrations_cinder.py -q\n# 72 passed,
        7 warnings\n\nKUBECONFIG=<temporary test kubeconfig> uv run --python 3.10
        pytest magnum_cluster_api/tests/unit/test_driver.py -q\n# 56 passed, 15 warnings\n\nrustfmt
        --check src/features/kubelet_config.rs\n# passed\n\ngit diff --check\n# passed\n```\n\nReal-case
        AIO validation on the PR code path:\n\n- Rolled Magnum API/conductor/proxy
        images built from the PR code path into one OVS AIO and one OVN AIO. The branch
        was later rewritten only to normalize DCO trailers and add the Black formatting
        fix; latest pushed head is `9593f3b`.\n- Verified cluster-create selector
        override rejection on both backends: trying to pass `config_profile=profile-gpu`
        at cluster create time against a template selecting another value failed before
        CAPI rendering.\n- Verified fresh cluster create with selector labels only
        on the cluster template passed PR #1013 validation/rendering on both backends;
        the validation environments then stopped on exhausted OpenStack server-group
        quota before node creation.\n- Verified an upgrade/update path on both backends
        through Magnum cluster upgrade to a template selecting `profile-safe`. Both
        clusters reached `UPDATE_COMPLETE`; CAPI reported `TopologyReconciled=True`
        and `RollingOut=False`; workload control-plane and worker nodes were Ready.\n-
        Verified rendered topology content for the converged profile, including `maxPods:
        111`, profile-managed files, profile `preKubeadmCommands`, and profile `postKubeadmCommands`
        while preserving existing bootstrap commands.\n- Verified the aggressive `profile-gpu`
        content renders the expected kubelet patch, files, and pre/post commands.
        It is not a valid runtime profile for the non-GPU VM AIO workers, so convergence
        testing used `profile-safe`; final GPU runtime settings still need validation
        on real GPU or bare-metal nodes.\n- The live upgrade test found and fixed
        a worker bootstrap edge case: Cluster API can prune empty worker command arrays,
        so the first worker profile command must create the list before later commands
        append to it. That fix remains included in the current stack as `fix: seed
        worker config profile command arrays`.\n\nPR #1015 is already closed and superseded
        by this config-profile model.\n\nSigned-off-by: Rico Lin <rico@vexxhost.com>"
      change_url: https://github.com/vexxhost/magnum-cluster-api/pull/1013
      child_jobs: []
      commit_id: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
      event_id: eb4ba180-5964-11f1-9aef-a74aa1093d02
      executor:
        hostname: 2d72f0692154
        inventory_file: /var/lib/zuul/builds/cb697379681a4493be3aea334d69de62/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/cb697379681a4493be3aea334d69de62/work/logs
        result_data_file: /var/lib/zuul/builds/cb697379681a4493be3aea334d69de62/work/results.json
        src_root: /var/lib/zuul/builds/cb697379681a4493be3aea334d69de62/work/src
        work_root: /var/lib/zuul/builds/cb697379681a4493be3aea334d69de62/work
      include_vars: []
      items:
      - branch: main
        change: '1013'
        change_message: "Support config profiles\n\n## Usage\n\nOperators define reusable
          profiles in the management Kubernetes cluster using `ConfigMap/mcapi-config-profiles`,
          normally in the `magnum-system` namespace. Users select those profiles through
          cluster template labels.\n\nCluster-wide profile:\n\n```bash\nopenstack
          coe cluster template create k8s-bm-gpu \\\n  ...same base template options...
          \\\n  --labels config_profile=profile-gpu\n\nopenstack coe cluster create
          bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nCluster default plus
          a GPU nodegroup layout:\n\n```bash\nopenstack coe cluster template create
          k8s-bm-gpu \\\n  ...same base template options... \\\n  --labels config_profile=profile-standard
          \\\n  --labels nodegroup_config_profile_set=profile-bm-gpu-layout\n\nopenstack
          coe cluster create bm-gpu \\\n  --cluster-template k8s-bm-gpu\n```\n\nPassing
          `config_profile` or `nodegroup_config_profile_set` directly through `openstack
          coe cluster create --labels` is rejected unless the value exactly matches
          the selected cluster template. These are bootstrap-time selectors; changing
          the cluster row label later would not rerun cloud-init or kubeadm on existing
          nodes. To change these labels after cluster creation, create a new cluster
          template that selects the desired profile and run Magnum cluster upgrade.
          Magnum cluster update does not support real label changes.\n\n## Problem\n\nBaremetal
          and GPU-oriented Kubernetes clusters need declarative node bootstrap and
          kubelet tuning that survives node replacement, scale-up, and rolling upgrades.
          The older options are not good production interfaces:\n\n- post-create SSH
          edits to node files are not declarative and do not survive node replacement;\n-
          `kubeletExtraArgs` is a CLI flag path, while these settings belong in structured
          kubelet configuration;\n- arbitrary JSON/YAML in Magnum labels would make
          labels an untyped config transport and would be difficult to validate safely;\n-
          exposing every kubelet or cloud-init field as an individual Magnum label
          would create a large and unstable user-facing label surface.\n\n## Solution\n\nPR
          #1013 exposes node bootstrap customization through operator-managed config
          profiles.\n\nA profile can include:\n\n- `kubeletConfig`\n- `files`\n- `preKubeadmCommands`\n-
          `postKubeadmCommands`\n\nMagnum renders kubelet config as `/etc/kubernetes/patches/kubeletconfiguration+merge.yaml`,
          adds the kubeadm patch directory, and appends profile files/commands into
          CAPI kubeadm bootstrap fields. Magnum owns `apiVersion`, `kind`, and `nodegroups`;
          profiles must not set those fields. Profiles must use the structured wrapper
          fields above. Unwrapped kubelet-only YAML such as `maxPods: 250` is rejected.\n\n```yaml\napiVersion:
          v1\nkind: ConfigMap\nmetadata:\n  name: mcapi-config-profiles\n  namespace:
          magnum-system\ndata:\n  profile-standard: |\n    kubeletConfig:\n      maxPods:
          110\n    files:\n      - path: /etc/atmosphere/profile-role\n        content:
          standard\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-standard.sh\n
          \   postKubeadmCommands:\n      - echo standard-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-gpu: |\n    kubeletConfig:\n      cpuManagerPolicy: static\n      cpuManagerPolicyOptions:\n
          \       full-pcpus-only: \"true\"\n      memoryManagerPolicy: Static\n      topologyManagerPolicy:
          single-numa-node\n      topologyManagerScope: pod\n      reservedSystemCPUs:
          0-1\n      maxPods: 250\n    files:\n      - path: /etc/atmosphere/profile-role\n
          \       content: gpu\n    preKubeadmCommands:\n      - bash /etc/atmosphere/profile-gpu.sh\n
          \   postKubeadmCommands:\n      - echo gpu-post >/var/log/atmosphere/config-profile-post\n\n
          \ profile-bm-gpu-layout: |\n    nodegroups:\n      gpu-workers:\n        profile:
          profile-gpu\n```\n\nUsers select predefined profiles with two cluster template
          labels:\n\n| Label | Example | Effect |\n|---|---|---|\n| `config_profile`
          | `profile-standard` | Selects the cluster-wide default profile rendered
          as the `configProfile` ClusterClass topology variable. |\n| `nodegroup_config_profile_set`
          | `profile-bm-gpu-layout` | Selects an operator-defined nodegroup layout
          that renders MachineDeployment-level `configProfile` overrides. |\n\nThere
          are no per-field kubelet labels, no arbitrary user JSON passthrough, and
          no compatibility aliases for names that only existed in the unmerged PR
          history. The user-facing surface is named profile selection, and the operator
          controls the profile contents. Missing profiles, invalid YAML, reserved
          fields, invalid file entries, unknown nodegroup profile references, and
          cluster-create selector overrides fail validation before CAPI rendering.\n\nThis
          covers the PR #1015 use case through profiles. A single operator-managed
          profile can write files and run pre/post kubeadm commands for every node
          in the cluster, and a nodegroup layout can apply a different profile only
          to GPU workers while controllers and other workers keep the standard profile.
          This avoids exposing `extra_files` or command labels directly to users.\n\n##
          Documentation\n\n- `docs/user/labels.md` documents the selector labels,
          their cluster-template source of truth, and the supported profile fields.\n-
          `docs/user/use-cases.md` shows operator-defined profiles and user selection
          through cluster templates.\n- `docs/developer/cluster-topology.md` documents
          how profiles map to topology variables and MachineDeployment overrides.\n-
          The internal validation note under `notes/logs/mcapi` documents the detailed
          OVS/OVN AIO runbook and recovery details.\n\n## Validation\n\nLocal validation
          after the latest fixes:\n\n```bash\ncargo test kubelet_config --quiet\n#
          2 passed\n\ncargo test containerd_config --quiet\n# 2 passed\n\nuv run --python
          3.10 pytest magnum_cluster_api/tests/unit/test_resources.py magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 66 passed, 7 warnings\n```\n\nBroader local validation previously
          run on this branch:\n\n```bash\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_utils.py
          -q\n# 55 passed, 7 warnings\n\nuv run --python 3.10 pytest magnum_cluster_api/tests/unit/test_resources.py
          magnum_cluster_api/tests/unit/test_utils.py magnum_cluster_api/tests/unit/test_integrations_common.py
          magnum_cluster_api/tests/unit/test_integrations_cinder.py -q\n# 72 passed,
          7 warnings\n\nKUBECONFIG=<temporary test kubeconfig> uv run --python 3.10
          pytest magnum_cluster_api/tests/unit/test_driver.py -q\n# 56 passed, 15
          warnings\n\nrustfmt --check src/features/kubelet_config.rs\n# passed\n\ngit
          diff --check\n# passed\n```\n\nReal-case AIO validation on the PR code path:\n\n-
          Rolled Magnum API/conductor/proxy images built from the PR code path into
          one OVS AIO and one OVN AIO. The branch was later rewritten only to normalize
          DCO trailers and add the Black formatting fix; latest pushed head is `9593f3b`.\n-
          Verified cluster-create selector override rejection on both backends: trying
          to pass `config_profile=profile-gpu` at cluster create time against a template
          selecting another value failed before CAPI rendering.\n- Verified fresh
          cluster create with selector labels only on the cluster template passed
          PR #1013 validation/rendering on both backends; the validation environments
          then stopped on exhausted OpenStack server-group quota before node creation.\n-
          Verified an upgrade/update path on both backends through Magnum cluster
          upgrade to a template selecting `profile-safe`. Both clusters reached `UPDATE_COMPLETE`;
          CAPI reported `TopologyReconciled=True` and `RollingOut=False`; workload
          control-plane and worker nodes were Ready.\n- Verified rendered topology
          content for the converged profile, including `maxPods: 111`, profile-managed
          files, profile `preKubeadmCommands`, and profile `postKubeadmCommands` while
          preserving existing bootstrap commands.\n- Verified the aggressive `profile-gpu`
          content renders the expected kubelet patch, files, and pre/post commands.
          It is not a valid runtime profile for the non-GPU VM AIO workers, so convergence
          testing used `profile-safe`; final GPU runtime settings still need validation
          on real GPU or bare-metal nodes.\n- The live upgrade test found and fixed
          a worker bootstrap edge case: Cluster API can prune empty worker command
          arrays, so the first worker profile command must create the list before
          later commands append to it. That fix remains included in the current stack
          as `fix: seed worker config profile command arrays`.\n\nPR #1015 is already
          closed and superseded by this config-profile model.\n\nSigned-off-by: Rico
          Lin <rico@vexxhost.com>"
        change_url: https://github.com/vexxhost/magnum-cluster-api/pull/1013
        commit_id: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        patchset: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        project:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/magnum-cluster-api
          name: vexxhost/magnum-cluster-api
          short_name: magnum-cluster-api
          src_dir: src/github.com/vexxhost/magnum-cluster-api
        topic: null
      job: magnum-cluster-api-hydrophone-v1.33.11-calico
      jobtags: []
      max_attempts: 3
      message: U3VwcG9ydCBjb25maWcgcHJvZmlsZXMKCiMjIFVzYWdlCgpPcGVyYXRvcnMgZGVmaW5lIHJldXNhYmxlIHByb2ZpbGVzIGluIHRoZSBtYW5hZ2VtZW50IEt1YmVybmV0ZXMgY2x1c3RlciB1c2luZyBgQ29uZmlnTWFwL21jYXBpLWNvbmZpZy1wcm9maWxlc2AsIG5vcm1hbGx5IGluIHRoZSBgbWFnbnVtLXN5c3RlbWAgbmFtZXNwYWNlLiBVc2VycyBzZWxlY3QgdGhvc2UgcHJvZmlsZXMgdGhyb3VnaCBjbHVzdGVyIHRlbXBsYXRlIGxhYmVscy4KCkNsdXN0ZXItd2lkZSBwcm9maWxlOgoKYGBgYmFzaApvcGVuc3RhY2sgY29lIGNsdXN0ZXIgdGVtcGxhdGUgY3JlYXRlIGs4cy1ibS1ncHUgXAogIC4uLnNhbWUgYmFzZSB0ZW1wbGF0ZSBvcHRpb25zLi4uIFwKICAtLWxhYmVscyBjb25maWdfcHJvZmlsZT1wcm9maWxlLWdwdQoKb3BlbnN0YWNrIGNvZSBjbHVzdGVyIGNyZWF0ZSBibS1ncHUgXAogIC0tY2x1c3Rlci10ZW1wbGF0ZSBrOHMtYm0tZ3B1CmBgYAoKQ2x1c3RlciBkZWZhdWx0IHBsdXMgYSBHUFUgbm9kZWdyb3VwIGxheW91dDoKCmBgYGJhc2gKb3BlbnN0YWNrIGNvZSBjbHVzdGVyIHRlbXBsYXRlIGNyZWF0ZSBrOHMtYm0tZ3B1IFwKICAuLi5zYW1lIGJhc2UgdGVtcGxhdGUgb3B0aW9ucy4uLiBcCiAgLS1sYWJlbHMgY29uZmlnX3Byb2ZpbGU9cHJvZmlsZS1zdGFuZGFyZCBcCiAgLS1sYWJlbHMgbm9kZWdyb3VwX2NvbmZpZ19wcm9maWxlX3NldD1wcm9maWxlLWJtLWdwdS1sYXlvdXQKCm9wZW5zdGFjayBjb2UgY2x1c3RlciBjcmVhdGUgYm0tZ3B1IFwKICAtLWNsdXN0ZXItdGVtcGxhdGUgazhzLWJtLWdwdQpgYGAKClBhc3NpbmcgYGNvbmZpZ19wcm9maWxlYCBvciBgbm9kZWdyb3VwX2NvbmZpZ19wcm9maWxlX3NldGAgZGlyZWN0bHkgdGhyb3VnaCBgb3BlbnN0YWNrIGNvZSBjbHVzdGVyIGNyZWF0ZSAtLWxhYmVsc2AgaXMgcmVqZWN0ZWQgdW5sZXNzIHRoZSB2YWx1ZSBleGFjdGx5IG1hdGNoZXMgdGhlIHNlbGVjdGVkIGNsdXN0ZXIgdGVtcGxhdGUuIFRoZXNlIGFyZSBib290c3RyYXAtdGltZSBzZWxlY3RvcnM7IGNoYW5naW5nIHRoZSBjbHVzdGVyIHJvdyBsYWJlbCBsYXRlciB3b3VsZCBub3QgcmVydW4gY2xvdWQtaW5pdCBvciBrdWJlYWRtIG9uIGV4aXN0aW5nIG5vZGVzLiBUbyBjaGFuZ2UgdGhlc2UgbGFiZWxzIGFmdGVyIGNsdXN0ZXIgY3JlYXRpb24sIGNyZWF0ZSBhIG5ldyBjbHVzdGVyIHRlbXBsYXRlIHRoYXQgc2VsZWN0cyB0aGUgZGVzaXJlZCBwcm9maWxlIGFuZCBydW4gTWFnbnVtIGNsdXN0ZXIgdXBncmFkZS4gTWFnbnVtIGNsdXN0ZXIgdXBkYXRlIGRvZXMgbm90IHN1cHBvcnQgcmVhbCBsYWJlbCBjaGFuZ2VzLgoKIyMgUHJvYmxlbQoKQmFyZW1ldGFsIGFuZCBHUFUtb3JpZW50ZWQgS3ViZXJuZXRlcyBjbHVzdGVycyBuZWVkIGRlY2xhcmF0aXZlIG5vZGUgYm9vdHN0cmFwIGFuZCBrdWJlbGV0IHR1bmluZyB0aGF0IHN1cnZpdmVzIG5vZGUgcmVwbGFjZW1lbnQsIHNjYWxlLXVwLCBhbmQgcm9sbGluZyB1cGdyYWRlcy4gVGhlIG9sZGVyIG9wdGlvbnMgYXJlIG5vdCBnb29kIHByb2R1Y3Rpb24gaW50ZXJmYWNlczoKCi0gcG9zdC1jcmVhdGUgU1NIIGVkaXRzIHRvIG5vZGUgZmlsZXMgYXJlIG5vdCBkZWNsYXJhdGl2ZSBhbmQgZG8gbm90IHN1cnZpdmUgbm9kZSByZXBsYWNlbWVudDsKLSBga3ViZWxldEV4dHJhQXJnc2AgaXMgYSBDTEkgZmxhZyBwYXRoLCB3aGlsZSB0aGVzZSBzZXR0aW5ncyBiZWxvbmcgaW4gc3RydWN0dXJlZCBrdWJlbGV0IGNvbmZpZ3VyYXRpb247Ci0gYXJiaXRyYXJ5IEpTT04vWUFNTCBpbiBNYWdudW0gbGFiZWxzIHdvdWxkIG1ha2UgbGFiZWxzIGFuIHVudHlwZWQgY29uZmlnIHRyYW5zcG9ydCBhbmQgd291bGQgYmUgZGlmZmljdWx0IHRvIHZhbGlkYXRlIHNhZmVseTsKLSBleHBvc2luZyBldmVyeSBrdWJlbGV0IG9yIGNsb3VkLWluaXQgZmllbGQgYXMgYW4gaW5kaXZpZHVhbCBNYWdudW0gbGFiZWwgd291bGQgY3JlYXRlIGEgbGFyZ2UgYW5kIHVuc3RhYmxlIHVzZXItZmFjaW5nIGxhYmVsIHN1cmZhY2UuCgojIyBTb2x1dGlvbgoKUFIgIzEwMTMgZXhwb3NlcyBub2RlIGJvb3RzdHJhcCBjdXN0b21pemF0aW9uIHRocm91Z2ggb3BlcmF0b3ItbWFuYWdlZCBjb25maWcgcHJvZmlsZXMuCgpBIHByb2ZpbGUgY2FuIGluY2x1ZGU6CgotIGBrdWJlbGV0Q29uZmlnYAotIGBmaWxlc2AKLSBgcHJlS3ViZWFkbUNvbW1hbmRzYAotIGBwb3N0S3ViZWFkbUNvbW1hbmRzYAoKTWFnbnVtIHJlbmRlcnMga3ViZWxldCBjb25maWcgYXMgYC9ldGMva3ViZXJuZXRlcy9wYXRjaGVzL2t1YmVsZXRjb25maWd1cmF0aW9uK21lcmdlLnlhbWxgLCBhZGRzIHRoZSBrdWJlYWRtIHBhdGNoIGRpcmVjdG9yeSwgYW5kIGFwcGVuZHMgcHJvZmlsZSBmaWxlcy9jb21tYW5kcyBpbnRvIENBUEkga3ViZWFkbSBib290c3RyYXAgZmllbGRzLiBNYWdudW0gb3ducyBgYXBpVmVyc2lvbmAsIGBraW5kYCwgYW5kIGBub2RlZ3JvdXBzYDsgcHJvZmlsZXMgbXVzdCBub3Qgc2V0IHRob3NlIGZpZWxkcy4gUHJvZmlsZXMgbXVzdCB1c2UgdGhlIHN0cnVjdHVyZWQgd3JhcHBlciBmaWVsZHMgYWJvdmUuIFVud3JhcHBlZCBrdWJlbGV0LW9ubHkgWUFNTCBzdWNoIGFzIGBtYXhQb2RzOiAyNTBgIGlzIHJlamVjdGVkLgoKYGBgeWFtbAphcGlWZXJzaW9uOiB2MQpraW5kOiBDb25maWdNYXAKbWV0YWRhdGE6CiAgbmFtZTogbWNhcGktY29uZmlnLXByb2ZpbGVzCiAgbmFtZXNwYWNlOiBtYWdudW0tc3lzdGVtCmRhdGE6CiAgcHJvZmlsZS1zdGFuZGFyZDogfAogICAga3ViZWxldENvbmZpZzoKICAgICAgbWF4UG9kczogMTEwCiAgICBmaWxlczoKICAgICAgLSBwYXRoOiAvZXRjL2F0bW9zcGhlcmUvcHJvZmlsZS1yb2xlCiAgICAgICAgY29udGVudDogc3RhbmRhcmQKICAgIHByZUt1YmVhZG1Db21tYW5kczoKICAgICAgLSBiYXNoIC9ldGMvYXRtb3NwaGVyZS9wcm9maWxlLXN0YW5kYXJkLnNoCiAgICBwb3N0S3ViZWFkbUNvbW1hbmRzOgogICAgICAtIGVjaG8gc3RhbmRhcmQtcG9zdCA+L3Zhci9sb2cvYXRtb3NwaGVyZS9jb25maWctcHJvZmlsZS1wb3N0CgogIHByb2ZpbGUtZ3B1OiB8CiAgICBrdWJlbGV0Q29uZmlnOgogICAgICBjcHVNYW5hZ2VyUG9saWN5OiBzdGF0aWMKICAgICAgY3B1TWFuYWdlclBvbGljeU9wdGlvbnM6CiAgICAgICAgZnVsbC1wY3B1cy1vbmx5OiAidHJ1ZSIKICAgICAgbWVtb3J5TWFuYWdlclBvbGljeTogU3RhdGljCiAgICAgIHRvcG9sb2d5TWFuYWdlclBvbGljeTogc2luZ2xlLW51bWEtbm9kZQogICAgICB0b3BvbG9neU1hbmFnZXJTY29wZTogcG9kCiAgICAgIHJlc2VydmVkU3lzdGVtQ1BVczogMC0xCiAgICAgIG1heFBvZHM6IDI1MAogICAgZmlsZXM6CiAgICAgIC0gcGF0aDogL2V0Yy9hdG1vc3BoZXJlL3Byb2ZpbGUtcm9sZQogICAgICAgIGNvbnRlbnQ6IGdwdQogICAgcHJlS3ViZWFkbUNvbW1hbmRzOgogICAgICAtIGJhc2ggL2V0Yy9hdG1vc3BoZXJlL3Byb2ZpbGUtZ3B1LnNoCiAgICBwb3N0S3ViZWFkbUNvbW1hbmRzOgogICAgICAtIGVjaG8gZ3B1LXBvc3QgPi92YXIvbG9nL2F0bW9zcGhlcmUvY29uZmlnLXByb2ZpbGUtcG9zdAoKICBwcm9maWxlLWJtLWdwdS1sYXlvdXQ6IHwKICAgIG5vZGVncm91cHM6CiAgICAgIGdwdS13b3JrZXJzOgogICAgICAgIHByb2ZpbGU6IHByb2ZpbGUtZ3B1CmBgYAoKVXNlcnMgc2VsZWN0IHByZWRlZmluZWQgcHJvZmlsZXMgd2l0aCB0d28gY2x1c3RlciB0ZW1wbGF0ZSBsYWJlbHM6Cgp8IExhYmVsIHwgRXhhbXBsZSB8IEVmZmVjdCB8CnwtLS18LS0tfC0tLXwKfCBgY29uZmlnX3Byb2ZpbGVgIHwgYHByb2ZpbGUtc3RhbmRhcmRgIHwgU2VsZWN0cyB0aGUgY2x1c3Rlci13aWRlIGRlZmF1bHQgcHJvZmlsZSByZW5kZXJlZCBhcyB0aGUgYGNvbmZpZ1Byb2ZpbGVgIENsdXN0ZXJDbGFzcyB0b3BvbG9neSB2YXJpYWJsZS4gfAp8IGBub2RlZ3JvdXBfY29uZmlnX3Byb2ZpbGVfc2V0YCB8IGBwcm9maWxlLWJtLWdwdS1sYXlvdXRgIHwgU2VsZWN0cyBhbiBvcGVyYXRvci1kZWZpbmVkIG5vZGVncm91cCBsYXlvdXQgdGhhdCByZW5kZXJzIE1hY2hpbmVEZXBsb3ltZW50LWxldmVsIGBjb25maWdQcm9maWxlYCBvdmVycmlkZXMuIHwKClRoZXJlIGFyZSBubyBwZXItZmllbGQga3ViZWxldCBsYWJlbHMsIG5vIGFyYml0cmFyeSB1c2VyIEpTT04gcGFzc3Rocm91Z2gsIGFuZCBubyBjb21wYXRpYmlsaXR5IGFsaWFzZXMgZm9yIG5hbWVzIHRoYXQgb25seSBleGlzdGVkIGluIHRoZSB1bm1lcmdlZCBQUiBoaXN0b3J5LiBUaGUgdXNlci1mYWNpbmcgc3VyZmFjZSBpcyBuYW1lZCBwcm9maWxlIHNlbGVjdGlvbiwgYW5kIHRoZSBvcGVyYXRvciBjb250cm9scyB0aGUgcHJvZmlsZSBjb250ZW50cy4gTWlzc2luZyBwcm9maWxlcywgaW52YWxpZCBZQU1MLCByZXNlcnZlZCBmaWVsZHMsIGludmFsaWQgZmlsZSBlbnRyaWVzLCB1bmtub3duIG5vZGVncm91cCBwcm9maWxlIHJlZmVyZW5jZXMsIGFuZCBjbHVzdGVyLWNyZWF0ZSBzZWxlY3RvciBvdmVycmlkZXMgZmFpbCB2YWxpZGF0aW9uIGJlZm9yZSBDQVBJIHJlbmRlcmluZy4KClRoaXMgY292ZXJzIHRoZSBQUiAjMTAxNSB1c2UgY2FzZSB0aHJvdWdoIHByb2ZpbGVzLiBBIHNpbmdsZSBvcGVyYXRvci1tYW5hZ2VkIHByb2ZpbGUgY2FuIHdyaXRlIGZpbGVzIGFuZCBydW4gcHJlL3Bvc3Qga3ViZWFkbSBjb21tYW5kcyBmb3IgZXZlcnkgbm9kZSBpbiB0aGUgY2x1c3RlciwgYW5kIGEgbm9kZWdyb3VwIGxheW91dCBjYW4gYXBwbHkgYSBkaWZmZXJlbnQgcHJvZmlsZSBvbmx5IHRvIEdQVSB3b3JrZXJzIHdoaWxlIGNvbnRyb2xsZXJzIGFuZCBvdGhlciB3b3JrZXJzIGtlZXAgdGhlIHN0YW5kYXJkIHByb2ZpbGUuIFRoaXMgYXZvaWRzIGV4cG9zaW5nIGBleHRyYV9maWxlc2Agb3IgY29tbWFuZCBsYWJlbHMgZGlyZWN0bHkgdG8gdXNlcnMuCgojIyBEb2N1bWVudGF0aW9uCgotIGBkb2NzL3VzZXIvbGFiZWxzLm1kYCBkb2N1bWVudHMgdGhlIHNlbGVjdG9yIGxhYmVscywgdGhlaXIgY2x1c3Rlci10ZW1wbGF0ZSBzb3VyY2Ugb2YgdHJ1dGgsIGFuZCB0aGUgc3VwcG9ydGVkIHByb2ZpbGUgZmllbGRzLgotIGBkb2NzL3VzZXIvdXNlLWNhc2VzLm1kYCBzaG93cyBvcGVyYXRvci1kZWZpbmVkIHByb2ZpbGVzIGFuZCB1c2VyIHNlbGVjdGlvbiB0aHJvdWdoIGNsdXN0ZXIgdGVtcGxhdGVzLgotIGBkb2NzL2RldmVsb3Blci9jbHVzdGVyLXRvcG9sb2d5Lm1kYCBkb2N1bWVudHMgaG93IHByb2ZpbGVzIG1hcCB0byB0b3BvbG9neSB2YXJpYWJsZXMgYW5kIE1hY2hpbmVEZXBsb3ltZW50IG92ZXJyaWRlcy4KLSBUaGUgaW50ZXJuYWwgdmFsaWRhdGlvbiBub3RlIHVuZGVyIGBub3Rlcy9sb2dzL21jYXBpYCBkb2N1bWVudHMgdGhlIGRldGFpbGVkIE9WUy9PVk4gQUlPIHJ1bmJvb2sgYW5kIHJlY292ZXJ5IGRldGFpbHMuCgojIyBWYWxpZGF0aW9uCgpMb2NhbCB2YWxpZGF0aW9uIGFmdGVyIHRoZSBsYXRlc3QgZml4ZXM6CgpgYGBiYXNoCmNhcmdvIHRlc3Qga3ViZWxldF9jb25maWcgLS1xdWlldAojIDIgcGFzc2VkCgpjYXJnbyB0ZXN0IGNvbnRhaW5lcmRfY29uZmlnIC0tcXVpZXQKIyAyIHBhc3NlZAoKdXYgcnVuIC0tcHl0aG9uIDMuMTAgcHl0ZXN0IG1hZ251bV9jbHVzdGVyX2FwaS90ZXN0cy91bml0L3Rlc3RfcmVzb3VyY2VzLnB5IG1hZ251bV9jbHVzdGVyX2FwaS90ZXN0cy91bml0L3Rlc3RfdXRpbHMucHkgLXEKIyA2NiBwYXNzZWQsIDcgd2FybmluZ3MKYGBgCgpCcm9hZGVyIGxvY2FsIHZhbGlkYXRpb24gcHJldmlvdXNseSBydW4gb24gdGhpcyBicmFuY2g6CgpgYGBiYXNoCnV2IHJ1biAtLXB5dGhvbiAzLjEwIHB5dGVzdCBtYWdudW1fY2x1c3Rlcl9hcGkvdGVzdHMvdW5pdC90ZXN0X3V0aWxzLnB5IC1xCiMgNTUgcGFzc2VkLCA3IHdhcm5pbmdzCgp1diBydW4gLS1weXRob24gMy4xMCBweXRlc3QgbWFnbnVtX2NsdXN0ZXJfYXBpL3Rlc3RzL3VuaXQvdGVzdF9yZXNvdXJjZXMucHkgbWFnbnVtX2NsdXN0ZXJfYXBpL3Rlc3RzL3VuaXQvdGVzdF91dGlscy5weSBtYWdudW1fY2x1c3Rlcl9hcGkvdGVzdHMvdW5pdC90ZXN0X2ludGVncmF0aW9uc19jb21tb24ucHkgbWFnbnVtX2NsdXN0ZXJfYXBpL3Rlc3RzL3VuaXQvdGVzdF9pbnRlZ3JhdGlvbnNfY2luZGVyLnB5IC1xCiMgNzIgcGFzc2VkLCA3IHdhcm5pbmdzCgpLVUJFQ09ORklHPTx0ZW1wb3JhcnkgdGVzdCBrdWJlY29uZmlnPiB1diBydW4gLS1weXRob24gMy4xMCBweXRlc3QgbWFnbnVtX2NsdXN0ZXJfYXBpL3Rlc3RzL3VuaXQvdGVzdF9kcml2ZXIucHkgLXEKIyA1NiBwYXNzZWQsIDE1IHdhcm5pbmdzCgpydXN0Zm10IC0tY2hlY2sgc3JjL2ZlYXR1cmVzL2t1YmVsZXRfY29uZmlnLnJzCiMgcGFzc2VkCgpnaXQgZGlmZiAtLWNoZWNrCiMgcGFzc2VkCmBgYAoKUmVhbC1jYXNlIEFJTyB2YWxpZGF0aW9uIG9uIHRoZSBQUiBjb2RlIHBhdGg6CgotIFJvbGxlZCBNYWdudW0gQVBJL2NvbmR1Y3Rvci9wcm94eSBpbWFnZXMgYnVpbHQgZnJvbSB0aGUgUFIgY29kZSBwYXRoIGludG8gb25lIE9WUyBBSU8gYW5kIG9uZSBPVk4gQUlPLiBUaGUgYnJhbmNoIHdhcyBsYXRlciByZXdyaXR0ZW4gb25seSB0byBub3JtYWxpemUgRENPIHRyYWlsZXJzIGFuZCBhZGQgdGhlIEJsYWNrIGZvcm1hdHRpbmcgZml4OyBsYXRlc3QgcHVzaGVkIGhlYWQgaXMgYDk1OTNmM2JgLgotIFZlcmlmaWVkIGNsdXN0ZXItY3JlYXRlIHNlbGVjdG9yIG92ZXJyaWRlIHJlamVjdGlvbiBvbiBib3RoIGJhY2tlbmRzOiB0cnlpbmcgdG8gcGFzcyBgY29uZmlnX3Byb2ZpbGU9cHJvZmlsZS1ncHVgIGF0IGNsdXN0ZXIgY3JlYXRlIHRpbWUgYWdhaW5zdCBhIHRlbXBsYXRlIHNlbGVjdGluZyBhbm90aGVyIHZhbHVlIGZhaWxlZCBiZWZvcmUgQ0FQSSByZW5kZXJpbmcuCi0gVmVyaWZpZWQgZnJlc2ggY2x1c3RlciBjcmVhdGUgd2l0aCBzZWxlY3RvciBsYWJlbHMgb25seSBvbiB0aGUgY2x1c3RlciB0ZW1wbGF0ZSBwYXNzZWQgUFIgIzEwMTMgdmFsaWRhdGlvbi9yZW5kZXJpbmcgb24gYm90aCBiYWNrZW5kczsgdGhlIHZhbGlkYXRpb24gZW52aXJvbm1lbnRzIHRoZW4gc3RvcHBlZCBvbiBleGhhdXN0ZWQgT3BlblN0YWNrIHNlcnZlci1ncm91cCBxdW90YSBiZWZvcmUgbm9kZSBjcmVhdGlvbi4KLSBWZXJpZmllZCBhbiB1cGdyYWRlL3VwZGF0ZSBwYXRoIG9uIGJvdGggYmFja2VuZHMgdGhyb3VnaCBNYWdudW0gY2x1c3RlciB1cGdyYWRlIHRvIGEgdGVtcGxhdGUgc2VsZWN0aW5nIGBwcm9maWxlLXNhZmVgLiBCb3RoIGNsdXN0ZXJzIHJlYWNoZWQgYFVQREFURV9DT01QTEVURWA7IENBUEkgcmVwb3J0ZWQgYFRvcG9sb2d5UmVjb25jaWxlZD1UcnVlYCBhbmQgYFJvbGxpbmdPdXQ9RmFsc2VgOyB3b3JrbG9hZCBjb250cm9sLXBsYW5lIGFuZCB3b3JrZXIgbm9kZXMgd2VyZSBSZWFkeS4KLSBWZXJpZmllZCByZW5kZXJlZCB0b3BvbG9neSBjb250ZW50IGZvciB0aGUgY29udmVyZ2VkIHByb2ZpbGUsIGluY2x1ZGluZyBgbWF4UG9kczogMTExYCwgcHJvZmlsZS1tYW5hZ2VkIGZpbGVzLCBwcm9maWxlIGBwcmVLdWJlYWRtQ29tbWFuZHNgLCBhbmQgcHJvZmlsZSBgcG9zdEt1YmVhZG1Db21tYW5kc2Agd2hpbGUgcHJlc2VydmluZyBleGlzdGluZyBib290c3RyYXAgY29tbWFuZHMuCi0gVmVyaWZpZWQgdGhlIGFnZ3Jlc3NpdmUgYHByb2ZpbGUtZ3B1YCBjb250ZW50IHJlbmRlcnMgdGhlIGV4cGVjdGVkIGt1YmVsZXQgcGF0Y2gsIGZpbGVzLCBhbmQgcHJlL3Bvc3QgY29tbWFuZHMuIEl0IGlzIG5vdCBhIHZhbGlkIHJ1bnRpbWUgcHJvZmlsZSBmb3IgdGhlIG5vbi1HUFUgVk0gQUlPIHdvcmtlcnMsIHNvIGNvbnZlcmdlbmNlIHRlc3RpbmcgdXNlZCBgcHJvZmlsZS1zYWZlYDsgZmluYWwgR1BVIHJ1bnRpbWUgc2V0dGluZ3Mgc3RpbGwgbmVlZCB2YWxpZGF0aW9uIG9uIHJlYWwgR1BVIG9yIGJhcmUtbWV0YWwgbm9kZXMuCi0gVGhlIGxpdmUgdXBncmFkZSB0ZXN0IGZvdW5kIGFuZCBmaXhlZCBhIHdvcmtlciBib290c3RyYXAgZWRnZSBjYXNlOiBDbHVzdGVyIEFQSSBjYW4gcHJ1bmUgZW1wdHkgd29ya2VyIGNvbW1hbmQgYXJyYXlzLCBzbyB0aGUgZmlyc3Qgd29ya2VyIHByb2ZpbGUgY29tbWFuZCBtdXN0IGNyZWF0ZSB0aGUgbGlzdCBiZWZvcmUgbGF0ZXIgY29tbWFuZHMgYXBwZW5kIHRvIGl0LiBUaGF0IGZpeCByZW1haW5zIGluY2x1ZGVkIGluIHRoZSBjdXJyZW50IHN0YWNrIGFzIGBmaXg6IHNlZWQgd29ya2VyIGNvbmZpZyBwcm9maWxlIGNvbW1hbmQgYXJyYXlzYC4KClBSICMxMDE1IGlzIGFscmVhZHkgY2xvc2VkIGFuZCBzdXBlcnNlZGVkIGJ5IHRoaXMgY29uZmlnLXByb2ZpbGUgbW9kZWwuCgpTaWduZWQtb2ZmLWJ5OiBSaWNvIExpbiA8cmljb0B2ZXh4aG9zdC5jb20+
      override_checkout: master
      patchset: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/vexxhost/zuul-config:
            canonical_name: github.com/vexxhost/zuul-config
            checkout: main
            commit: 298983cd1253e6833abdb49d87d912527e0e6597
          trusted/project_1/opendev.org/zuul/zuul-jobs:
            canonical_name: opendev.org/zuul/zuul-jobs
            checkout: master
            commit: d92d463b41a57b3a411e83e5bc8bc029788d1d2b
          trusted/project_2/github.com/vexxhost/zuul-jobs:
            canonical_name: github.com/vexxhost/zuul-jobs
            checkout: main
            commit: 348c7ff425450b0356e1d84589143dce260be74a
          untrusted/project_0/opendev.org/openstack/devstack:
            canonical_name: opendev.org/openstack/devstack
            checkout: master
            commit: efb5b0e4da07560d72d604b789a3cece6586476e
          untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs:
            canonical_name: opendev.org/openstack/openstack-zuul-jobs
            checkout: master
            commit: aebda82f8822e38db5bbd25ab31ea110792e8c2b
          untrusted/project_2/github.com/vexxhost/zuul-config:
            canonical_name: github.com/vexxhost/zuul-config
            checkout: main
            commit: 298983cd1253e6833abdb49d87d912527e0e6597
          untrusted/project_3/opendev.org/zuul/zuul-jobs:
            canonical_name: opendev.org/zuul/zuul-jobs
            checkout: master
            commit: d92d463b41a57b3a411e83e5bc8bc029788d1d2b
          untrusted/project_4/github.com/vexxhost/zuul-jobs:
            canonical_name: github.com/vexxhost/zuul-jobs
            checkout: main
            commit: 348c7ff425450b0356e1d84589143dce260be74a
          untrusted/project_5/github.com/vexxhost/magnum-cluster-api:
            canonical_name: github.com/vexxhost/magnum-cluster-api
            checkout: main
            commit: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
        playbooks:
        - path: untrusted/project_5/github.com/vexxhost/magnum-cluster-api/zuul.d/playbooks/hydrophone/run.yml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/playbook_0/role_1/devstack
            link_target: untrusted/project_0/opendev.org/openstack/devstack
            role_path: ansible/playbook_0/role_1/devstack/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/playbook_0/role_2/openstack-zuul-jobs
            link_target: untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs
            role_path: ansible/playbook_0/role_2/openstack-zuul-jobs/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/playbook_0/role_4/zuul-jobs
            link_target: untrusted/project_3/opendev.org/zuul/zuul-jobs
            role_path: ansible/playbook_0/role_4/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_5/zuul-jobs
            link_target: untrusted/project_4/github.com/vexxhost/zuul-jobs
            role_path: ansible/playbook_0/role_5/zuul-jobs/roles
        post_playbooks:
        - path: untrusted/project_5/github.com/vexxhost/magnum-cluster-api/zuul.d/playbooks/hydrophone/post.yml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_0/role_1/devstack
            link_target: untrusted/project_0/opendev.org/openstack/devstack
            role_path: ansible/post_playbook_0/role_1/devstack/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_0/role_2/openstack-zuul-jobs
            link_target: untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs
            role_path: ansible/post_playbook_0/role_2/openstack-zuul-jobs/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_0/role_4/zuul-jobs
            link_target: untrusted/project_3/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_0/role_4/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_0/role_5/zuul-jobs
            link_target: untrusted/project_4/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_0/role_5/zuul-jobs/roles
        - path: untrusted/project_0/opendev.org/openstack/devstack/playbooks/post.yaml
          roles:
          - checkout: master
            checkout_description: playbook branch
            link_name: ansible/post_playbook_1/role_0/devstack
            link_target: untrusted/project_0/opendev.org/openstack/devstack
            role_path: ansible/post_playbook_1/role_0/devstack/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_1/role_1/openstack-zuul-jobs
            link_target: untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs
            role_path: ansible/post_playbook_1/role_1/openstack-zuul-jobs/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_1/role_3/zuul-jobs
            link_target: untrusted/project_3/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_1/role_3/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_1/role_4/zuul-jobs
            link_target: untrusted/project_4/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_1/role_4/zuul-jobs/roles
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/post.yaml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_2/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_2/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_2/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_2/role_2/zuul-jobs/roles
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/post-logs.yaml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/post_playbook_3/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/post_playbook_3/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/post_playbook_3/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/post_playbook_3/role_2/zuul-jobs/roles
        pre_playbooks:
        - path: trusted/project_0/github.com/vexxhost/zuul-config/playbooks/base/pre.yaml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_0/role_1/zuul-jobs
            link_target: trusted/project_1/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_0/role_1/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/pre_playbook_0/role_2/zuul-jobs
            link_target: trusted/project_2/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_0/role_2/zuul-jobs/roles
        - path: untrusted/project_0/opendev.org/openstack/devstack/playbooks/pre.yaml
          roles:
          - checkout: master
            checkout_description: playbook branch
            link_name: ansible/pre_playbook_1/role_0/devstack
            link_target: untrusted/project_0/opendev.org/openstack/devstack
            role_path: ansible/pre_playbook_1/role_0/devstack/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_1/role_1/openstack-zuul-jobs
            link_target: untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs
            role_path: ansible/pre_playbook_1/role_1/openstack-zuul-jobs/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_1/role_3/zuul-jobs
            link_target: untrusted/project_3/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_1/role_3/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/pre_playbook_1/role_4/zuul-jobs
            link_target: untrusted/project_4/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_1/role_4/zuul-jobs/roles
        - path: untrusted/project_5/github.com/vexxhost/magnum-cluster-api/zuul.d/playbooks/hydrophone/pre.yml
          roles:
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_2/role_1/devstack
            link_target: untrusted/project_0/opendev.org/openstack/devstack
            role_path: ansible/pre_playbook_2/role_1/devstack/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_2/role_2/openstack-zuul-jobs
            link_target: untrusted/project_1/opendev.org/openstack/openstack-zuul-jobs
            role_path: ansible/pre_playbook_2/role_2/openstack-zuul-jobs/roles
          - checkout: master
            checkout_description: job override ref
            link_name: ansible/pre_playbook_2/role_4/zuul-jobs
            link_target: untrusted/project_3/opendev.org/zuul/zuul-jobs
            role_path: ansible/pre_playbook_2/role_4/zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/pre_playbook_2/role_5/zuul-jobs
            link_target: untrusted/project_4/github.com/vexxhost/zuul-jobs
            role_path: ansible/pre_playbook_2/role_5/zuul-jobs/roles
      post_review: false
      post_timeout: null
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/vexxhost/magnum-cluster-api
        name: vexxhost/magnum-cluster-api
        short_name: magnum-cluster-api
        src_dir: src/github.com/vexxhost/magnum-cluster-api
      projects:
        github.com/novnc/novnc:
          canonical_hostname: github.com
          canonical_name: github.com/novnc/novnc
          checkout: master
          checkout_description: job override ref
          commit: fc5b83c08fbaee38ac448b90eb034002b4fc4ff1
          name: novnc/novnc
          required: true
          short_name: novnc
          src_dir: src/github.com/novnc/novnc
        github.com/vexxhost/magnum-cluster-api:
          canonical_hostname: github.com
          canonical_name: github.com/vexxhost/magnum-cluster-api
          checkout: main
          checkout_description: zuul branch
          commit: 4227f8ea63fd4b490b5e695cf8c794b9aecd2df4
          name: vexxhost/magnum-cluster-api
          required: false
          short_name: magnum-cluster-api
          src_dir: src/github.com/vexxhost/magnum-cluster-api
        opendev.org/openstack/barbican:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/barbican
          checkout: master
          checkout_description: job override ref
          commit: f96e68006c2a46903c5c001139f29012e68d7a5d
          name: openstack/barbican
          required: true
          short_name: barbican
          src_dir: src/opendev.org/openstack/barbican
        opendev.org/openstack/cinder:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/cinder
          checkout: master
          checkout_description: job override ref
          commit: 15bcf475966eab62a638e2663144fbe4b03bedb7
          name: openstack/cinder
          required: true
          short_name: cinder
          src_dir: src/opendev.org/openstack/cinder
        opendev.org/openstack/devstack:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/devstack
          checkout: master
          checkout_description: job override ref
          commit: efb5b0e4da07560d72d604b789a3cece6586476e
          name: openstack/devstack
          required: true
          short_name: devstack
          src_dir: src/opendev.org/openstack/devstack
        opendev.org/openstack/glance:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/glance
          checkout: master
          checkout_description: job override ref
          commit: eac2fa47f26da3515c7a1e8c91226750517c52d4
          name: openstack/glance
          required: true
          short_name: glance
          src_dir: src/opendev.org/openstack/glance
        opendev.org/openstack/keystone:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/keystone
          checkout: master
          checkout_description: job override ref
          commit: 2230026f77a8ed50493d2d58be9120910ceb2089
          name: openstack/keystone
          required: true
          short_name: keystone
          src_dir: src/opendev.org/openstack/keystone
        opendev.org/openstack/magnum:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/magnum
          checkout: master
          checkout_description: job override ref
          commit: 4df4d5752f279635b65688f9fd6d97801f78b805
          name: openstack/magnum
          required: true
          short_name: magnum
          src_dir: src/opendev.org/openstack/magnum
        opendev.org/openstack/manila:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/manila
          checkout: master
          checkout_description: job override ref
          commit: 4a392b68344dd3e3010ea3c0c74681d81589872c
          name: openstack/manila
          required: true
          short_name: manila
          src_dir: src/opendev.org/openstack/manila
        opendev.org/openstack/neutron:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/neutron
          checkout: master
          checkout_description: job override ref
          commit: be04b56ea18b7c284e3fa201e391aa6c17a26060
          name: openstack/neutron
          required: true
          short_name: neutron
          src_dir: src/opendev.org/openstack/neutron
        opendev.org/openstack/nova:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/nova
          checkout: master
          checkout_description: job override ref
          commit: 26c0c65cf2e8b3b4de8f6914eda40756906d77d7
          name: openstack/nova
          required: true
          short_name: nova
          src_dir: src/opendev.org/openstack/nova
        opendev.org/openstack/octavia:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/octavia
          checkout: master
          checkout_description: job override ref
          commit: 9ff4683c8212e4c043af69f1b5ebadc21651dc58
          name: openstack/octavia
          required: true
          short_name: octavia
          src_dir: src/opendev.org/openstack/octavia
        opendev.org/openstack/os-test-images:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/os-test-images
          checkout: master
          checkout_description: job override ref
          commit: 5d0367e03788764f41da8effffa14e3eac513201
          name: openstack/os-test-images
          required: true
          short_name: os-test-images
          src_dir: src/opendev.org/openstack/os-test-images
        opendev.org/openstack/ovn-octavia-provider:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/ovn-octavia-provider
          checkout: master
          checkout_description: job override ref
          commit: d0a7783cd02566847600d5db1c330e6865594331
          name: openstack/ovn-octavia-provider
          required: true
          short_name: ovn-octavia-provider
          src_dir: src/opendev.org/openstack/ovn-octavia-provider
        opendev.org/openstack/placement:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/placement
          checkout: master
          checkout_description: job override ref
          commit: d2727844011a8f144c818556ed7e8d43f756576f
          name: openstack/placement
          required: true
          short_name: placement
          src_dir: src/opendev.org/openstack/placement
        opendev.org/openstack/python-magnumclient:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/python-magnumclient
          checkout: master
          checkout_description: job override ref
          commit: b80aea157f95e9fa6ed14a2d44f9d0cf6ff9332d
          name: openstack/python-magnumclient
          required: true
          short_name: python-magnumclient
          src_dir: src/opendev.org/openstack/python-magnumclient
        opendev.org/openstack/requirements:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/requirements
          checkout: master
          checkout_description: job override ref
          commit: a95648476f1c3fc47b41ca0fb95ba9bcfdcc135c
          name: openstack/requirements
          required: true
          short_name: requirements
          src_dir: src/opendev.org/openstack/requirements
        opendev.org/openstack/swift:
          canonical_hostname: opendev.org
          canonical_name: opendev.org/openstack/swift
          checkout: master
          checkout_description: job override ref
          commit: ca8749f344168ad1c4e07fdfd24e68ca04c57eae
          name: openstack/swift
          required: true
          short_name: swift
          src_dir: src/opendev.org/openstack/swift
      ref: refs/pull/1013/head
      resources: {}
      tenant: oss
      timeout: 7200
      topic: null
      voting: true
    zuul_copy_output:
      /etc/ceph: logs
      /etc/glusterfs/glusterd.vol: logs
      /etc/libvirt: logs
      /etc/lvm: logs
      /etc/resolv.conf: logs
      /etc/sudoers: logs
      /etc/sudoers.d: logs
      /var/log/ceph: logs
      /var/log/glusterfs: logs
      /var/log/libvirt: logs
      /var/log/mysql: logs
      /var/log/openvswitch: logs
      /var/log/postgresql: logs
      /var/log/rabbitmq: logs
      /var/log/unbound.log: logs
      '{{ devstack_conf_dir }}/.localrc.auto': logs
      '{{ devstack_conf_dir }}/.stackenv': logs
      '{{ devstack_conf_dir }}/local.conf': logs
      '{{ devstack_conf_dir }}/localrc': logs
      '{{ devstack_full_log}}': logs
      '{{ devstack_log_dir }}/atop': logs
      '{{ devstack_log_dir }}/devstacklog.txt': logs
      '{{ devstack_log_dir }}/devstacklog.txt.summary': logs
      '{{ devstack_log_dir }}/dstat-csv.log': logs
      '{{ devstack_log_dir }}/qemu.coredump': logs
      '{{ devstack_log_dir }}/tcpdump.pcap': logs
      '{{ devstack_log_dir }}/worlddump-latest.txt': logs
      '{{ stage_dir }}/apache': logs
      '{{ stage_dir }}/apache_config': logs
      '{{ stage_dir }}/audit.log': logs
      '{{ stage_dir }}/core': logs
      '{{ stage_dir }}/deprecations.log': logs
      '{{ stage_dir }}/df.txt': logs
      '{{ stage_dir }}/dpkg-l.txt': logs
      '{{ stage_dir }}/etc': logs
      '{{ stage_dir }}/iptables.txt': logs
      '{{ stage_dir }}/listen53.txt': logs
      '{{ stage_dir }}/mount.txt': logs
      '{{ stage_dir }}/performance.json': logs
      '{{ stage_dir }}/pip2-freeze.txt': logs
      '{{ stage_dir }}/pip3-freeze.txt': logs
      '{{ stage_dir }}/rpm-qa.txt': logs
      '{{ stage_dir }}/services.txt': logs
      '{{ stage_dir }}/verify_tempest_conf.log': logs
