diff --git a/ansible/network-connectivity.yml b/ansible/network-connectivity.yml index 5fdbe2eab..7e2cfb5ef 100644 --- a/ansible/network-connectivity.yml +++ b/ansible/network-connectivity.yml @@ -68,6 +68,11 @@ network. run_once: True + - name: Compute a mapping of network name to hosts on that network + run_once: True + set_fact: + network_hosts_map: "{{ ansible_play_batch | net_hosts_by_network }}" + - name: Ensure hosts on the same network are reachable command: > ping {{ remote_ip }} -c1 -M do {% if mtu %} -s {{ mtu | int - icmp_overhead_bytes }}{% endif %} @@ -82,13 +87,9 @@ vars: # Select other hosts targeted by this play which have this network # interface (item). - remote_hosts: > - {{ hostvars.values() | - selectattr('inventory_hostname', 'is_in', ansible_play_batch) | - selectattr('network_interfaces', 'defined') | - selectattr('network_interfaces', 'issuperset', [item]) | - rejectattr('inventory_hostname', 'equalto', inventory_hostname) | - map(attribute='inventory_hostname') | + remote_hosts: >- + {{ (network_hosts_map[item] | default([])) | + reject('equalto', inventory_hostname) | list }} # NOTE(wszumski): Needed to fix random choice for the run otherwise the # when check: remote_ip | length > 0, would pass, but remote_ip was '' diff --git a/ansible/physical-network.yml b/ansible/physical-network.yml index 8891a62c5..9cdc4de6d 100644 --- a/ansible/physical-network.yml +++ b/ansible/physical-network.yml @@ -28,7 +28,9 @@ physical_network_display: False # List of supported values for the 'switch_type' variable. supported_switch_types: + - alliedware-plus - arista + - cisco-nxos - dellos6 - dellos9 - dellos10 @@ -109,6 +111,22 @@ debug: var: switch_interface_config +- name: Ensure AlliedWare Plus physical switches are configured + hosts: switches_of_type_alliedware-plus:&switches_in_display_mode_False + gather_facts: no + max_fail_percentage: >- + {{ physical_network_max_fail_percentage | + default(kayobe_max_fail_percentage) | + default(100) }} + roles: + - role: ssh-known-host + when: not switch_skip_keyscan | bool + + - role: alliedware-plus-switch + alliedware_plus_switch_config: "{{ switch_config }}" + alliedware_plus_switch_interface_config: "{{ switch_interface_config }}" + alliedware_plus_switch_save: "{{ switch_config_save }}" + - name: Ensure Arista physical switches are configured hosts: switches_of_type_arista:&switches_in_display_mode_False gather_facts: no @@ -126,6 +144,22 @@ arista_switch_config: "{{ switch_config }}" arista_switch_interface_config: "{{ switch_interface_config }}" +- name: Ensure Cisco NX-OS physical switches are configured + hosts: switches_of_type_cisco-nxos:&switches_in_display_mode_False + gather_facts: no + max_fail_percentage: >- + {{ physical_network_max_fail_percentage | + default(kayobe_max_fail_percentage) | + default(100) }} + roles: + - role: ssh-known-host + when: not switch_skip_keyscan | bool + + - role: cisco-nxos-switch + cisco_nxos_switch_config: "{{ switch_config }}" + cisco_nxos_switch_interface_config: "{{ switch_interface_config }}" + cisco_nxos_switch_save: "{{ switch_config_save }}" + - name: Ensure DellOS physical switches are configured hosts: switches_of_type_dellos6:switches_of_type_dellos9:switches_of_type_dellos10:&switches_in_display_mode_False gather_facts: no diff --git a/ansible/roles/alliedware-plus-switch/README.md b/ansible/roles/alliedware-plus-switch/README.md new file mode 100644 index 000000000..4b5290336 --- /dev/null +++ b/ansible/roles/alliedware-plus-switch/README.md @@ -0,0 +1,79 @@ +AlliedWare Plus Switch +====================== + +This role configures AlliedWare Plus switches using the +`alliedtelesis.awplus.awplus_config` Ansible module. It provides a fairly +minimal abstraction of the configuration interface provided by the +`awplus_config` module, allowing for application of arbitrary switch +configuration options. + +Requirements +------------ + +The switches should be configured to allow SSH access. + +The `alliedtelesis.awplus` Ansible collection must be installed. + +Role Variables +-------------- + +`alliedware_plus_switch_config` is a list of configuration lines to apply to +the switch, and defaults to an empty list. + +`alliedware_plus_switch_interface_config` contains interface configuration. It +is a dict mapping switch interface names to configuration dicts, and defaults +to an empty dict. Each dict may contain the following items: + +- `description` - an optional description to apply to the interface. +- `config` - a list of per-interface configuration commands. + +`alliedware_plus_switch_save` controls whether the running configuration is +saved to the startup configuration when they differ, including any previously +unsaved changes. Defaults to `false`. + +Dependencies +------------ + +None + +Example Playbook +---------------- + +The following playbook configures hosts in the `alliedware-plus-switches` +group. It assumes host variables for each switch holding the host and SSH +credentials. + + --- + - name: Ensure AlliedWare Plus switches are configured + hosts: alliedware-plus-switches + gather_facts: no + vars: + ansible_connection: ansible.netcommon.network_cli + ansible_network_os: alliedtelesis.awplus.awplus + roles: + - role: alliedware-plus-switch + alliedware_plus_switch_config: + - "vlan database" + - " vlan 1000,1001 state enable" + alliedware_plus_switch_interface_config: + port1.0.1: + description: server-1 + config: + - "no shutdown" + - "switchport" + - "switchport mode trunk" + - "switchport trunk native vlan 1000" + - "switchport trunk allowed vlan add 1001" + port1.0.2: + description: server-2 + config: + - "no shutdown" + - "switchport" + - "switchport mode trunk" + - "switchport trunk native vlan 1000" + - "switchport trunk allowed vlan add 1001" + +Author Information +------------------ + +- Pierre Riteau () diff --git a/ansible/roles/alliedware-plus-switch/defaults/main.yml b/ansible/roles/alliedware-plus-switch/defaults/main.yml new file mode 100644 index 000000000..3a1e6556c --- /dev/null +++ b/ansible/roles/alliedware-plus-switch/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# List of configuration lines to apply to the switch. +alliedware_plus_switch_config: [] + +# Interface configuration. Dict mapping switch interface names to configuration +# dicts. Each dict contains an optional 'description' item and a 'config' item +# which should contain a list of per-interface configuration. +alliedware_plus_switch_interface_config: {} + +# Whether to save the configuration. +alliedware_plus_switch_save: false diff --git a/ansible/roles/alliedware-plus-switch/tasks/main.yml b/ansible/roles/alliedware-plus-switch/tasks/main.yml new file mode 100644 index 000000000..ceb0d434c --- /dev/null +++ b/ansible/roles/alliedware-plus-switch/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Ensure AlliedWare Plus switches are configured + alliedtelesis.awplus.awplus_config: + lines: "{{ lookup('template', 'alliedware-plus-config.j2') }}" + save_when: >- + {{ alliedware_plus_switch_save | bool | ternary('modified', 'never') }} diff --git a/ansible/roles/alliedware-plus-switch/templates/alliedware-plus-config.j2 b/ansible/roles/alliedware-plus-switch/templates/alliedware-plus-config.j2 new file mode 100644 index 000000000..e1138bb9e --- /dev/null +++ b/ansible/roles/alliedware-plus-switch/templates/alliedware-plus-config.j2 @@ -0,0 +1,15 @@ +#jinja2: trim_blocks: True,lstrip_blocks: True + +{% for line in alliedware_plus_switch_config %} +{{ line }} +{% endfor %} + +{% for interface, config in alliedware_plus_switch_interface_config.items() %} +interface {{ interface }} +{% if config.description is defined %} + description {{ config.description }} +{% endif %} +{% for line in config.config %} + {{ line }} +{% endfor %} +{% endfor %} diff --git a/ansible/roles/cisco-nxos-switch/README.md b/ansible/roles/cisco-nxos-switch/README.md new file mode 100644 index 000000000..e51f59cdd --- /dev/null +++ b/ansible/roles/cisco-nxos-switch/README.md @@ -0,0 +1,59 @@ +Cisco NX-OS Switch +================== + +This role configures Cisco NX-OS switches using the `cisco.nxos.nxos_config` +Ansible module. It allows global and per-interface configuration commands to +be applied to the switch. + +Requirements +------------ + +The switches should be configured to allow SSH access. + +The `cisco.nxos` Ansible collection must be installed. + +Role Variables +-------------- + +`cisco_nxos_switch_config` is a list of configuration lines to apply to the +switch, and defaults to an empty list. + +`cisco_nxos_switch_interface_config` is a dict mapping switch interface names +to configuration dicts, and defaults to an empty dict. Each dict contains: + +- `description` - an optional description to apply to the interface. +- `config` - a list of per-interface configuration commands. + +`cisco_nxos_switch_save` controls whether the running configuration is saved +to the startup configuration when they differ, including any previously unsaved +changes. Defaults to `false`. + +Dependencies +------------ + +None + +Example Playbook +---------------- + +The following playbook configures hosts in the `cisco-nxos-switches` group. +It assumes host variables for each switch holding the host and SSH credentials. +It enables LLDP and configures an Ethernet interface as a switchport. + + --- + - name: Ensure Cisco NX-OS switches are configured + hosts: cisco-nxos-switches + gather_facts: no + vars: + ansible_connection: ansible.netcommon.network_cli + ansible_network_os: cisco.nxos.nxos + roles: + - role: cisco-nxos-switch + cisco_nxos_switch_config: + - "feature lldp" + cisco_nxos_switch_interface_config: + Ethernet1/1: + description: server-1 + config: + - "switchport" + - "switchport mode access" diff --git a/ansible/roles/cisco-nxos-switch/defaults/main.yml b/ansible/roles/cisco-nxos-switch/defaults/main.yml new file mode 100644 index 000000000..8356cac5a --- /dev/null +++ b/ansible/roles/cisco-nxos-switch/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# List of configuration lines to apply to the switch. +cisco_nxos_switch_config: [] + +# Interface configuration. Dict mapping switch interface names to configuration +# dicts. Each dict contains an optional 'description' item and a 'config' item +# which should contain a list of per-interface configuration. +cisco_nxos_switch_interface_config: {} + +# Whether to save the configuration. +cisco_nxos_switch_save: false diff --git a/ansible/roles/cisco-nxos-switch/tasks/main.yml b/ansible/roles/cisco-nxos-switch/tasks/main.yml new file mode 100644 index 000000000..beb5cd07b --- /dev/null +++ b/ansible/roles/cisco-nxos-switch/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Ensure Cisco NX-OS switches are configured + cisco.nxos.nxos_config: + content: "{{ lookup('template', 'cisco-nxos-config.j2') }}" + save_when: >- + {{ cisco_nxos_switch_save | bool | ternary('modified', 'never') }} diff --git a/ansible/roles/cisco-nxos-switch/templates/cisco-nxos-config.j2 b/ansible/roles/cisco-nxos-switch/templates/cisco-nxos-config.j2 new file mode 100644 index 000000000..d219991f9 --- /dev/null +++ b/ansible/roles/cisco-nxos-switch/templates/cisco-nxos-config.j2 @@ -0,0 +1,16 @@ +#jinja2: trim_blocks: True,lstrip_blocks: True + +{% for line in cisco_nxos_switch_config %} +{{ line }} +{% endfor %} + +{% for interface, config in cisco_nxos_switch_interface_config.items() %} +interface {{ interface }} +{% if config.description is defined %} + description {{ config.description }} +{% endif %} +{% for line in config.config %} + {{ line }} +{% endfor %} +! +{% endfor %} diff --git a/ansible/roles/dell-switch/templates/dellos10-config.j2 b/ansible/roles/dell-switch/templates/dellos10-config.j2 index 7904f89c6..0fcbec176 100644 --- a/ansible/roles/dell-switch/templates/dellos10-config.j2 +++ b/ansible/roles/dell-switch/templates/dellos10-config.j2 @@ -4,13 +4,28 @@ {{ line }} {% endfor %} +{# Configure VLAN interfaces first as others might depend on them. #} + {% for interface, config in dell_switch_interface_config.items() %} +{% if 'vlan' in interface %} interface {{ interface }} {% if config.description is defined %} -description "{{ config.description }}" + description "{{ config.description }}" {% endif %} {% for line in config.config %} -{{ line }} + {{ line }} +{% endfor %} +{% endif %} +{% endfor %} + +{% for interface, config in dell_switch_interface_config.items() %} +{% if 'vlan' not in interface %} +interface {{ interface }} +{% if config.description is defined %} + description "{{ config.description }}" +{% endif %} +{% for line in config.config %} + {{ line }} {% endfor %} -exit +{% endif %} {% endfor %} diff --git a/ansible/roles/firewalld/tasks/enabled.yml b/ansible/roles/firewalld/tasks/enabled.yml index 1ab86bcd6..4428c8b27 100644 --- a/ansible/roles/firewalld/tasks/enabled.yml +++ b/ansible/roles/firewalld/tasks/enabled.yml @@ -13,6 +13,23 @@ state: started become: true +# NOTE: The firewalld Ansible module decides whether to use online or offline +# (on-disk only) operations when it is imported, based on whether the firewalld +# D-Bus interface is available. Starting the service does not guarantee that +# the interface is ready yet, so if the first zone is created too early it is +# written to disk only and is not loaded into the running daemon. A subsequent +# attempt to assign an interface to that zone then fails with +# 'INVALID_ZONE'. Wait until firewalld can be queried before configuring it. +- name: Wait for firewalld to be ready + command: + cmd: firewall-cmd --state + register: firewalld_state + until: firewalld_state.rc == 0 + retries: 30 + delay: 1 + changed_when: false + become: true + - block: - name: Get firewalld current default zone command: diff --git a/ansible/roles/ntp/tasks/sync.yml b/ansible/roles/ntp/tasks/sync.yml index 25db6c7f3..c5d26a09a 100644 --- a/ansible/roles/ntp/tasks/sync.yml +++ b/ansible/roles/ntp/tasks/sync.yml @@ -32,4 +32,6 @@ retries: 60 delay: 10 failed_when: sync_check.rc != 0 + # NOTE(wszumski): Need to explitly skip due to retries checking stdout/rc + when: not ansible_check_mode | bool when: ntp_force_sync | bool diff --git a/doc/source/configuration/reference/physical-network.rst b/doc/source/configuration/reference/physical-network.rst index ff00a60c8..bfc2c1273 100644 --- a/doc/source/configuration/reference/physical-network.rst +++ b/doc/source/configuration/reference/physical-network.rst @@ -14,7 +14,9 @@ configure`` command. See :ref:`physical-network` for details. The following switch operating systems are currently supported: +* AlliedWare Plus * Arista EOS +* Cisco NX-OS * Cumulus Linux (via `Network Command Line Utility (NCLU) `__) * Cumulus Linux (via `NVIDIA User Experience command line utility (NVUE) @@ -196,6 +198,28 @@ hosts file on the Ansible control host will need to be populated manually. Device-specific Configuration Variables ======================================= +AlliedWare Plus +--------------- + +Configuration for these devices is applied using the ``alliedware-plus-switch`` +Ansible role in Kayobe. The role configures AlliedWare Plus switches using the +``alliedtelesis.awplus`` Ansible modules. + +``switch_type`` should be set to ``alliedware-plus``. + + +``switch_config_save`` may be set to ``true`` to save the running configuration +to the startup configuration when they differ, including any previously unsaved +changes. + +* ``ansible_host`` is the hostname or IP address. Optional. +* ``ansible_user`` is the SSH username. +* ``ansible_ssh_pass`` is the SSH password. +* ``ansible_connection`` should be ``ansible.netcommon.network_cli``. +* ``ansible_network_os`` should be ``alliedtelesis.awplus.awplus``. +* ``ansible_become`` should be ``true``. +* ``ansible_become_method`` should be ``enable``. + Arista EOS ---------- @@ -213,6 +237,44 @@ modules. * ``ansible_become`` should be ``true``. * ``ansible_become_method`` should be ``enable``. +Cisco NX-OS +----------- + +Configuration for these devices is applied using the ``cisco-nxos-switch`` +Ansible role in Kayobe. The role configures Cisco NX-OS switches using the +``cisco.nxos.nxos_config`` Ansible module. + +``switch_type`` should be set to ``cisco-nxos``. + +``switch_config_save`` may be set to ``true`` to save the running configuration +to the startup configuration when they differ, including any previously unsaved +changes. + +* ``ansible_host`` is the hostname or IP address. Optional. +* ``ansible_user`` is the SSH username. +* ``ansible_ssh_pass`` is the SSH password, if using password authentication. +* ``ansible_connection`` should be ``ansible.netcommon.network_cli``. +* ``ansible_network_os`` should be ``cisco.nxos.nxos``. + +The SSH user must have permission to configure the switch. + +For example, the following host variables enable LLDP and configure a +switchport: + +.. code-block:: yaml + + switch_type: cisco-nxos + ansible_connection: ansible.netcommon.network_cli + ansible_network_os: cisco.nxos.nxos + switch_config: + - "feature lldp" + switch_interface_config: + Ethernet1/1: + description: server-1 + config: + - "switchport" + - "switchport mode access" + Cumulus Linux (with NCLU) ------------------------- diff --git a/kayobe/plugins/filter/networks.py b/kayobe/plugins/filter/networks.py index 50fe5caf8..b5b0804cf 100644 --- a/kayobe/plugins/filter/networks.py +++ b/kayobe/plugins/filter/networks.py @@ -183,6 +183,28 @@ def net_ip(context, name, inventory_hostname=None): return ips.get(inventory_hostname) +@jinja2.pass_context +def net_hosts_by_network(context, hostnames): + """Return a mapping of network name to hosts with that network. + + :param context: a Jinja2 Context object. + :param hostnames: list of inventory hostnames to consider. + :returns: a dict mapping network name to a list of inventory hostnames + from hostnames whose network_interfaces includes that network. + """ + hostvars = context['hostvars'] + result = {} + for hostname in hostnames: + if hostname not in hostvars: + continue + interfaces = hostvars[hostname].get('network_interfaces') + if not interfaces: + continue + for name in interfaces: + result.setdefault(name, []).append(hostname) + return result + + @jinja2.pass_context def net_interface(context, name, inventory_hostname=None): return net_attr(context, name, 'interface', inventory_hostname) @@ -788,6 +810,7 @@ def get_filters(): 'net_attr': net_attr, 'net_vip_address': net_vip_address, 'net_fqdn': _make_attr_filter('fqdn'), + 'net_hosts_by_network': net_hosts_by_network, 'net_ip': net_ip, 'net_interface': net_interface, 'net_parent': net_parent, diff --git a/playbooks/kayobe-overcloud-base/run.yml b/playbooks/kayobe-overcloud-base/run.yml index 9fcb2da17..74c16090b 100644 --- a/playbooks/kayobe-overcloud-base/run.yml +++ b/playbooks/kayobe-overcloud-base/run.yml @@ -74,6 +74,16 @@ KAYOBE_ENVIRONMENT=aio-network-connectivity kayobe network connectivity check --limit overcloud &> {{ logs_dir }}/ansible/network-connectivity-check executable: /bin/bash + - name: Run configuration dump + shell: + # Dump the configuration for all hosts in the inventory. This exercises + # the kayobe_config_dump filter, including for host variables that + # reference hostvars or undefined variables. + cmd: > + source {{ kayobe_src_dir }}/dev/environment-setup.sh && + kayobe configuration dump &> {{ logs_dir }}/ansible/configuration-dump + executable: /bin/bash + - name: Perform database backup shell: cmd: > diff --git a/releasenotes/notes/add-cisco-nxos-switch-support-6ef2c747d24e5197.yaml b/releasenotes/notes/add-cisco-nxos-switch-support-6ef2c747d24e5197.yaml new file mode 100644 index 000000000..7886998b7 --- /dev/null +++ b/releasenotes/notes/add-cisco-nxos-switch-support-6ef2c747d24e5197.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds support for configuring Cisco NX-OS switches with + ``kayobe physical network configure``. Set ``switch_type`` to + ``cisco-nxos`` to use this support. diff --git a/releasenotes/notes/alliedware-plus-switch-75625ad2e5d61241.yaml b/releasenotes/notes/alliedware-plus-switch-75625ad2e5d61241.yaml new file mode 100644 index 000000000..1dd97cd60 --- /dev/null +++ b/releasenotes/notes/alliedware-plus-switch-75625ad2e5d61241.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for configuration of AlliedWare Plus switches. This is + integrated with the ``kayobe physical network configure`` command. diff --git a/releasenotes/notes/bug-2168529-b206bb5ace7090b3.yaml b/releasenotes/notes/bug-2168529-b206bb5ace7090b3.yaml new file mode 100644 index 000000000..d06aaa964 --- /dev/null +++ b/releasenotes/notes/bug-2168529-b206bb5ace7090b3.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes Dell OS10 switch config. Also makes sure that VLAN interfaces are + defined first as others might depend on them. + `LP#2168529 `__ diff --git a/releasenotes/notes/firewalld-wait-ready-7a1c2d9e4f6b8a30.yaml b/releasenotes/notes/firewalld-wait-ready-7a1c2d9e4f6b8a30.yaml new file mode 100644 index 000000000..76b0bcba0 --- /dev/null +++ b/releasenotes/notes/firewalld-wait-ready-7a1c2d9e4f6b8a30.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixes a race when configuring firewalld that could cause host + configuration to fail with ``INVALID_ZONE`` for a zone that was created + while firewalld was still starting up. The firewalld role now waits until + the firewalld D-Bus interface is ready before creating zones, ensuring + that they are applied to the running daemon rather than only to the + on-disk configuration. diff --git a/releasenotes/notes/ntp-fix-check-mode-47dd67d567ef7d96.yaml b/releasenotes/notes/ntp-fix-check-mode-47dd67d567ef7d96.yaml new file mode 100644 index 000000000..b1f88ba5e --- /dev/null +++ b/releasenotes/notes/ntp-fix-check-mode-47dd67d567ef7d96.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The ``ntp`` role now explicitly skips the ``command`` based clock + synchronisation tasks when running in check mode. diff --git a/releasenotes/notes/optimize-network-connectivity-check-ca6df7237a453cc0.yaml b/releasenotes/notes/optimize-network-connectivity-check-ca6df7237a453cc0.yaml new file mode 100644 index 000000000..c3d2ede7b --- /dev/null +++ b/releasenotes/notes/optimize-network-connectivity-check-ca6df7237a453cc0.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Optimize network connectivity check by mapping of network name + to hosts on that network only once per play, instead of once per host. diff --git a/requirements.yml b/requirements.yml index 462f10fe5..6c3a9ab5e 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,8 +1,12 @@ --- collections: + - name: alliedtelesis.awplus + version: 1.3.0 - name: https://github.com/openstack/ansible-collection-kolla type: git version: stable/2026.1 + - name: cisco.nxos + version: 13.0.0 - name: community.docker version: 5.2.0 - name: community.network