From 216360014c483e27993f25f1fc20ea6cf21a218b Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Thu, 12 Feb 2026 16:33:26 -0500 Subject: [PATCH 1/2] Add playbooks for SKMO Initial testing --- playbooks/skmo/ensure-central-ca-bundle.yaml | 28 ++++ playbooks/skmo/prepare-leaf.yaml | 131 +++++++++++++++++++ playbooks/skmo/trust-leaf-ca.yaml | 51 ++++++++ 3 files changed, 210 insertions(+) create mode 100644 playbooks/skmo/ensure-central-ca-bundle.yaml create mode 100644 playbooks/skmo/prepare-leaf.yaml create mode 100644 playbooks/skmo/trust-leaf-ca.yaml diff --git a/playbooks/skmo/ensure-central-ca-bundle.yaml b/playbooks/skmo/ensure-central-ca-bundle.yaml new file mode 100644 index 0000000000..a37bccb458 --- /dev/null +++ b/playbooks/skmo/ensure-central-ca-bundle.yaml @@ -0,0 +1,28 @@ +--- +- name: Ensure central control plane uses custom CA bundle + hosts: localhost + gather_facts: false + vars: + central_namespace: openstack + controlplane_name: controlplane + ca_bundle_secret_name: custom-ca-certs + tasks: + - name: Check current caBundleSecretName + ansible.builtin.shell: | + set -euo pipefail + oc -n {{ central_namespace }} get osctlplane {{ controlplane_name }} \ + -o jsonpath='{.spec.tls.caBundleSecretName}' + args: + executable: /bin/bash + register: ca_bundle_name + changed_when: false + failed_when: false + + - name: Patch control plane to use custom CA bundle when unset + ansible.builtin.shell: | + set -euo pipefail + oc -n {{ central_namespace }} patch osctlplane {{ controlplane_name }} \ + --type json -p '[{"op":"add","path":"/spec/tls","value":{}},{"op":"add","path":"/spec/tls/caBundleSecretName","value":"{{ ca_bundle_secret_name }}"}]' + args: + executable: /bin/bash + when: ca_bundle_name.stdout | trim == "" diff --git a/playbooks/skmo/prepare-leaf.yaml b/playbooks/skmo/prepare-leaf.yaml new file mode 100644 index 0000000000..ee4125c0a0 --- /dev/null +++ b/playbooks/skmo/prepare-leaf.yaml @@ -0,0 +1,131 @@ +--- +- name: Prepare SKMO leaf prerequisites in regionZero + hosts: localhost + gather_facts: false + vars: + skmo_values_file: "{{ cifmw_architecture_repo }}/examples/va/multi-namespace-skmo/control-plane2/skmo-values.yaml" + osp_secrets_env_file: "{{ playbook_dir }}/../../lib/control-plane/base/osp-secrets.env" + central_namespace: openstack + leaf_namespace: openstack2 + leaf_secret_name: osp-secret + central_rootca_secret: rootca-public + tasks: + - name: Load SKMO values + ansible.builtin.set_fact: + skmo_values: "{{ lookup('file', skmo_values_file) | from_yaml }}" + + - name: Set SKMO leaf facts + ansible.builtin.set_fact: + leaf_region: "{{ skmo_values.data.leafRegion }}" + leaf_admin_user: "{{ skmo_values.data.leafAdminUser }}" + leaf_admin_project: "{{ skmo_values.data.leafAdminProject }}" + leaf_admin_password_key: "{{ skmo_values.data.leafAdminPasswordKey }}" + keystone_internal_url: "{{ skmo_values.data.keystoneInternalURL }}" + keystone_public_url: "{{ skmo_values.data.keystonePublicURL }}" + ca_bundle_secret_name: "{{ skmo_values.data.leafCaBundleSecretName }}" + + - name: Ensure leaf osp-secret exists (pre-create from env file) + ansible.builtin.shell: | + set -euo pipefail + if ! oc -n {{ leaf_namespace }} get secret {{ leaf_secret_name }} >/dev/null 2>&1; then + oc -n {{ leaf_namespace }} create secret generic {{ leaf_secret_name }} \ + --from-env-file="{{ osp_secrets_env_file }}" \ + --dry-run=client -o yaml | oc apply -f - + fi + args: + executable: /bin/bash + + - name: Read leaf admin password from leaf secret + ansible.builtin.shell: | + set -euo pipefail + oc -n {{ leaf_namespace }} get secret {{ leaf_secret_name }} \ + -o jsonpath='{.data.{{ leaf_admin_password_key }}}' | base64 -d + args: + executable: /bin/bash + register: leaf_admin_password + changed_when: false + + - name: Ensure leaf region exists in central Keystone + ansible.builtin.shell: | + set -euo pipefail + oc -n {{ central_namespace }} rsh openstackclient \ + openstack region show {{ leaf_region }} >/dev/null 2>&1 || \ + oc -n {{ central_namespace }} rsh openstackclient \ + openstack region create {{ leaf_region }} + args: + executable: /bin/bash + + - name: Ensure keystone catalog endpoints exist for leaf region + ansible.builtin.shell: | + set -euo pipefail + if ! oc -n {{ central_namespace }} rsh openstackclient \ + openstack endpoint list --service keystone --interface public --region {{ leaf_region }} \ + -f value -c ID | head -1 | grep -q .; then + oc -n {{ central_namespace }} rsh openstackclient \ + openstack endpoint create --region {{ leaf_region }} identity public "{{ keystone_public_url }}" + fi + if ! oc -n {{ central_namespace }} rsh openstackclient \ + openstack endpoint list --service keystone --interface internal --region {{ leaf_region }} \ + -f value -c ID | head -1 | grep -q .; then + oc -n {{ central_namespace }} rsh openstackclient \ + openstack endpoint create --region {{ leaf_region }} identity internal "{{ keystone_internal_url }}" + fi + args: + executable: /bin/bash + + - name: Ensure leaf admin project exists in central Keystone + ansible.builtin.shell: | + set -euo pipefail + oc -n {{ central_namespace }} rsh openstackclient \ + openstack project show {{ leaf_admin_project }} >/dev/null 2>&1 || \ + oc -n {{ central_namespace }} rsh openstackclient \ + openstack project create {{ leaf_admin_project }} + args: + executable: /bin/bash + + - name: Ensure leaf admin user exists and has admin role + ansible.builtin.shell: | + set -euo pipefail + if ! oc -n {{ central_namespace }} rsh openstackclient \ + openstack user show {{ leaf_admin_user }} >/dev/null 2>&1; then + oc -n {{ central_namespace }} rsh openstackclient \ + openstack user create --domain Default --password "{{ leaf_admin_password.stdout | trim }}" {{ leaf_admin_user }} + fi + oc -n {{ central_namespace }} rsh openstackclient \ + openstack role add --project {{ leaf_admin_project }} --user {{ leaf_admin_user }} admin + args: + executable: /bin/bash + + - name: Create or append leaf CA bundle secret + ansible.builtin.shell: | + set -euo pipefail + tmpdir="$(mktemp -d)" + newkey="skmo-central-rootca.crt" + export TMPDIR="${tmpdir}" + + if oc -n {{ leaf_namespace }} get secret {{ ca_bundle_secret_name }} \ + >/dev/null 2>&1; then + oc -n {{ leaf_namespace }} get secret {{ ca_bundle_secret_name }} \ + -o json | python3 -c ' + import base64, json, os, sys + tmpdir = os.environ.get("TMPDIR") + data = json.load(sys.stdin).get("data", {}) + for key, value in data.items(): + path = os.path.join(tmpdir, key) + with open(path, "wb") as f: + f.write(base64.b64decode(value)) + ' + fi + + oc -n {{ central_namespace }} get secret {{ central_rootca_secret }} \ + -o jsonpath='{.data.tls\.crt}' | base64 -d \ + > "${tmpdir}/${newkey}" + + oc -n {{ leaf_namespace }} create secret generic \ + {{ ca_bundle_secret_name }} \ + --from-file="${tmpdir}" \ + --dry-run=client -o yaml | oc apply -f - + + rm -rf "${tmpdir}" + args: + executable: /bin/bash diff --git a/playbooks/skmo/trust-leaf-ca.yaml b/playbooks/skmo/trust-leaf-ca.yaml new file mode 100644 index 0000000000..5fd86d48e3 --- /dev/null +++ b/playbooks/skmo/trust-leaf-ca.yaml @@ -0,0 +1,51 @@ +--- +- name: Trust SKMO leaf CA in central region + hosts: localhost + gather_facts: false + vars: + skmo_values_file: "{{ cifmw_architecture_repo }}/examples/va/multi-namespace-skmo/control-plane2/skmo-values.yaml" + central_namespace: openstack + leaf_namespace: openstack2 + leaf_rootca_secret: rootca-public + tasks: + - name: Load SKMO values + ansible.builtin.set_fact: + skmo_values: "{{ lookup('file', skmo_values_file) | from_yaml }}" + + - name: Set central CA bundle secret name + ansible.builtin.set_fact: + central_ca_bundle_secret_name: "{{ skmo_values.data.centralCaBundleSecretName }}" + + - name: Create or append central CA bundle secret + ansible.builtin.shell: | + set -euo pipefail + tmpdir="$(mktemp -d)" + newkey="skmo-leaf-rootca.crt" + export TMPDIR="${tmpdir}" + + if oc -n {{ central_namespace }} get secret \ + {{ central_ca_bundle_secret_name }} >/dev/null 2>&1; then + oc -n {{ central_namespace }} get secret \ + {{ central_ca_bundle_secret_name }} -o json | python3 -c ' + import base64, json, os, sys + tmpdir = os.environ.get("TMPDIR") + data = json.load(sys.stdin).get("data", {}) + for key, value in data.items(): + path = os.path.join(tmpdir, key) + with open(path, "wb") as f: + f.write(base64.b64decode(value)) + ' + fi + + oc -n {{ leaf_namespace }} get secret {{ leaf_rootca_secret }} \ + -o jsonpath='{.data.tls\.crt}' | base64 -d \ + > "${tmpdir}/${newkey}" + + oc -n {{ central_namespace }} create secret generic \ + {{ central_ca_bundle_secret_name }} \ + --from-file="${tmpdir}" \ + --dry-run=client -o yaml | oc apply -f - + + rm -rf "${tmpdir}" + args: + executable: /bin/bash From defd4abc0fb7c3db508ba1124f8ec01cc576e50e Mon Sep 17 00:00:00 2001 From: Ade Lee Date: Mon, 16 Feb 2026 16:48:25 -0500 Subject: [PATCH 2/2] Add the va-multi-skmo scenario We need to separate the common bits out and use some inheritance to avoid duplication, but this is proving problematic right now. Just duplicate stuff now to make sure we have all the other bits in place. --- playbooks/skmo/prepare-leaf.yaml | 2 +- .../templates/multi-namespace-skmo | 1 + scenarios/reproducers/va-multi-skmo.yml | 406 ++++++++++++++++++ 3 files changed, 408 insertions(+), 1 deletion(-) create mode 120000 roles/ci_gen_kustomize_values/templates/multi-namespace-skmo create mode 100644 scenarios/reproducers/va-multi-skmo.yml diff --git a/playbooks/skmo/prepare-leaf.yaml b/playbooks/skmo/prepare-leaf.yaml index ee4125c0a0..a2219d4467 100644 --- a/playbooks/skmo/prepare-leaf.yaml +++ b/playbooks/skmo/prepare-leaf.yaml @@ -4,7 +4,7 @@ gather_facts: false vars: skmo_values_file: "{{ cifmw_architecture_repo }}/examples/va/multi-namespace-skmo/control-plane2/skmo-values.yaml" - osp_secrets_env_file: "{{ playbook_dir }}/../../lib/control-plane/base/osp-secrets.env" + osp_secrets_env_file: "{{ cifmw_architecture_repo }}/lib/control-plane/base/osp-secrets.env" central_namespace: openstack leaf_namespace: openstack2 leaf_secret_name: osp-secret diff --git a/roles/ci_gen_kustomize_values/templates/multi-namespace-skmo b/roles/ci_gen_kustomize_values/templates/multi-namespace-skmo new file mode 120000 index 0000000000..67c8e7f36c --- /dev/null +++ b/roles/ci_gen_kustomize_values/templates/multi-namespace-skmo @@ -0,0 +1 @@ +multi-namespace \ No newline at end of file diff --git a/scenarios/reproducers/va-multi-skmo.yml b/scenarios/reproducers/va-multi-skmo.yml new file mode 100644 index 0000000000..86ad2007b6 --- /dev/null +++ b/scenarios/reproducers/va-multi-skmo.yml @@ -0,0 +1,406 @@ +--- +cifmw_architecture_scenario: multi-namespace-skmo + +# HERE if you want to override kustomization, you can uncomment this parameter +# and push the data structure you want to apply. +# cifmw_architecture_user_kustomize: +# stage_0: +# 'network-values': +# data: +# starwars: Obiwan + +# HERE, if you want to stop the deployment loop at any stage, you can uncomment +# the following parameter and update the value to match the stage you want to +# reach. Known stages are: +# pre_kustomize_stage_INDEX +# pre_apply_stage_INDEX +# post_apply_stage_INDEX +# +# cifmw_deploy_architecture_stopper: + +cifmw_arch_automation_file: multi-namespace-skmo.yaml +cifmw_os_must_gather_additional_namespaces: kuttl,openshift-storage,sushy-emulator,openstack2 +cifmw_reproducer_validate_network_host: "192.168.122.1" +cifmw_libvirt_manager_default_gw_nets: + - ocpbm + - osptrunk2 +cifmw_networking_mapper_interfaces_info_translations: + osp_trunk: + - controlplane + - ctlplane + osptrunk2: + - ctlplane2 + +# Override the default 3-compute VA setting, since 3 computes in both namespaces is too expensive +cifmw_libvirt_manager_compute_amount: 2 + +cifmw_libvirt_manager_configuration: + networks: + osp_trunk: | + + osp_trunk + + + + + + + osptrunk2: | + + osptrunk2 + + + + + + + ocpbm: | + + ocpbm + + + + + + + ocppr: | + + ocppr + + + + vms: + ocp: + amount: 3 + admin_user: core + image_local_dir: "{{ cifmw_basedir }}/images/" + disk_file_name: "ocp_master" + disksize: "100" + extra_disks_num: 3 + extra_disks_size: "50G" + cpus: 16 + memory: 32 + root_part_id: 4 + uefi: true + nets: + - ocppr + - ocpbm + - osp_trunk # ctlplane and isolated networks for openstack namespace cloud + - osptrunk2 # ctlplane and isolated networks for openstack2 namespace cloud + - osp_trunk # OVN datacentre for openstack namespace cloud + - osptrunk2 # OVN datacentre for openstack2 namespace cloud + compute: + uefi: "{{ cifmw_use_uefi }}" + root_part_id: "{{ cifmw_root_partition_id }}" + amount: "{{ [cifmw_libvirt_manager_compute_amount|int, 2] | max }}" + image_url: "{{ cifmw_discovered_image_url }}" + sha256_image_name: "{{ cifmw_discovered_hash }}" + image_local_dir: "{{ cifmw_basedir }}/images/" + disk_file_name: "base-os.qcow2" + disksize: "{{ [cifmw_libvirt_manager_compute_disksize|int, 50] | max }}" + memory: "{{ [cifmw_libvirt_manager_compute_memory|int, 8] | max }}" + cpus: "{{ [cifmw_libvirt_manager_compute_cpus|int, 4] | max }}" + nets: + - ocpbm + - osp_trunk + compute2: + uefi: "{{ cifmw_use_uefi }}" + root_part_id: "{{ cifmw_root_partition_id }}" + amount: "{{ [cifmw_libvirt_manager_compute_amount|int, 2] | max }}" + image_url: "{{ cifmw_discovered_image_url }}" + sha256_image_name: "{{ cifmw_discovered_hash }}" + image_local_dir: "{{ cifmw_basedir }}/images/" + disk_file_name: "base-os.qcow2" + disksize: "{{ [cifmw_libvirt_manager_compute_disksize|int, 50] | max }}" + memory: "{{ [cifmw_libvirt_manager_compute_memory|int, 8] | max }}" + cpus: "{{ [cifmw_libvirt_manager_compute_cpus|int, 4] | max }}" + nets: + - ocpbm + - osptrunk2 + controller: + uefi: "{{ cifmw_use_uefi }}" + root_part_id: "{{ cifmw_root_partition_id }}" + image_url: "{{ cifmw_discovered_image_url }}" + sha256_image_name: "{{ cifmw_discovered_hash }}" + image_local_dir: "{{ cifmw_basedir }}/images/" + disk_file_name: "base-os.qcow2" + disksize: 50 + memory: 8 + cpus: 4 + nets: + - ocpbm + - osp_trunk + - osptrunk2 + +## devscript support for OCP deploy +cifmw_devscripts_config_overrides: + fips_mode: "{{ cifmw_fips_enabled | default(false) | bool }}" + +# Set Logical Volume Manager Storage by default for local storage +cifmw_use_lvms: true +cifmw_lvms_disk_list: + - /dev/vda + - /dev/vdb + - /dev/vdc + +cifmw_networking_definition: + networks: + ctlplane: + network: "192.168.122.0/24" + gateway: "192.168.122.1" + dns: + - "192.168.122.1" + mtu: 1500 + tools: + multus: + ranges: + - start: 30 + end: 70 + netconfig: + ranges: + - start: 100 + end: 120 + - start: 150 + end: 170 + metallb: + ranges: + - start: 80 + end: 90 + ctlplane2: + network: "192.168.133.0/24" + gateway: "192.168.133.1" + dns: + - "192.168.133.1" + mtu: 1500 + tools: + multus: + ranges: + - start: 30 + end: 70 + netconfig: + ranges: + - start: 100 + end: 120 + - start: 150 + end: 170 + metallb: + ranges: + - start: 80 + end: 90 + internalapi: + network: "172.17.0.0/24" + vlan: 20 + mtu: 1496 + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + internalapi2: + network: "172.17.10.0/24" + vlan: 30 + mtu: 1496 + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + storage: + network: "172.18.0.0/24" + vlan: 21 + mtu: 1496 + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + storage2: + network: "172.18.10.0/24" + vlan: 31 + mtu: 1496 + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + tenant: + network: "172.19.0.0/24" + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + vlan: 22 + mtu: 1496 + tenant2: + network: "172.19.10.0/24" + tools: + metallb: + ranges: + - start: 80 + end: 90 + netconfig: + ranges: + - start: 100 + end: 250 + multus: + ranges: + - start: 30 + end: 70 + vlan: 32 + mtu: 1496 + external: + network: "10.0.0.0/24" + tools: + netconfig: + ranges: + - start: 100 + end: 250 + vlan: 22 + mtu: 1500 + external2: + network: "10.10.0.0/24" + tools: + netconfig: + ranges: + - start: 100 + end: 250 + vlan: 32 + mtu: 1500 + + group-templates: + ocps: + network-template: + range: + start: 10 + length: 10 + networks: &ocps_nets + ctlplane: {} + internalapi: + trunk-parent: ctlplane + tenant: + trunk-parent: ctlplane + storage: + trunk-parent: ctlplane + ctlplane2: {} + internalapi2: + trunk-parent: ctlplane2 + tenant2: + trunk-parent: ctlplane2 + storage2: + trunk-parent: ctlplane2 + ocp_workers: + network-template: + range: + start: 20 + length: 10 + networks: *ocps_nets + computes: + network-template: + range: + start: 100 + length: 21 + networks: + ctlplane: {} + internalapi: + trunk-parent: ctlplane + tenant: + trunk-parent: ctlplane + storage: + trunk-parent: ctlplane + compute2s: + network-template: + range: + start: 200 + length: 21 + networks: + ctlplane2: {} + internalapi2: + trunk-parent: ctlplane2 + tenant2: + trunk-parent: ctlplane2 + storage2: + trunk-parent: ctlplane2 + instances: + controller-0: + networks: + ctlplane: + ip: "192.168.122.9" + ctlplane2: + ip: "192.168.133.9" + +# Hooks +post_deploy: + - name: Discover hypervisors for openstack2 namespace + type: playbook + source: "{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/ci-framework/hooks/playbooks/nova_manage_discover_hosts.yml" + extra_vars: + namespace: openstack2 + _cell_conductors: nova-cell0-conductor-0 + +pre_admin_setup: + - name: Prepare OSP networks in openstack2 namespace + type: playbook + source: "{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/ci-framework/playbooks/multi-namespace/ns2_osp_networks.yaml" + extra_vars: + cifmw_os_net_setup_namespace: openstack2 + cifmw_os_net_setup_public_cidr: "192.168.133.0/24" + cifmw_os_net_setup_public_start: "192.168.133.230" + cifmw_os_net_setup_public_end: "192.168.133.250" + cifmw_os_net_setup_public_gateway: "192.168.133.1" + +post_tests: + - name: Run tempest against openstack2 namespace + type: playbook + source: "{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/ci-framework/playbooks/multi-namespace/ns2_validation.yaml" + extra_vars: + cifmw_test_operator_tempest_name: tempest-tests2 + cifmw_test_operator_namespace: openstack2