diff --git a/ansible/inventory/group_vars/all/ipa b/ansible/inventory/group_vars/all/ipa index 9a5042f9f..bde7da682 100644 --- a/ansible/inventory/group_vars/all/ipa +++ b/ansible/inventory/group_vars/all/ipa @@ -23,12 +23,14 @@ ipa_builder_source_version: "{{ openstack_branch }}" ipa_build_dib_host_packages_extra: [] # List of default Diskimage Builder (DIB) elements to use when building IPA -# images. Default is ["centos", "enable-serial-console", +# images. Default is ["centos", "dynamic-login", "enable-serial-console", # "ironic-python-agent-ramdisk"] when os_distribution is "rocky", and -# ["ubuntu", "enable-serial-console", "ironic-python-agent-ramdisk"] otherwise. +# ["ubuntu", "dynamic-login", "enable-serial-console", +# "ironic-python-agent-ramdisk"] otherwise. ipa_build_dib_elements_default: # TODO(mattcrees): Use {{ os_distribution }} here when Rocky IPA builds work. - "{{ 'centos' if os_distribution == 'rocky' else os_distribution }}" + - dynamic-login - enable-serial-console - ironic-python-agent-ramdisk @@ -50,6 +52,7 @@ ipa_build_dib_env_default: DIB_RELEASE: "{{ '9-stream' if os_distribution == 'rocky' else os_release }}" DIB_REPOLOCATION_ironic_python_agent: "{{ ipa_build_source_url }}" DIB_REPOREF_ironic_python_agent: "{{ ipa_build_source_version }}" + DIB_REPOREF_requirements: "{{ ipa_build_source_version }}" # Dictionary of additional environment variables to provide to Diskimage # Builder (DIB) during IPA image build. diff --git a/ansible/inventory/group_vars/all/kolla b/ansible/inventory/group_vars/all/kolla index c312cbca5..4955e510c 100644 --- a/ansible/inventory/group_vars/all/kolla +++ b/ansible/inventory/group_vars/all/kolla @@ -233,6 +233,8 @@ overcloud_container_image_regex_map: enabled: "{{ kolla_enable_placement | bool }}" - regex: ^prometheus enabled: "{{ kolla_enable_prometheus | bool }}" + - regex: ^proxysql + enabled: "{{ kolla_enable_proxysql | bool }}" - regex: ^qdrouterd enabled: "{{ kolla_enable_qdrouterd | bool }}" - regex: ^rabbitmq @@ -400,6 +402,7 @@ kolla_overcloud_inventory_pass_through_host_vars_default: - "kolla_external_vip_interface" - "kolla_neutron_external_interfaces" - "kolla_neutron_bridge_names" + - "kolla_neutron_physical_networks" # List of names of additional host variables to pass through from kayobe hosts # to kolla-ansible hosts, if set. See also @@ -430,6 +433,7 @@ kolla_overcloud_inventory_pass_through_host_vars_map_default: kolla_tunnel_interface: "tunnel_interface" kolla_neutron_external_interfaces: "neutron_external_interface" kolla_neutron_bridge_names: "neutron_bridge_name" + kolla_neutron_physical_networks: "neutron_physical_networks" # Dict mapping names of additional variables in # kolla_overcloud_inventory_pass_through_host_vars to the variable to use in @@ -508,6 +512,7 @@ kolla_enable_mariadb: "yes" kolla_enable_memcached: "yes" kolla_enable_neutron: "{{ kolla_enable_openstack_core | bool }}" kolla_enable_nova: "{{ kolla_enable_openstack_core | bool }}" +kolla_enable_proxysql: "no" kolla_enable_rabbitmq: "yes" # OpenStack services can be enabled or disabled with these options diff --git a/ansible/inventory/group_vars/all/proxy b/ansible/inventory/group_vars/all/proxy index eb791bbef..2941e3398 100644 --- a/ansible/inventory/group_vars/all/proxy +++ b/ansible/inventory/group_vars/all/proxy @@ -19,3 +19,4 @@ no_proxy: - "127.0.0.1" - "localhost" - "{{ ('http://' ~ docker_registry) | urlsplit('hostname') if docker_registry else '' }}" + - "{{ kolla_internal_vip_address }}" diff --git a/ansible/inventory/group_vars/all/time b/ansible/inventory/group_vars/all/time index 6a60b7c15..4f84ce60d 100644 --- a/ansible/inventory/group_vars/all/time +++ b/ansible/inventory/group_vars/all/time @@ -38,3 +38,17 @@ chrony_ntp_servers: # Synchronise hardware clock with system time. Default is true. chrony_rtcsync_enabled: true + +# Force synchronisation from NTP sources. This methods may jump the clock by +# large values which can cause issues with some software. Disabled by default. +ntp_force_sync: false + +# Maximum number of tries used by the `chronyc waitsync` command. Only used +# when ntp_force_sync is true. Default is 60 which waits for a maximum of 10 +# minutes (60 times 10 seconds). +chrony_waitsync_max_tries: 60 + +# Maximum correction used by the `chronyc waitsync` command. Only used when +# ntp_force_sync is true. Default is 0.01 which waits for the remaining +# correction to be less than 10 milliseconds. +chrony_waitsync_max_correction: 0.01 diff --git a/ansible/kolla-ansible.yml b/ansible/kolla-ansible.yml index 6e3d2ce27..7f426d713 100644 --- a/ansible/kolla-ansible.yml +++ b/ansible/kolla-ansible.yml @@ -169,11 +169,11 @@ - var_name: "kolla_network_interface" description: "Default network" network: "{{ internal_net_name }}" - required: True + required: False - var_name: "kolla_api_interface" description: "API network" network: "{{ internal_net_name }}" - required: True + required: False - var_name: "kolla_storage_interface" description: "Storage network" network: "{{ storage_net_name }}" diff --git a/ansible/logging.yml b/ansible/logging.yml index 326fe66d8..801ee3a72 100644 --- a/ansible/logging.yml +++ b/ansible/logging.yml @@ -18,8 +18,8 @@ file: path: /var/log/journal owner: "root" - group: "root" - mode: 0755 + group: "systemd-journal" + mode: 02755 state: directory notify: Flush journal to disk when: journald_storage == "persistent" diff --git a/ansible/overcloud-deprovision.yml b/ansible/overcloud-deprovision.yml index a8bcf8b46..642042404 100644 --- a/ansible/overcloud-deprovision.yml +++ b/ansible/overcloud-deprovision.yml @@ -37,7 +37,7 @@ - name: confirm_deprovision prompt: | The following hosts will be deprovisioned: - {{ play_hosts | join(', ') }} + {{ play_hosts | intersect(groups['overcloud'] | default([])) | join(', ') }} If you want to proceed type: yes default: "no" private: false diff --git a/ansible/roles/image-download/defaults/main.yml b/ansible/roles/image-download/defaults/main.yml index 52bec6ddd..00b079f37 100644 --- a/ansible/roles/image-download/defaults/main.yml +++ b/ansible/roles/image-download/defaults/main.yml @@ -21,3 +21,23 @@ image_download_dest: # Host from which to fetch the image. # Only used when image_download_path is set. image_download_host: "{{ inventory_hostname }}" + +# Username for Digest, Basic or WSSE authentication. Default is unset, in which +# case the parameter is omitted. +image_download_url_username: + +# Password for Digest, Basic or WSSE authentication. Default is unset, in which +# case the parameter is omitted. +image_download_url_password: + +# Force sending the Basic authentication header upon initial request. Useful if +# the remote endpoint does not respond with HTTP 401 to the initial +# unauthenticated request. Must be a boolean. Default is unset, in which case +# the parameter is omitted. +image_download_force_basic_auth: + +# List of header names that will not be sent on subsequent redirected requests. +# Set to ['Authorization'] if being redirected from an authenticated endpoint +# to an unauthenticated endpoint. Default is unset, in which case the parameter +# is omitted. +image_download_unredirected_headers: diff --git a/ansible/roles/image-download/tasks/main.yml b/ansible/roles/image-download/tasks/main.yml index 3a4dc4ba0..c1d799fdd 100644 --- a/ansible/roles/image-download/tasks/main.yml +++ b/ansible/roles/image-download/tasks/main.yml @@ -15,6 +15,10 @@ uri: url: "{{ image_download_checksum_url }}" return_content: true + url_username: "{{ image_download_url_username or omit }}" + url_password: "{{ image_download_url_password or omit }}" + force_basic_auth: "{{ image_download_force_basic_auth or omit }}" + unredirected_headers: "{{ image_download_unredirected_headers or omit }}" register: expected_checksum until: expected_checksum is successful retries: 3 @@ -35,6 +39,10 @@ # Always download the image if we have no checksum to compare with. force: "{{ expected_checksum is skipped }}" backup: true + url_username: "{{ image_download_url_username or omit }}" + url_password: "{{ image_download_url_password or omit }}" + force_basic_auth: "{{ image_download_force_basic_auth or omit }}" + unredirected_headers: "{{ image_download_unredirected_headers or omit }}" register: result until: result is successful retries: 3 diff --git a/ansible/roles/infra-vms/tasks/deploy.yml b/ansible/roles/infra-vms/tasks/deploy.yml index 2000bfdc6..ae6663359 100644 --- a/ansible/roles/infra-vms/tasks/deploy.yml +++ b/ansible/roles/infra-vms/tasks/deploy.yml @@ -4,7 +4,7 @@ include_role: name: jriguera.configdrive vars: - configdrive_os_family: "{{ vm_hostvars.infra_vm_os_family }}" + configdrive_os_family: "{{ vm_os_family }}" configdrive_uuid: "{{ vm_name | to_uuid }}" # Must set configdrive_instance_dir when using a loop # https://github.com/jriguera/ansible-role-configdrive/blob/8438592c84585c86e62ae07e526d3da53629b377/tasks/main.yml#L17 @@ -16,13 +16,10 @@ configdrive_volume_path: "{{ image_cache_path }}" configdrive_config_dir_delete: False configdrive_resolv: - domain: "{{ vm_hostvars.resolv_domain | default }}" - search: "{{ vm_hostvars.resolv_search | default }}" - dns: "{{ vm_hostvars.resolv_nameservers | default([]) }}" - configdrive_network_device_list: > - {{ vm_hostvars.network_interfaces | - map('net_configdrive_network_device', vm_hostvars.inventory_hostname) | - list }} + domain: "{{ vm_resolv_domain | default }}" + search: "{{ vm_resolv_search | default }}" + dns: "{{ vm_resolv_nameservers | default([]) }}" + configdrive_network_device_list: "{{ vm_network_device_list }}" - name: "[{{ vm_name }}] Set a fact containing the configdrive image path" set_fact: @@ -56,7 +53,7 @@ - name: "[{{ vm_name }}] Stat image files" stat: path: "{{ image_cache_path }}/{{ item.image | basename }}" - with_items: "{{ vm_hostvars.infra_vm_volumes | selectattr('image', 'defined') }}" + with_items: "{{ vm_volumes | selectattr('image', 'defined') }}" register: image_stat_result - name: "[{{ vm_name }}] Fix image ownership" @@ -75,7 +72,7 @@ vm_configdrive_device: cdrom vm_configdrive_volume: name: "{{ vm_name }}-configdrive" - pool: "{{ vm_hostvars.infra_vm_pool }}" + pool: "{{ vm_pool }}" # Round size up to next multiple of 4096. capacity: "{{ (stat_result.stat.size + 4095) // 4096 * 4096 }}" device: "{{ vm_configdrive_device }}" @@ -85,10 +82,10 @@ libvirt_vm_image_cache_path: "{{ image_cache_path }}" libvirt_vms: - name: "{{ vm_name }}" - boot_firmware: "{{ vm_hostvars.infra_vm_boot_firmware | default }}" - machine: "{{ vm_hostvars.infra_vm_machine | default }}" - memory_mb: "{{ vm_hostvars.infra_vm_memory_mb }}" - vcpus: "{{ vm_hostvars.infra_vm_vcpus }}" - volumes: "{{ vm_hostvars.infra_vm_volumes + [vm_configdrive_volume] }}" - interfaces: "{{ vm_hostvars.infra_vm_interfaces }}" + boot_firmware: "{{ vm_boot_firmware | default }}" + machine: "{{ vm_machine | default }}" + memory_mb: "{{ vm_memory_mb }}" + vcpus: "{{ vm_vcpus }}" + volumes: "{{ vm_volumes + [vm_configdrive_volume] }}" + interfaces: "{{ vm_interfaces }}" console_log_enabled: true diff --git a/ansible/roles/infra-vms/tasks/destroy.yml b/ansible/roles/infra-vms/tasks/destroy.yml index c45b996c7..b1a9965f5 100644 --- a/ansible/roles/infra-vms/tasks/destroy.yml +++ b/ansible/roles/infra-vms/tasks/destroy.yml @@ -6,12 +6,12 @@ vars: infra_vm_configdrive_volume: name: "{{ vm_name }}-configdrive" - pool: "{{ hostvars[vm_hostvars.infra_vm_hypervisor].infra_vm_pool }}" + pool: "{{ vm_pool }}" libvirt_vms: - name: "{{ vm_name }}" - boot_firmware: "{{ vm_hostvars.infra_vm_boot_firmware | default }}" - memory_mb: "{{ vm_hostvars.infra_vm_memory_mb }}" - vcpus: "{{ vm_hostvars.infra_vm_vcpus }}" - volumes: "{{ vm_hostvars.infra_vm_volumes + [infra_vm_configdrive_volume] }}" + boot_firmware: "{{ vm_boot_firmware | default }}" + memory_mb: "{{ vm_memory_mb }}" + vcpus: "{{ vm_vcpus }}" + volumes: "{{ vm_volumes + [infra_vm_configdrive_volume] }}" state: "absent" become: True diff --git a/ansible/roles/infra-vms/tasks/main.yml b/ansible/roles/infra-vms/tasks/main.yml index 9fec42e3c..a7306935e 100644 --- a/ansible/roles/infra-vms/tasks/main.yml +++ b/ansible/roles/infra-vms/tasks/main.yml @@ -10,8 +10,21 @@ - name: "{{ infra_vm_action | capitalize }} infra VMs (loop)" include_tasks: "{{ infra_vm_action }}.yml" vars: - vm_name: "{{ vm_hostvars.infra_vm_name }}" - vm_hostvars: "{{ hostvars[vm_item] }}" + vm_name: "{{ hostvars[vm_item].infra_vm_name }}" + vm_os_family: "{{ hostvars[vm_item].infra_vm_os_family }}" + vm_resolv_domain: "{{ hostvars[vm_item].resolv_domain }}" + vm_resolv_search: "{{ hostvars[vm_item].resolv_search }}" + vm_resolv_nameservers: "{{ hostvars[vm_item].resolv_nameservers }}" + vm_network_device_list: "{{ hostvars[vm_item].network_interfaces | + map('net_configdrive_network_device', hostvars[vm_item].inventory_hostname) | + list }}" + vm_volumes: "{{ hostvars[vm_item].infra_vm_volumes }}" + vm_pool: "{{ hostvars[vm_item].infra_vm_pool }}" + vm_boot_firmware: "{{ hostvars[vm_item].infra_vm_boot_firmware }}" + vm_machine: "{{ hostvars[vm_item].infra_vm_machine }}" + vm_memory_mb: "{{ hostvars[vm_item].infra_vm_memory_mb }}" + vm_vcpus: "{{ hostvars[vm_item].infra_vm_vcpus }}" + vm_interfaces: "{{ hostvars[vm_item].infra_vm_interfaces }}" loop: "{{ infra_vm_vms }}" when: (infra_vm_action == "deploy" and vm_name not in all_vms.list_vms) or infra_vm_action == "destroy" loop_control: diff --git a/ansible/roles/ipa-images/tasks/set-driver-info.yml b/ansible/roles/ipa-images/tasks/set-driver-info.yml index f6d937832..4592da109 100644 --- a/ansible/roles/ipa-images/tasks/set-driver-info.yml +++ b/ansible/roles/ipa-images/tasks/set-driver-info.yml @@ -20,12 +20,19 @@ set_fact: ipa_images_ramdisk_uuid: "{{ ipa_images_glance.results[1].images[0].id }}" +- name: Change system scope to all for Ironic operations + set_fact: + ipa_images_ironic_openstack_auth_env: "{{ ipa_images_openstack_auth_env | + combine ({ 'OS_PROJECT_NAME': omit }) | + combine ({ 'OS_PROJECT_DOMAIN_NAME': omit }) | + combine ({ 'OS_SYSTEM_SCOPE': 'all' }) }}" + - name: Get a list of ironic nodes command: | {{ ipa_images_venv }}/bin/openstack baremetal node list --fields name uuid driver_info -f json register: ipa_images_ironic_node_list changed_when: False - environment: "{{ ipa_images_openstack_auth_env }}" + environment: "{{ ipa_images_ironic_openstack_auth_env }}" - name: Make sure openstack nodes are in baremetal-compute group add_host: @@ -59,4 +66,4 @@ when: item["Driver Info"].deploy_kernel != ipa_images_kernel_uuid or item["Driver Info"].deploy_ramdisk != ipa_images_ramdisk_uuid - environment: "{{ ipa_images_openstack_auth_env }}" + environment: "{{ ipa_images_ironic_openstack_auth_env }}" diff --git a/ansible/roles/kolla-ansible-host-vars/tests/test.yml b/ansible/roles/kolla-ansible-host-vars/tests/test.yml index 5f1d77274..4ff5d56ef 100644 --- a/ansible/roles/kolla-ansible-host-vars/tests/test.yml +++ b/ansible/roles/kolla-ansible-host-vars/tests/test.yml @@ -16,6 +16,7 @@ kolla_dns_interface: "eth5" kolla_neutron_external_interfaces: "eth6,eth7" kolla_neutron_bridge_names: "br0,br1" + kolla_neutron_physical_networks: "physnet1,physnet2" kolla_provision_interface: "eth8" kolla_inspector_dnsmasq_interface: "eth9" kolla_tunnel_interface: "eth10" @@ -32,6 +33,7 @@ kolla_storage_interface: "eth3" kolla_neutron_external_interfaces: "eth4,eth5" kolla_neutron_bridge_names: "br0,br1" + kolla_neutron_physical_networks: "physnet2,physnet3" kolla_tunnel_interface: "eth6" - name: Test kolla-ansible-host-vars role extras @@ -68,6 +70,7 @@ - "kolla_external_vip_interface" - "kolla_neutron_external_interfaces" - "kolla_neutron_bridge_names" + - "kolla_neutron_physical_networks" kolla_ansible_pass_through_host_vars_map: kolla_network_interface: "network_interface" kolla_api_interface: "api_interface" @@ -81,6 +84,7 @@ kolla_tunnel_interface: "tunnel_interface" kolla_neutron_external_interfaces: "neutron_external_interface" kolla_neutron_bridge_names: "neutron_bridge_name" + kolla_neutron_physical_networks: "neutron_physical_networks" kolla_ansible_inventory_path: "{{ temp_path }}" - name: Check whether inventory host vars files exist @@ -125,6 +129,7 @@ kolla_external_vip_interface: "eth1" neutron_external_interface: "eth6,eth7" neutron_bridge_name: "br0,br1" + neutron_physical_networks: "physnet1,physnet2" test-compute: | --- ansible_host: "1.2.3.6" @@ -134,6 +139,7 @@ tunnel_interface: "eth6" neutron_external_interface: "eth4,eth5" neutron_bridge_name: "br0,br1" + neutron_physical_networks: "physnet2,physnet3" always: - name: Ensure the temporary directory is removed diff --git a/ansible/roles/kolla-ansible/templates/overcloud-services.j2 b/ansible/roles/kolla-ansible/templates/overcloud-services.j2 index aae2018ce..0014866d3 100644 --- a/ansible/roles/kolla-ansible/templates/overcloud-services.j2 +++ b/ansible/roles/kolla-ansible/templates/overcloud-services.j2 @@ -453,6 +453,9 @@ ovn-database [ovn-sb-db:children] ovn-database +[ovn-sb-db-relay:children] +ovn-database + [venus-api:children] venus diff --git a/ansible/roles/kolla-ansible/vars/main.yml b/ansible/roles/kolla-ansible/vars/main.yml index c6d3a9d10..20fcb00c3 100644 --- a/ansible/roles/kolla-ansible/vars/main.yml +++ b/ansible/roles/kolla-ansible/vars/main.yml @@ -200,6 +200,7 @@ kolla_feature_flags: - openvswitch - osprofiler - ovn + - ovn_sb_db_relay - ovs_dpdk - placement - prometheus diff --git a/ansible/roles/ntp/defaults/main.yml b/ansible/roles/ntp/defaults/main.yml index dea948074..aa7558b19 100644 --- a/ansible/roles/ntp/defaults/main.yml +++ b/ansible/roles/ntp/defaults/main.yml @@ -1,5 +1,8 @@ --- -ntp_actions: ["validate", "prepare", "deploy"] +ntp_actions: ["validate", "prepare", "deploy", "sync"] + +# Do not jump clock by default +ntp_force_sync: False ntp_service_disable_list: - ntp.service diff --git a/ansible/roles/ntp/tasks/main.yml b/ansible/roles/ntp/tasks/main.yml index efb655192..731ea3730 100644 --- a/ansible/roles/ntp/tasks/main.yml +++ b/ansible/roles/ntp/tasks/main.yml @@ -10,3 +10,7 @@ - name: Deploy service include_tasks: deploy.yml when: '"deploy" in ntp_actions' + +- name: Synchronise time + include_tasks: sync.yml + when: '"sync" in ntp_actions' diff --git a/ansible/roles/ntp/tasks/sync.yml b/ansible/roles/ntp/tasks/sync.yml new file mode 100644 index 000000000..f969936c3 --- /dev/null +++ b/ansible/roles/ntp/tasks/sync.yml @@ -0,0 +1,21 @@ +--- +# NOTE(priteau): We need to run handlers to restart chronyd before forcing +# clock synchronisation, or the system clock could appear as not synchronised +# during CI jobs because the tests run too quickly after the chronyd restart. +- meta: flush_handlers + +- name: Force time synchronisation + become: True + block: + - name: Configure burst + command: chronyc 'burst 4/4' + + - name: Step the clock + command: chronyc makestep + + - name: Wait before hardware clock synchronisation + command: "chronyc waitsync {{ chrony_waitsync_max_tries }} {{ chrony_waitsync_max_correction }}" + + - name: Force hardware clock synchronisation + command: hwclock --systohc + when: ntp_force_sync | bool diff --git a/dev/functions b/dev/functions index 8455ba2ad..ccdff4e15 100644 --- a/dev/functions +++ b/dev/functions @@ -262,7 +262,7 @@ function environment_setup { function run_kayobe { # Run a kayobe command, including extra arguments provided via # $KAYOBE_EXTRA_ARGS. - kayobe ${KAYOBE_EXTRA_ARGS} $* + kayobe ${KAYOBE_EXTRA_ARGS} "${@}" } function control_host_bootstrap { @@ -581,6 +581,25 @@ function overcloud_test_init { fi } +function overcloud_test_bounce_interface { + shift + LIMIT=$1 + INTERFACE=$2 + + environment_setup + + # Change current configuration so that a subsequent host configure will bounce the interfaces: + if is_dnf; then + # Rocky + run_kayobe overcloud host command run -b --command "rm -f /etc/NetworkManager/system-connections/$INTERFACE.nmconnection" --limit "$LIMIT" + else + # Ubuntu + run_kayobe overcloud host command run -b --command "rm -f /etc/systemd/network/50-kayobe-$INTERFACE.network" --limit "$LIMIT" + fi + # Trigger bounce + run_kayobe overcloud host configure -t network +} + function overcloud_test { set -eu diff --git a/dev/overcloud-test-bounce-interface.sh b/dev/overcloud-test-bounce-interface.sh new file mode 100755 index 000000000..c797cdf41 --- /dev/null +++ b/dev/overcloud-test-bounce-interface.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -eu +set -o pipefail + +# Bounces a given interface on hosts matching the supplied limit. +# Positional arguments: +# arg0: Ansible limit +# arg1: Interface to bounce + +PARENT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${PARENT}/functions" + + +function main { + config_init + overcloud_test_bounce_interface controllers "${@}" +} + +main "${@:1}" diff --git a/doc/source/configuration/reference/ironic-python-agent.rst b/doc/source/configuration/reference/ironic-python-agent.rst index 69d58824f..23f7f4f06 100644 --- a/doc/source/configuration/reference/ironic-python-agent.rst +++ b/doc/source/configuration/reference/ironic-python-agent.rst @@ -49,10 +49,10 @@ image build``. list. ``ipa_build_dib_elements_default`` List of default Diskimage Builder (DIB) elements to use when building IPA - images. Default is ``["centos", "enable-serial-console", + images. Default is ``["centos", "dynamic-login", "enable-serial-console", "ironic-python-agent-ramdisk"]`` when ``os_distribution`` is ``"rocky"``, and - ``["ubuntu", "enable-serial-console", "ironic-python-agent-ramdisk"]`` - otherwise. + ``["ubuntu", "dynamic-login", "enable-serial-console", + "ironic-python-agent-ramdisk"]`` otherwise. ``ipa_build_dib_elements_extra`` List of additional Diskimage Builder (DIB) elements to use when building IPA images. Default is empty. @@ -62,15 +62,14 @@ image build``. ``ipa_build_dib_elements_extra``. ``ipa_build_dib_env_default`` Dictionary of default environment variables to provide to Diskimage Builder - (DIB) during IPA image build. Default is - ``{"DIB_RELEASE": "9-stream", "DIB_REPOLOCATION_ironic_python_agent": "{{ - ipa_build_source_url }}", "DIB_REPOREF_ironic_python_agent": "{{ - ipa_build_source_version }}", "DIB_REPOREF_requirements": "{{ - openstack_branch }}"}`` if ``os_distribution`` is ``"rocky"`` else - ``{"DIB_RELEASE": "jammy", "DIB_REPOLOCATION_ironic_python_agent": "{{ - ipa_build_source_url }}", "DIB_REPOREF_ironic_python_agent": "{{ - ipa_build_source_version }}", "DIB_REPOREF_requirements": "{{ - openstack_branch }}"}``. + (DIB) during IPA image build. Default is ``{"DIB_RELEASE": "9-stream", + "DIB_REPOLOCATION_ironic_python_agent": "{{ ipa_build_source_url }}", + "DIB_REPOREF_ironic_python_agent": "{{ ipa_build_source_version }}", + "DIB_REPOREF_requirements": "{{ ipa_build_source_version }}"}`` if + ``os_distribution`` is ``"rocky"`` else ``{"DIB_RELEASE": "{{ os_release + }}", "DIB_REPOLOCATION_ironic_python_agent": "{{ ipa_build_source_url }}", + "DIB_REPOREF_ironic_python_agent": "{{ ipa_build_source_version }}", + "DIB_REPOREF_requirements": "{{ ipa_build_source_version }}"}``. ``ipa_build_dib_env_extra`` Dictionary of additional environment variables to provide to Diskimage Builder (DIB) during IPA image build. Default is empty. @@ -134,6 +133,48 @@ be useful for inspecting hardware with Mellanox InfiniBand NICs. ipa_build_dib_elements_extra: - "mellanox" +Example: Dynamically allowing access to the IPA environment +----------------------------------------------------------- + +When debugging a failed deployment, it can sometimes be necessary to allow +access to the image dynamically. + +The :diskimage-builder-doc:`dynamic-login element +` can be used to authorize SSH keys by appending +them to the kernel arguments. This element is included by default in IPA images +since the Epoxy 18.0.0 release. On previous releases, it can be added with: + +.. code-block:: yaml + :caption: ``ipa.yml`` + + ipa_build_dib_elements_extra: + - "dynamic-login" + +Bifrost can be configured to use ``dynamic-login`` with the +``kolla_bifrost_extra_kernel_options`` variable: + +.. code-block:: yaml + :caption: ``bifrost.yml`` + + kolla_bifrost_extra_kernel_options: + - sshkey="ssh-rsa BBA1..." + +The updated configuration is applied with ``kayobe seed service deploy``. + +Overcloud Ironic can be configured with the +``kolla_ironic_pxe_append_params_extra`` variable: + +.. code-block:: yaml + :caption: ``ironic.yml`` + + kolla_ironic_pxe_append_params_extra: + - sshkey="ssh-rsa BBA1..." + +The updated configuration is applied with ``kayobe overcloud service deploy``. + +Further information on troubleshooting IPA can be found +:ironic-python-agent-doc:`here `. + Example: Configuring a development user account ----------------------------------------------- @@ -160,10 +201,6 @@ and password for an account that has passwordless sudo: DIB_DEV_USER_PASSWORD: "correct horse battery staple" DIB_DEV_USER_PWDLESS_SUDO: "yes" -Alternatively, the :diskimage-builder-doc:`dynamic-login element -` can be used to authorize SSH keys by appending -them to the kernel arguments. - Further information on troubleshooting IPA can be found :ironic-python-agent-doc:`here `. diff --git a/doc/source/configuration/reference/kolla-ansible.rst b/doc/source/configuration/reference/kolla-ansible.rst index d53ecf419..92358647c 100644 --- a/doc/source/configuration/reference/kolla-ansible.rst +++ b/doc/source/configuration/reference/kolla-ansible.rst @@ -479,6 +479,7 @@ defined for a host, it is ignored. - "kolla_external_vip_interface" - "kolla_neutron_external_interfaces" - "kolla_neutron_bridge_names" + - "kolla_neutron_physical_networks" It is possible to extend this list via ``kolla_overcloud_inventory_pass_through_host_vars_extra``. @@ -504,6 +505,7 @@ defined for a host, it is ignored. kolla_tunnel_interface: "tunnel_interface" kolla_neutron_external_interfaces: "neutron_external_interface" kolla_neutron_bridge_names: "neutron_bridge_name" + kolla_neutron_physical_networks: "neutron_physical_networks" It is possible to extend this dict via ``kolla_overcloud_inventory_pass_through_host_vars_map_extra``. diff --git a/doc/source/configuration/reference/network.rst b/doc/source/configuration/reference/network.rst index f091c6f28..bcd7b56a3 100644 --- a/doc/source/configuration/reference/network.rst +++ b/doc/source/configuration/reference/network.rst @@ -79,7 +79,9 @@ supported: ``table`` is the routing table ID. ``physical_network`` Name of the physical network on which this network exists. This aligns with - the physical network concept in neutron. + the physical network concept in neutron. This may be used to customise the + Neutron physical network name used for an external network. This attribute + should be set for all external networks or none. ``libvirt_network_name`` A name to give to a Libvirt network representing this network on the seed hypervisor. @@ -335,6 +337,34 @@ To configure a network called ``example`` with a default route and a - cidr: 10.1.0.0/24 table: exampleroutetable +Configuring Custom Neutron Physical Network Names +------------------------------------------------- + +By default, Kolla Ansible uses Neutron physical network names starting with +``physnet1`` through to ``physnetN`` for each external network interface on a +host. + +Sometimes we may want to customise the physical network names used. This may be +to allow for not all hosts having access to all physical networks, or to use +more descriptive names. + +For example, in an environment with a separate physical network for Ironic +provisioning, controllers might have access to two physical networks, while +compute nodes have access to one. We could have a situation where the +controllers and computes use inconsistent physical network names. To avoid +this, we can add ``physical_network`` attributes to these networks. In the +following example, the Ironic provisioning network is ``provision_wl``, and the +external network is ``external``. + +.. code-block:: yaml + :caption: ``$KAYOBE_CONFIG_PATH/networks.yml`` + + provision_wl_physical_network: physnet1 + external_physical_network: physnet2 + +This ensures that compute nodes treat ``external`` as ``physnet2``, even though +it is the only physical network to which they are attached. + .. _configuration-network-per-host: Per-host Network Configuration diff --git a/etc/kayobe/apt.yml b/etc/kayobe/apt.yml index e4bb5b179..4f6fe4892 100644 --- a/etc/kayobe/apt.yml +++ b/etc/kayobe/apt.yml @@ -50,8 +50,6 @@ # * machine: 'machine' entry in the auth file # * login: 'login' entry in the auth file # * password: 'password' entry in the auth file -# * filename: Name of a file in which to store the auth configuration. The -# extension should be '.conf'. # * filename: Name of a file in /etc/apt/auth.conf.d in which to store # the auth configuration. The extension should be ``.conf``. # Default is an empty list. diff --git a/etc/kayobe/ipa.yml b/etc/kayobe/ipa.yml index 0138c6c44..00a9b9e97 100644 --- a/etc/kayobe/ipa.yml +++ b/etc/kayobe/ipa.yml @@ -23,9 +23,10 @@ #ipa_build_dib_host_packages_extra: # List of default Diskimage Builder (DIB) elements to use when building IPA -# images. Default is ["centos", "enable-serial-console", +# images. Default is ["centos", "dynamic-login", "enable-serial-console", # "ironic-python-agent-ramdisk"] when os_distribution is "rocky", and -# ["ubuntu", "enable-serial-console", "ironic-python-agent-ramdisk"] otherwise. +# ["ubuntu", "dynamic-login", "enable-serial-console", +# "ironic-python-agent-ramdisk"] otherwise. #ipa_build_dib_elements_default: # List of additional Diskimage Builder (DIB) elements to use when building IPA diff --git a/etc/kayobe/proxy.yml b/etc/kayobe/proxy.yml index 714b9dae5..d7df51e52 100644 --- a/etc/kayobe/proxy.yml +++ b/etc/kayobe/proxy.yml @@ -12,8 +12,9 @@ # List of domains, hostnames, IP addresses and networks for which no proxy is # used. Defaults to ["127.0.0.1", "localhost", "{{ ('http://' ~ -# docker_registry) | urlsplit('hostname') }}"] if docker_registry is set, or -# ["127.0.0.1", "localhost"] otherwise. This is configured only if either +# docker_registry) | urlsplit('hostname') }}","{{ kolla_internal_vip_address +# }}"] if docker_registry is set, or ["127.0.0.1", "localhost","{{ +# kolla_internal_vip_address }}"] otherwise. This is configured only if either # http_proxy or https_proxy is set. #no_proxy: diff --git a/etc/kayobe/time.yml b/etc/kayobe/time.yml index 8bfbd3391..652844c8d 100644 --- a/etc/kayobe/time.yml +++ b/etc/kayobe/time.yml @@ -33,6 +33,20 @@ # Synchronise hardware clock with system time. Default is true. #chrony_rtcsync_enabled: +# Force synchronisation from NTP sources. This methods may jump the clock by +# large values which can cause issues with some software. Disabled by default. +#ntp_force_sync: + +# Maximum number of tries used by the `chronyc waitsync` command. Only used +# when ntp_force_sync is true. Default is 60 which waits for a maximum of 10 +# minutes (60 times 10 seconds). +#chrony_waitsync_max_tries: + +# Maximum correction used by the `chronyc waitsync` command. Only used when +# ntp_force_sync is true. Default is 0.01 which waits for the remaining +# correction to be less than 10 milliseconds. +#chrony_waitsync_max_correction: + ############################################################################### # Dummy variable to allow Ansible to accept this file. workaround_ansible_issue_8743: yes diff --git a/kayobe/plugins/action/kolla_ansible_host_vars.py b/kayobe/plugins/action/kolla_ansible_host_vars.py index 356c7e7ff..b4ee02db8 100644 --- a/kayobe/plugins/action/kolla_ansible_host_vars.py +++ b/kayobe/plugins/action/kolla_ansible_host_vars.py @@ -63,20 +63,27 @@ def _run(self, interfaces, external_networks): except ConfigError as e: errors.append(str(e)) - # Build a list of external network interfaces. - external_interfaces = [] + # Build a dict mapping external network interfaces to a list of Kayobe + # network names that they provide connectivity for. + external_interfaces = {} for network in external_networks: try: iface = self._get_external_interface(network["network"], network["required"]) - if iface and iface not in external_interfaces: - external_interfaces.append(iface) + if iface: + iface_networks = external_interfaces.get(iface, []) + if network["network"] not in iface_networks: + iface_networks.append(network["network"]) + external_interfaces[iface] = iface_networks except ConfigError as e: errors.append(str(e)) if external_interfaces: - facts.update(self._get_external_interface_facts( - external_interfaces)) + try: + facts.update(self._get_external_interface_facts( + external_interfaces)) + except ConfigError as e: + errors.append(str(e)) result['changed'] = False if errors: @@ -137,11 +144,13 @@ def _get_external_interface(self, net_name, required): def _get_external_interface_facts(self, external_interfaces): neutron_bridge_names = [] neutron_external_interfaces = [] + neutron_physical_networks = [] + missing_physical_networks = [] bridge_suffix = self._templar.template( "{{ network_bridge_suffix_ovs }}") patch_prefix = self._templar.template("{{ network_patch_prefix }}") patch_suffix = self._templar.template("{{ network_patch_suffix_ovs }}") - for interface in external_interfaces: + for interface, iface_networks in external_interfaces.items(): is_bridge = ("{{ '%s' in (network_interfaces |" "net_select_bridges |" "map('net_interface')) }}" % interface) @@ -157,8 +166,38 @@ def _get_external_interface_facts(self, external_interfaces): else: external_interface = interface neutron_external_interfaces.append(external_interface) - return { + # One external network interface may be referenced by multiple + # external networks. Check if they have a physical_network + # attribute set, and if so, whether they are consistent. + iface_physical_networks = [] + for iface_network in iface_networks: + physical_network = self._templar.template( + "{{ '%s' | net_physical_network }}" % iface_network) + if (physical_network and + physical_network not in iface_physical_networks): + iface_physical_networks.append(physical_network) + if iface_physical_networks: + if len(iface_physical_networks) > 1: + raise ConfigError( + "Inconsistent 'physical_network' attributes for " + "external networks %s using interface %s: %s" % + (", ".join(iface_networks), interface, + ", ".join(iface_physical_networks))) + neutron_physical_networks += iface_physical_networks + else: + missing_physical_networks += iface_networks + facts = { "kolla_neutron_bridge_names": ",".join(neutron_bridge_names), "kolla_neutron_external_interfaces": ",".join( neutron_external_interfaces), } + if neutron_physical_networks: + if missing_physical_networks: + raise ConfigError( + "Some external networks have a 'physical_network' " + "attribute defined but the following do not: %s" % + ", ".join(missing_physical_networks)) + + facts["kolla_neutron_physical_networks"] = ",".join( + neutron_physical_networks) + return facts diff --git a/kayobe/plugins/filter/networks.py b/kayobe/plugins/filter/networks.py index 51b63ddba..07a353328 100644 --- a/kayobe/plugins/filter/networks.py +++ b/kayobe/plugins/filter/networks.py @@ -188,6 +188,11 @@ def net_interface(context, name, inventory_hostname=None): return net_attr(context, name, 'interface', inventory_hostname) +@jinja2.pass_context +def net_type(context, name, inventory_hostname=None): + return net_attr(context, name, 'type', inventory_hostname) + + @jinja2.pass_context def net_no_ip(context, name, inventory_hostname=None): return net_attr(context, name, 'no_ip', inventory_hostname) @@ -425,6 +430,7 @@ def net_interface_obj(context, name, inventory_hostname=None, names=None): zone = net_zone(context, name, inventory_hostname) vip_address = net_vip_address(context, name, inventory_hostname) allowed_addresses = [vip_address] if vip_address else None + _type = net_type(context, name, inventory_hostname) _validate_rules(rules) interface = { 'device': device, @@ -441,6 +447,7 @@ def net_interface_obj(context, name, inventory_hostname=None, names=None): 'zone': zone, 'allowed_addresses': allowed_addresses, 'onboot': 'yes', + 'type': _type, } interface = {k: v for k, v in interface.items() if v is not None} return interface diff --git a/kayobe/tests/unit/plugins/action/test_kolla_ansible_host_vars.py b/kayobe/tests/unit/plugins/action/test_kolla_ansible_host_vars.py index 667cd1329..3ae00e1a9 100644 --- a/kayobe/tests/unit/plugins/action/test_kolla_ansible_host_vars.py +++ b/kayobe/tests/unit/plugins/action/test_kolla_ansible_host_vars.py @@ -41,6 +41,11 @@ def _net_select_bridges(context, names): if (_net_interface(context, name) or "").startswith("br")] +@jinja2.pass_context +def _net_physical_network(context, name): + return context.get(name + '_physical_network') + + class FakeTemplar(object): def __init__(self, variables): @@ -50,6 +55,7 @@ def __init__(self, variables): self.env.filters['net_parent'] = _net_parent self.env.filters['net_vlan'] = _net_vlan self.env.filters['net_select_bridges'] = _net_select_bridges + self.env.filters['net_physical_network'] = _net_physical_network def template(self, string): template = self.env.from_string(string) @@ -248,6 +254,26 @@ def test_run_external_networks_one(self): } self.assertEqual(expected, result) + def test_run_external_networks_one_with_physnet(self): + variables = copy.deepcopy(self.variables) + variables["foo_physical_network"] = "custom1" + module = self._create_module(variables) + external_networks = [{ + "network": "foo", + "required": False, + }] + result = module._run([], external_networks) + expected = { + "changed": False, + "ansible_facts": { + "kolla_neutron_bridge_names": "eth0-ovs", + "kolla_neutron_external_interfaces": "eth0", + "kolla_neutron_physical_networks": "custom1", + }, + "_ansible_facts_cacheable": False, + } + self.assertEqual(expected, result) + def test_run_external_networks_two(self): module = self._create_module() external_networks = [{ @@ -268,6 +294,50 @@ def test_run_external_networks_two(self): } self.assertEqual(expected, result) + def test_run_external_networks_two_with_physnet(self): + variables = copy.deepcopy(self.variables) + variables["foo_physical_network"] = "custom1" + variables["bar_physical_network"] = "custom2" + module = self._create_module(variables) + external_networks = [{ + "network": "foo", + "required": False, + }, { + "network": "bar", + "required": False, + }] + result = module._run([], external_networks) + expected = { + "changed": False, + "ansible_facts": { + "kolla_neutron_bridge_names": "eth0-ovs,eth1-ovs", + "kolla_neutron_external_interfaces": "eth0,eth1", + "kolla_neutron_physical_networks": "custom1,custom2", + }, + "_ansible_facts_cacheable": False, + } + self.assertEqual(expected, result) + + def test_run_external_networks_two_with_one_physnet(self): + variables = copy.deepcopy(self.variables) + variables["foo_physical_network"] = "custom1" + module = self._create_module(variables) + external_networks = [{ + "network": "foo", + "required": False, + }, { + "network": "bar", + "required": False, + }] + result = module._run([], external_networks) + expected = { + "changed": False, + "failed": True, + "msg": ("Some external networks have a 'physical_network' " + "attribute defined but the following do not: bar"), + } + self.assertEqual(expected, result) + def test_run_external_networks_two_same_interface(self): variables = copy.deepcopy(self.variables) variables["bar_interface"] = "eth0" @@ -290,6 +360,54 @@ def test_run_external_networks_two_same_interface(self): } self.assertEqual(expected, result) + def test_run_external_networks_two_same_interface_with_physnet(self): + variables = copy.deepcopy(self.variables) + variables["bar_interface"] = "eth0" + variables["foo_physical_network"] = "custom1" + module = self._create_module(variables) + external_networks = [{ + "network": "foo", + "required": False, + }, { + "network": "bar", + "required": False, + }] + result = module._run([], external_networks) + expected = { + "changed": False, + "ansible_facts": { + "kolla_neutron_bridge_names": "eth0-ovs", + "kolla_neutron_external_interfaces": "eth0", + "kolla_neutron_physical_networks": "custom1", + }, + "_ansible_facts_cacheable": False, + } + self.assertEqual(expected, result) + + def test_run_external_networks_two_same_interface_with_different_physnets( + self): + variables = copy.deepcopy(self.variables) + variables["bar_interface"] = "eth0" + variables["foo_physical_network"] = "custom1" + variables["bar_physical_network"] = "custom2" + module = self._create_module(variables) + external_networks = [{ + "network": "foo", + "required": False, + }, { + "network": "bar", + "required": False, + }] + result = module._run([], external_networks) + expected = { + "changed": False, + "failed": True, + "msg": ("Inconsistent 'physical_network' attributes for external " + "networks foo, bar using interface eth0: custom1, " + "custom2"), + } + self.assertEqual(expected, result) + def test_run_external_networks_two_vlans(self): variables = copy.deepcopy(self.variables) variables["foo_interface"] = "eth0.1" diff --git a/molecule-requirements.txt b/molecule-requirements.txt index 794a7bf57..60ecf7351 100644 --- a/molecule-requirements.txt +++ b/molecule-requirements.txt @@ -3,7 +3,7 @@ # process, which may cause wedges in the gate later. ansible-lint>=3.0.0,<6.0.0,!=4.3.0 # MIT -ansible-compat # MIT +ansible-compat<25.0.0 # MIT docker # Apache-2.0 molecule # MIT molecule-plugins[docker] # MIT diff --git a/playbooks/kayobe-base/pre.yml b/playbooks/kayobe-base/pre.yml index e2a106826..194a5f7f8 100644 --- a/playbooks/kayobe-base/pre.yml +++ b/playbooks/kayobe-base/pre.yml @@ -78,7 +78,9 @@ kayobe_galaxy_requirements_src_dir: "{{ kolla_ansible_src_dir }}" kayobe_galaxy_requirements_dest_path: "/tmp/kolla-ansible-requirements.yml" - - block: + - vars: + branch_prefix: "{{ 'unmaintained' if previous_release | lower == '2023.1' else 'stable' }}" + block: - name: Ensure previous kayobe directory exists file: path: "{{ previous_kayobe_src_dir }}" @@ -86,7 +88,7 @@ - name: Ensure previous kayobe repository is cloned command: >- - git clone {{ kayobe_src_dir }} {{ previous_kayobe_src_dir }} -b stable/{{ previous_release | lower }} + git clone {{ kayobe_src_dir }} {{ previous_kayobe_src_dir }} -b {{ branch_prefix }}/{{ previous_release | lower }} - name: Ensure previous kayobe-config directory exists file: @@ -95,7 +97,7 @@ - name: Ensure kayobe-config repository is cloned command: >- - git clone {{ kayobe_config_src_dir }} {{ previous_kayobe_config_src_dir }} -b stable/{{ previous_release | lower }} + git clone {{ kayobe_config_src_dir }} {{ previous_kayobe_config_src_dir }} -b {{ branch_prefix }}/{{ previous_release | lower }} - name: Ensure previous kolla-ansible directory exists file: @@ -104,7 +106,7 @@ - name: Ensure previous kolla-ansible repository is cloned command: >- - git clone {{ kolla_ansible_src_dir }} {{ previous_kolla_ansible_src_dir }} -b stable/{{ previous_release | lower }} + git clone {{ kolla_ansible_src_dir }} {{ previous_kolla_ansible_src_dir }} -b {{ branch_prefix }}/{{ previous_release | lower }} - name: Ensure previous kayobe is executed in verbose mode lineinfile: @@ -112,19 +114,29 @@ line: "export KAYOBE_EXTRA_ARGS=-vvv" regexp: "^#export KAYOBE_EXTRA_ARGS=$" + - name: Ensure ansible.cfg exists for previous Kayobe + copy: + content: "{{ ansible_cfg }}" + dest: "{{ previous_kayobe_config_src_dir }}/etc/kayobe/ansible.cfg" + + - name: Ensure ansible.cfg exists for previous Kolla Ansible + copy: + content: "{{ ansible_cfg_kolla }}" + dest: "{{ previous_kayobe_config_src_dir }}/etc/kayobe/kolla/ansible.cfg" + - name: Update kayobe requirements.yml include_role: name: kayobe-galaxy-requirements vars: kayobe_galaxy_requirements_src_dir: "{{ previous_kayobe_src_dir }}" - kayobe_galaxy_requirements_branch: "stable/{{ previous_release | lower }}" + kayobe_galaxy_requirements_branch: "{{ branch_prefix }}/{{ previous_release | lower }}" - name: Update kolla-ansible requirements.yml include_role: name: kayobe-galaxy-requirements vars: kayobe_galaxy_requirements_src_dir: "{{ previous_kolla_ansible_src_dir }}" - kayobe_galaxy_requirements_branch: "stable/{{ previous_release | lower }}" + kayobe_galaxy_requirements_branch: "{{ branch_prefix }}/{{ previous_release | lower }}" kayobe_galaxy_requirements_dest_path: "/tmp/previous-kolla-ansible-requirements.yml" when: is_upgrade diff --git a/playbooks/kayobe-infra-vm-base/overrides.yml.j2 b/playbooks/kayobe-infra-vm-base/overrides.yml.j2 index 3493adab5..0c3865b48 100644 --- a/playbooks/kayobe-infra-vm-base/overrides.yml.j2 +++ b/playbooks/kayobe-infra-vm-base/overrides.yml.j2 @@ -15,6 +15,9 @@ pip_upper_constraints_file: "/tmp/upper-constraints.txt" # QEMU. libvirt_vm_engine: "qemu" +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" diff --git a/playbooks/kayobe-overcloud-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-base/overrides.yml.j2 index cae26b818..bd389f97a 100644 --- a/playbooks/kayobe-overcloud-base/overrides.yml.j2 +++ b/playbooks/kayobe-overcloud-base/overrides.yml.j2 @@ -15,6 +15,9 @@ kolla_ansible_requirements_yml: "/tmp/kolla-ansible-requirements.yml" kolla_openstack_logging_debug: True pip_upper_constraints_file: "/tmp/upper-constraints.txt" +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" @@ -39,6 +42,9 @@ kolla_enable_ironic: true kolla_enable_heat: false kolla_enable_horizon: false +# Enable Mariabackup for testing database backups +kolla_enable_mariabackup: true + {% if tls_enabled %} kolla_enable_nova_libvirt_container: false compute_libvirt_enable_tls: true diff --git a/playbooks/kayobe-overcloud-base/run.yml b/playbooks/kayobe-overcloud-base/run.yml index d296d6844..76ecd8af6 100644 --- a/playbooks/kayobe-overcloud-base/run.yml +++ b/playbooks/kayobe-overcloud-base/run.yml @@ -49,3 +49,10 @@ source "${KOLLA_CONFIG_PATH:-/etc/kolla}/admin-openrc.sh" && kayobe baremetal compute register &> {{ logs_dir }}/ansible/baremetal-compute-register executable: /bin/bash + + - name: Perform database backup + shell: + cmd: > + source {{ kayobe_src_dir }}/dev/environment-setup.sh && + kayobe overcloud database backup &> {{ logs_dir }}/ansible/database-backup + executable: /bin/bash diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 index ae454d644..41f2ba814 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 +++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 @@ -190,6 +190,9 @@ chrony_ntp_servers: - option: maxsources val: 2 +# Force system clock synchronisation +ntp_force_sync: True + # Enable firewalld controller_firewalld_enabled: true controller_firewalld_zones: diff --git a/playbooks/kayobe-overcloud-host-configure-base/run.yml b/playbooks/kayobe-overcloud-host-configure-base/run.yml index 38e41494c..bca6bc7f4 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/run.yml +++ b/playbooks/kayobe-overcloud-host-configure-base/run.yml @@ -10,6 +10,11 @@ testinfra_venv: ~/testinfra-venv test_path: "{{ kayobe_src_dir }}/playbooks/kayobe-overcloud-host-configure-base/tests/" tasks: + - name: Prevent NetworkManager from managing default interface + command: 'nmcli dev set {{ ansible_facts.default_ipv4.interface }} managed no' + become: true + when: ansible_facts.os_family == 'RedHat' + - name: Ensure overcloud is deployed shell: cmd: "{{ kayobe_src_dir }}/dev/overcloud-deploy.sh &> {{ logs_dir }}/ansible/overcloud-deploy" @@ -29,3 +34,8 @@ command: "{{ testinfra_venv }}/bin/py.test {{ test_path }} --html={{ logs_dir }}/test-results.html --self-contained-html" environment: SITE_MIRROR_FQDN: "{{ zuul_site_mirror_fqdn }}" + + - name: Test bouncing interfaces + shell: + cmd: "{{ kayobe_src_dir }}/dev/overcloud-test-bounce-interface.sh controllers br1 &> {{ logs_dir }}/ansible/overcloud-test-bounce-interface" + executable: /bin/bash diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py index 22cceb429..628cd6227 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py +++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py @@ -12,8 +12,8 @@ def _is_apt(): - info = distro.linux_distribution() - return info[0].startswith('Ubuntu') + info = distro.id() + return info == 'ubuntu' def _is_dnf(): @@ -26,6 +26,12 @@ def _is_dnf_mirror(): return info == 'centos' +def _is_ubuntu_noble(): + name = distro.name() + version = distro.version() + return name == 'Ubuntu' and version == '24.04' + + def test_network_ethernet(host): interface = host.interface('dummy2') assert interface.exists @@ -151,13 +157,13 @@ def test_docker_storage_driver_is_overlay2(host): @pytest.mark.parametrize('user', ['kolla', 'stack']) def test_docker_image_download(host, user): with host.sudo(user): - host.check_output("docker pull alpine") + host.check_output("docker pull quay.io/podman/hello") @pytest.mark.parametrize('user', ['kolla', 'stack']) def test_docker_container_run(host, user): with host.sudo(user): - host.check_output("docker run --rm alpine /bin/true") + host.check_output("docker run --rm quay.io/podman/hello") def test_timezone(host): diff --git a/playbooks/kayobe-overcloud-upgrade-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-upgrade-base/overrides.yml.j2 index 84301ef94..1170a5ed9 100644 --- a/playbooks/kayobe-overcloud-upgrade-base/overrides.yml.j2 +++ b/playbooks/kayobe-overcloud-upgrade-base/overrides.yml.j2 @@ -19,6 +19,9 @@ kolla_ansible_requirements_yml: "/tmp/previous-kolla-ansible-requirements.yml" {% endif %} kolla_openstack_logging_debug: true +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" diff --git a/playbooks/kayobe-seed-base/overrides.yml.j2 b/playbooks/kayobe-seed-base/overrides.yml.j2 index 062ab6c15..91c09a698 100644 --- a/playbooks/kayobe-seed-base/overrides.yml.j2 +++ b/playbooks/kayobe-seed-base/overrides.yml.j2 @@ -17,6 +17,9 @@ kolla_ansible_requirements_yml: "/tmp/kolla-ansible-requirements.yml" kolla_openstack_logging_debug: True pip_upper_constraints_file: "/tmp/upper-constraints.txt" +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" diff --git a/playbooks/kayobe-seed-upgrade-base/overrides.yml.j2 b/playbooks/kayobe-seed-upgrade-base/overrides.yml.j2 index 185d42420..683833025 100644 --- a/playbooks/kayobe-seed-upgrade-base/overrides.yml.j2 +++ b/playbooks/kayobe-seed-upgrade-base/overrides.yml.j2 @@ -19,6 +19,9 @@ kolla_ansible_requirements_yml: "/tmp/previous-kolla-ansible-requirements.yml" {% endif %} kolla_openstack_logging_debug: True +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" diff --git a/playbooks/kayobe-seed-vm-base/overrides.yml.j2 b/playbooks/kayobe-seed-vm-base/overrides.yml.j2 index c7284ce5e..2413fcdf6 100644 --- a/playbooks/kayobe-seed-vm-base/overrides.yml.j2 +++ b/playbooks/kayobe-seed-vm-base/overrides.yml.j2 @@ -16,6 +16,9 @@ pip_upper_constraints_file: "/tmp/upper-constraints.txt" # QEMU. libvirt_vm_engine: "qemu" +# Force system clock synchronisation +ntp_force_sync: True + # Use the CI infra's PyPI mirror. pip_local_mirror: true pip_index_url: "http://{{ zuul_site_mirror_fqdn }}/pypi/simple" diff --git a/releasenotes/notes/adds-internal-vip-to-no-proxy-cbb4db4ea3909185.yaml b/releasenotes/notes/adds-internal-vip-to-no-proxy-cbb4db4ea3909185.yaml new file mode 100644 index 000000000..d03f9f5d1 --- /dev/null +++ b/releasenotes/notes/adds-internal-vip-to-no-proxy-cbb4db4ea3909185.yaml @@ -0,0 +1,17 @@ +--- +features: + - | + Adds the internal VIP to the NOPROXY/noproxy environment variables. +security: + - | + When running API requests from a host configured with kayobe, traffic + destined for the internal VIP is sent via the default proxy. This can be a + security issue if not using TLS as the proxy will be able to intercept the + traffic. If using an untrusted proxy, with TLS disabled on the internal + VIP, it is recommended that you run ``kayobe overcloud host configure -t + proxy``, ``kayobe seed hypervisor host configure -t proxy``, ``kayobe seed + host configure -t proxy``, and ``kayobe infra vm host configure -t proxy``, + to add the internal VIP to the no proxy configuration. This is considered a + minor issue as traffic between containers will not use the proxy by + default. + `LP#2087556 `__ diff --git a/releasenotes/notes/bug-2058922-c16426f013ddf3ca.yaml b/releasenotes/notes/bug-2058922-c16426f013ddf3ca.yaml new file mode 100644 index 000000000..1122890b5 --- /dev/null +++ b/releasenotes/notes/bug-2058922-c16426f013ddf3ca.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixes IPA and host image build failures when Git was not installed on the + build host. `LP#2058922 `__ diff --git a/releasenotes/notes/bug-2083014-set-driver-info-86ff04d3d32cdb8c.yaml b/releasenotes/notes/bug-2083014-set-driver-info-86ff04d3d32cdb8c.yaml new file mode 100644 index 000000000..9bc5bbab1 --- /dev/null +++ b/releasenotes/notes/bug-2083014-set-driver-info-86ff04d3d32cdb8c.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes an issue where task 'ensure ironic nodes use the new Ironic + Python Agent (IPA) images` fails with 'dict object' has no + attribute 'deploy_kernel'. + `__. diff --git a/releasenotes/notes/build-proxysql-image-ec09332ef0b7dae0.yaml b/releasenotes/notes/build-proxysql-image-ec09332ef0b7dae0.yaml new file mode 100644 index 000000000..744a26571 --- /dev/null +++ b/releasenotes/notes/build-proxysql-image-ec09332ef0b7dae0.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The ``proxysql`` image is now built when ``kolla_enable_proxysql`` is set + to ``true``. diff --git a/releasenotes/notes/bump-michael-rigart-interfaces-61def9d407e5de3e.yaml b/releasenotes/notes/bump-michael-rigart-interfaces-61def9d407e5de3e.yaml new file mode 100644 index 000000000..77a21b3cd --- /dev/null +++ b/releasenotes/notes/bump-michael-rigart-interfaces-61def9d407e5de3e.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Bumps the MichaelRigart.interfaces role to v1.15.3 to fix an issue where + NetworkManager was not restarted before bouncing network interfaces. + `LP#2100792 `__ diff --git a/releasenotes/notes/bump-michaelrigart.interfaces-1.15.4-900d2f560c4a19bb.yaml b/releasenotes/notes/bump-michaelrigart.interfaces-1.15.4-900d2f560c4a19bb.yaml new file mode 100644 index 000000000..20d08b0f4 --- /dev/null +++ b/releasenotes/notes/bump-michaelrigart.interfaces-1.15.4-900d2f560c4a19bb.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Bumps the MichaelRigart.interfaces role to v1.15.4 to fix compatibility + with CentOS Stream 9 due to changes in the ``iproute`` package. diff --git a/releasenotes/notes/bump-stackhpc-linux-1.3.0-892bc5ae7574508b.yaml b/releasenotes/notes/bump-stackhpc-linux-1.3.0-892bc5ae7574508b.yaml new file mode 100644 index 000000000..6b33b14cb --- /dev/null +++ b/releasenotes/notes/bump-stackhpc-linux-1.3.0-892bc5ae7574508b.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + Bumps the ``stackhpc.linux`` collection to 1.3.0. Note this version `uses + systemd to activate virtual functions + `__. This + change is restricted to the ``stackhpc.linux.sriov`` role, which is not + used by Kayobe. If a custom playbook uses this role, you can retain + existing behaviour by setting ``sriov_numvfs_driver`` to ``udev``. diff --git a/releasenotes/notes/fix-overcloud-deprovision-prompt-host-lists-cfb701162c114c60.yaml b/releasenotes/notes/fix-overcloud-deprovision-prompt-host-lists-cfb701162c114c60.yaml new file mode 100644 index 000000000..8add3af56 --- /dev/null +++ b/releasenotes/notes/fix-overcloud-deprovision-prompt-host-lists-cfb701162c114c60.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes a bug where non-overcloud hosts would show up in the confirmation + prompt for ``kayobe overcloud deprovision`` + `LP#2091703 `__ diff --git a/releasenotes/notes/fix-var-log-journal-group-mode-3e7c4c822d050c8f.yaml b/releasenotes/notes/fix-var-log-journal-group-mode-3e7c4c822d050c8f.yaml new file mode 100644 index 000000000..1c9e1d444 --- /dev/null +++ b/releasenotes/notes/fix-var-log-journal-group-mode-3e7c4c822d050c8f.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Updates the group and mode set on the ``/var/log/journal`` directory to + match default ownership and permissions used by ``systemd-journald``. + `LP#2083494 `__ diff --git a/releasenotes/notes/fixes-slave-interface-bounce-81d8dc8e5e474447.yaml b/releasenotes/notes/fixes-slave-interface-bounce-81d8dc8e5e474447.yaml new file mode 100644 index 000000000..6875a6429 --- /dev/null +++ b/releasenotes/notes/fixes-slave-interface-bounce-81d8dc8e5e474447.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes an issue where slave interfaces would not be brought back up when + bouncing the master interface. + `LP#2072340 `_. diff --git a/releasenotes/notes/fixes-vgpu-boot-race-condition-b51917e32f685221.yaml b/releasenotes/notes/fixes-vgpu-boot-race-condition-b51917e32f685221.yaml new file mode 100644 index 000000000..656eef62a --- /dev/null +++ b/releasenotes/notes/fixes-vgpu-boot-race-condition-b51917e32f685221.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes an issue on boot where vgpu devices would fail to start due to a race + condition in the startup logic. See + `LP#2102153 `__ for more + details. diff --git a/releasenotes/notes/handle-missing-grub-cmdline-linux-default-f4079def0b1357e7.yaml b/releasenotes/notes/handle-missing-grub-cmdline-linux-default-f4079def0b1357e7.yaml new file mode 100644 index 000000000..bc7afc093 --- /dev/null +++ b/releasenotes/notes/handle-missing-grub-cmdline-linux-default-f4079def0b1357e7.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes generation of kernel parameters when the GRUB_CMDLINE_LINUX_DEFAULT + variable is absent from ``/etc/default/grub``. + `LP#2083874 `__. diff --git a/releasenotes/notes/image-download-auth-e9fd02b71e26dd52.yaml b/releasenotes/notes/image-download-auth-e9fd02b71e26dd52.yaml new file mode 100644 index 000000000..7bb5c7e4d --- /dev/null +++ b/releasenotes/notes/image-download-auth-e9fd02b71e26dd52.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Adds variables to configure authentication parameters in the + ``image-download`` role, which is used to download IPA images. The new + variables are ``image_download_url_username``, + ``image_download_url_password``, ``image_download_force_basic_auth`` and + ``image_download_unredirected_headers``. See documentation of the `get_url + `__ + and `uri + `__ + Ansible modules for more details on how to use these variables. diff --git a/releasenotes/notes/improve-infra-vm-action-performance-5d8a218b75574be2.yaml b/releasenotes/notes/improve-infra-vm-action-performance-5d8a218b75574be2.yaml new file mode 100644 index 000000000..4a955502f --- /dev/null +++ b/releasenotes/notes/improve-infra-vm-action-performance-5d8a218b75574be2.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Deploying and destroying infrastructure VMs is now significantly faster + as only the required variables are passed to the relevant tasks as opposed + to the entire collection of hostvars for each VM. diff --git a/releasenotes/notes/ipa-dynamic-login-b1574eb8e6638a61.yaml b/releasenotes/notes/ipa-dynamic-login-b1574eb8e6638a61.yaml new file mode 100644 index 000000000..d93c9b640 --- /dev/null +++ b/releasenotes/notes/ipa-dynamic-login-b1574eb8e6638a61.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds ``dynamic-login`` to the default list of DIB elements included in + Ironic Python Agent (IPA) images. This element can dynamically configure + credentials at boot time, which is especially useful for troubleshooting. diff --git a/releasenotes/notes/neutron-physical-networks-861c2eca701360e8.yaml b/releasenotes/notes/neutron-physical-networks-861c2eca701360e8.yaml new file mode 100644 index 000000000..fb61e44b5 --- /dev/null +++ b/releasenotes/notes/neutron-physical-networks-861c2eca701360e8.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Adds support for customising Neutron physical network names using the + ``physical_network`` network attribute. +upgrade: + - | + The ``physical_network`` attribute must now be applied consistently + to all external networks in Kayobe configuration. If any external + network has the attribute, then all others must also. diff --git a/releasenotes/notes/ntp-force-sync-d22bfbd63c7be026.yaml b/releasenotes/notes/ntp-force-sync-d22bfbd63c7be026.yaml new file mode 100644 index 000000000..81184d236 --- /dev/null +++ b/releasenotes/notes/ntp-force-sync-d22bfbd63c7be026.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Supports forcing time synchronisation after configuring ``chrony`` if + ``ntp_force_sync`` is changed to ``True``. diff --git a/releasenotes/notes/nvue-1-2-6-dd6aaa63cd49a37d.yaml b/releasenotes/notes/nvue-1-2-6-dd6aaa63cd49a37d.yaml new file mode 100644 index 000000000..1152232ec --- /dev/null +++ b/releasenotes/notes/nvue-1-2-6-dd6aaa63cd49a37d.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Bumps the ``nvidia.nvue`` collection to 1.2.6. diff --git a/releasenotes/notes/pin-ipa-requirements-f9566011b2400e6c.yaml b/releasenotes/notes/pin-ipa-requirements-f9566011b2400e6c.yaml new file mode 100644 index 000000000..5b3b9a52c --- /dev/null +++ b/releasenotes/notes/pin-ipa-requirements-f9566011b2400e6c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Pin requirements for IPA image build to ensure that the ``ironic-lib`` + version matches ``ironic-python-agent``. + `LP#2089263 `__ diff --git a/requirements.yml b/requirements.yml index b27b5a53c..283e09923 100644 --- a/requirements.yml +++ b/requirements.yml @@ -5,16 +5,16 @@ collections: version: stable/2024.1 - name: dellemc.os10 version: 1.1.1 + - name: nvidia.nvue + version: 1.2.6 - name: openstack.cloud version: '<3' - name: stackhpc.linux - version: 1.2.3 + version: 1.3.1 - name: stackhpc.network version: 1.0.0 - name: stackhpc.openstack - version: 0.2.1 - - name: nvidia.nvue - version: 1.2.0 + version: 0.2.2 roles: - src: ahuffman.resolv @@ -24,8 +24,9 @@ roles: - src: jriguera.configdrive # There are no versioned releases of this role. version: acd08fd126d0e442ab8b3bc518e37761390d8c2f - - src: MichaelRigart.interfaces - version: v1.14.4 + - name: MichaelRigart.interfaces + src: git+https://github.com/stackhpc/ansible-role-interfaces + version: bugfix/infiniband - src: mrlesmithjr.chrony version: v0.1.4 - src: mrlesmithjr.manage_lvm diff --git a/roles/kayobe-galaxy-requirements/defaults/main.yml b/roles/kayobe-galaxy-requirements/defaults/main.yml index d39009ef8..dca4eae3c 100644 --- a/roles/kayobe-galaxy-requirements/defaults/main.yml +++ b/roles/kayobe-galaxy-requirements/defaults/main.yml @@ -1,5 +1,5 @@ --- kayobe_galaxy_requirements_src_dir: -kayobe_galaxy_requirements_branch: "{{ zuul.branch }}" +kayobe_galaxy_requirements_branch: kayobe_galaxy_requirements_src_path: "requirements.yml" kayobe_galaxy_requirements_dest_path: "{{ kayobe_galaxy_requirements_src_dir }}/{{ kayobe_galaxy_requirements_src_path }}" diff --git a/roles/kayobe-galaxy-requirements/tasks/main.yml b/roles/kayobe-galaxy-requirements/tasks/main.yml index 0fcd197f1..7f48fb8a1 100644 --- a/roles/kayobe-galaxy-requirements/tasks/main.yml +++ b/roles/kayobe-galaxy-requirements/tasks/main.yml @@ -14,7 +14,12 @@ new_requirement: name: "file://{{ ansible_collection_kolla_src_dir }}" type: git + override_version: version: "{{ kayobe_galaxy_requirements_branch }}" + # NOTE(wszusmki): Use version checked out by Zuul so Depends-On works and only override version + # for previous release. + new_requirement_with_version: "{{ new_requirement | combine(override_version) if kayobe_galaxy_requirements_branch else new_requirement }}" new_requirements: - collections: "{{ (old_requirements.collections | rejectattr('name', 'search', 'ansible-collection-kolla') | list) + [new_requirement] }}" + collections: "{{ (old_requirements.collections | rejectattr('name', 'search', 'ansible-collection-kolla') | list) + [new_requirement_with_version] }}" roles: "{{ old_requirements.roles | default([]) }}" +