Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6ceeb02
Fix nmstate zone sync for cloud-init profiles
grzegorzkoper Sep 3, 2026
e9643c5
Add check and diff mode support to nmstate engine
grzegorzkoper Sep 2, 2026
da7498d
Move package installs into a single script
RowanJohns Sep 10, 2026
b28e2c0
Bump MichaelRigart.interfaces to fix template bug
dougszumski Sep 9, 2026
b625af7
Add support for configuring Dell SONiC switches
MoteHue Sep 8, 2026
056d847
Support whitespace in Dell switch port description
L-Chams Jul 27, 2026
62f8f6e
CI: Ignore .md files
priteau Sep 23, 2026
6aca27b
CI: Prefer ipv4 over ipv6
priteau Sep 24, 2026
7768598
Merge "Fix nmstate zone sync for cloud-init profiles" into stable/2026.1
Sep 25, 2026
c2e2071
Merge "Bump MichaelRigart.interfaces to fix template bug" into stable…
Sep 25, 2026
250f2da
Use upper constraints to install docker from PyPI
priteau Sep 18, 2026
49b1226
Merge "Add support for configuring Dell SONiC switches" into stable/2…
Sep 25, 2026
1e261d7
Check python is installed in check mode
jovial Aug 25, 2026
1169b7e
Merge "Move package installs into a single script" into stable/2026.1
Sep 25, 2026
7577fb3
Merge "CI: Ignore .md files" into stable/2026.1
Sep 25, 2026
645bb12
Merge "Use upper constraints to install docker from PyPI" into stable…
Sep 25, 2026
d4a5147
Merge "Check python is installed in check mode" into stable/2026.1
Sep 25, 2026
140afe3
Merge "Support whitespace in Dell switch port description" into stabl…
Sep 25, 2026
512e168
Merge "Add check and diff mode support to nmstate engine" into stable…
Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ansible/control-host-configure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "install-packages.yml"
- import_playbook: "selinux.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
Expand Down
6 changes: 6 additions & 0 deletions ansible/install-packages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
- name: Install kayobe-packages
ansible.builtin.import_playbook: dev-tools.yml

- name: Install kolla-packages
ansible.builtin.import_playbook: kolla-packages.yml
1 change: 1 addition & 0 deletions ansible/kayobe-ansible-user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
changed_when: false
failed_when: false
register: check_python
check_mode: false

- name: Ensure Python is installed
raw: "(test -e /usr/bin/apt && sudo apt {{ apt_options | select | join(' ') }} update && sudo apt install {{ apt_options | select | join(' ') }} python3-minimal) || (test -e /usr/bin/dnf && sudo dnf {{ dnf_options | select | join(' ') }} install python3)"
Expand Down
3 changes: 1 addition & 2 deletions ansible/overcloud-host-configure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "install-packages.yml"
- import_playbook: "selinux.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
Expand All @@ -28,7 +28,6 @@
- import_playbook: "kolla-ansible-user.yml"
- import_playbook: "kolla-pip.yml"
- import_playbook: "kolla-target-venv.yml"
- import_playbook: "kolla-packages.yml"
- import_playbook: "container-engine.yml"
- import_playbook: "apparmor-libvirt.yml"
- import_playbook: "swift-block-devices.yml"
Expand Down
18 changes: 18 additions & 0 deletions ansible/physical-network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- dellos9
- dellos10
- dell-powerconnect
- dell-sonic
- junos
- mellanox
- nclu
Expand Down Expand Up @@ -159,6 +160,23 @@
dell_powerconnect_switch_config: "{{ switch_config }}"
dell_powerconnect_switch_interface_config: "{{ switch_interface_config }}"

- name: Ensure Dell SONiC physical switches are configured
hosts: switches_of_type_dell-sonic:&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: dell-switch-sonic
dell_switch_sonic_type: "{{ switch_type }}"
dell_switch_sonic_config: "{{ switch_config }}"
dell_switch_sonic_interface_config: "{{ switch_interface_config }}"
dell_switch_sonic_save: "{{ switch_config_save }}"

- name: Ensure Juniper physical switches are configured
hosts: switches_of_type_junos:&switches_in_display_mode_False
gather_facts: no
Expand Down
63 changes: 63 additions & 0 deletions ansible/roles/dell-switch-sonic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Dell Switch - SONiC
===================

This role configures Dell switches using the `dellemc.enterprise_sonic`
Ansible collections. It provides a fairly minimal abstraction of
the configuration interface provided by the collections, allowing for
application of arbitrary switch configuration options.

Requirements
------------

The switches should be configured to allow SSH access.

Role Variables
--------------

`dell_switch_sonic_config` is a list of configuration lines to apply to the switch,
and defaults to an empty list.

`dell_switch_sonic_interface_config` contains interface configuration. It is a dict
mapping switch interface names to configuration dicts. Each dict may contain
the following items:

- `description` - a description to apply to the interface.
- `config` - a list of per-interface configuration.

Dependencies
------------

None

Example Playbook
----------------

The following playbook configures hosts in the `dell-switches-sonic` group.
It assumes host variables for each switch holding the host, username and
passwords. It applies global configuration for LLDP, and enables two
10G ethernet interfaces as switchports.

---
- name: Ensure Dell SONiC switches are configured
hosts: dell-switches-sonic
gather_facts: no
roles:
- role: dell-switch-sonic
dell_switch_sonic_config:
- "lldp enable"
- "ntp server 10.0.12.34"
dell_switch_sonic_interface_config:
Ethernet1:
description: server-1
config:
- "no shutdown"
- "switchport"
Ethernet2:
description: server-2
config:
- "no shutdown"

Author Information
------------------

- Matt Crees (<mattc@stackhpc.com>)
11 changes: 11 additions & 0 deletions ansible/roles/dell-switch-sonic/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# List of configuration lines to apply to the switch.
dell_switch_sonic_config: []

# Interface configuration. Dict mapping switch interface names to configuration
# dicts. Each dict contains a 'description' item and a 'config' item which
# should contain a list of per-interface configuration.
dell_switch_sonic_interface_config: {}

# Whether to save the configuration.
dell_switch_sonic_save: false
5 changes: 5 additions & 0 deletions ansible/roles/dell-switch-sonic/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: Ensure Dell SONiC switches are configured
dellemc.enterprise_sonic.sonic_config: # noqa fqcn[canonical]
src: "../templates/dell-sonic-config.j2"
save: "{{ dell_switch_sonic_save | bool }}"
16 changes: 16 additions & 0 deletions ansible/roles/dell-switch-sonic/templates/dell-sonic-config.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#jinja2: trim_blocks: True,lstrip_blocks: True

{% for line in dell_switch_sonic_config %}
{{ line }}
{% endfor %}

{% for interface, config in dell_switch_sonic_interface_config.items() %}
interface {{ interface }}
{% if config.description is defined %}
description "{{ config.description }}"
{% endif %}
{% for line in config.config %}
{{ line }}
{% endfor %}
exit
{% endfor %}
2 changes: 1 addition & 1 deletion ansible/roles/dell-switch/templates/dellos10-config.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{% for interface, config in dell_switch_interface_config.items() %}
interface {{ interface }}
{% if config.description is defined %}
description {{ config.description }}
description "{{ config.description }}"
{% endif %}
{% for line in config.config %}
{{ line }}
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/dell-switch/templates/dellos6-config.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{% for interface, config in dell_switch_interface_config.items() %}
interface {{ interface }}
{% if config.description is defined %}
description {{ config.description }}
description "{{ config.description }}"
{% endif %}
{% for line in config.config %}
{{ line }}
Expand Down
4 changes: 2 additions & 2 deletions ansible/roles/dell-switch/templates/dellos9-config.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dell_switch_interface_config.items() %}
{% if 'vlan' not in interface %}
interface {{ interface }}
{% if config.description is defined %}
description {{ config.description }}
description "{{ config.description }}"
{% endif %}
{% for line in config.config %}
{{ line }}
Expand All @@ -25,7 +25,7 @@ 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 }}
Expand Down
3 changes: 3 additions & 0 deletions ansible/roles/dev-tools/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ dev_tools_packages_system:
# NOTE(mgoddard): The acl package is required for the setfacl command, used by
# become_user.
- acl
# NOTE(rowanjohns): kernel-modules-extra-matched needs to be installed on RedHat
# hosts so that iptables can be used.
- "{{ 'kernel-modules-extra-matched' if ansible_facts.os_family == 'RedHat' and ansible_facts.distribution_major_version == '10' else '' }}"

# List of packages to install.
dev_tools_packages: "{{ (dev_tools_packages_default + dev_tools_packages_extra + dev_tools_packages_system) | select | list }}"
8 changes: 1 addition & 7 deletions ansible/roles/kolla/tasks/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@
version: "{{ item.version | default(omit) }}"
state: "{{ item.state | default('present') }}"
virtualenv: "{{ kolla_venv }}"
extra_args: "{{ item.extra_args | default(default_extra_args) }}"
vars:
default_extra_args: "{% if kolla_upper_constraints_file %}-c {{ kolla_upper_constraints_file }}{% endif %}"
extra_args: "{% if kolla_upper_constraints_file %}-c {{ kolla_upper_constraints_file }}{% endif %}"
with_items:
# Install Kolla from source.
- name: "{{ kolla_source_path }}"
Expand All @@ -81,10 +79,6 @@
# Install docker from PyPI since it was dropped from kolla requirements.
# Skip 7.0.0 due to: https://github.com/docker/docker-py/issues/3195
- name: "docker!=7.0.0,<8"
# NOTE(wszumski): Workaround for ERROR: ResolutionImpossible when using upper constraints.
# This can be removed once a newer version of docker is released and the version in upper
# constraints has been bumped (>7.0.0).
extra_args: "{{ omit }}"
install: "{{ container_engine == 'docker' }}"
- name: "podman"
install: "{{ container_engine == 'podman' }}"
Expand Down
80 changes: 63 additions & 17 deletions ansible/roles/network-nmstate/library/nmstate_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# under the License.

import importlib
import json

from ansible.module_utils.basic import AnsibleModule

Expand All @@ -25,7 +26,8 @@
short_description: Apply network state using nmstate
description:
- "This module allows applying a network state using nmstate library.
Provides idempotency by comparing desired and current states."
Provides idempotency by comparing desired and current states.
Supports check and diff modes."
options:
state:
description:
Expand All @@ -39,7 +41,7 @@
default: False
type: bool
requirements:
- libnmstate
- libnmstate (nmstate 2.x)
"""

EXAMPLES = """
Expand All @@ -66,7 +68,15 @@
state:
description: Current network state after applying desired state
type: dict
returned: always
returned: when not in check mode
differences:
description: Computed differences between the current and desired states
type: dict
returned: when changed
diff:
description: Prepared diff of the computed differences
type: dict
returned: when changed
previous_state:
description: Network state before applying (when debug=true)
type: dict
Expand All @@ -78,6 +88,14 @@
"""


def _is_empty(value):
if isinstance(value, dict):
return all(_is_empty(item) for item in value.values())
if isinstance(value, (list, tuple)):
return all(_is_empty(item) for item in value)
return value is None


def run_module():
argument_spec = dict(
state=dict(required=True, type="dict"),
Expand All @@ -86,7 +104,7 @@ def run_module():

module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=False,
supports_check_mode=True,
)

try:
Expand All @@ -101,26 +119,54 @@ def run_module():
) % repr(e)
)

previous_state = libnmstate.show()
if not hasattr(libnmstate, "generate_differences"):
module.fail_json(
msg=(
"The installed libnmstate does not provide "
"generate_differences(). The nmstate_apply module requires "
"nmstate 2.x (for example python3-libnmstate 2.x)."
)
)

current_state = libnmstate.show()
desired_state = module.params["state"]
debug = module.params["debug"]

result = {"changed": False}

differences = libnmstate.generate_differences(
desired_state, current_state)
changed = not _is_empty(differences)

result = {"changed": changed}
if changed:
# "prepared" is a special Ansible diff key whose content is
# printed as-is, keeping --diff output to the changed properties.
if module._diff:
result["diff"] = {
"prepared": json.dumps(
differences, indent=1, sort_keys=True),
}
result["differences"] = differences

if debug:
result["previous_state"] = current_state
result["desired_state"] = desired_state

if module.check_mode:
module.exit_json(**result)
return

# generate_differences() compares against the runtime state, not
# the config persisted by NetworkManager, so apply unconditionally
# to re-persist the config and prevent drift after a reboot.
try:
libnmstate.apply(desired_state)
except Exception as e:
module.fail_json(msg="Failed to apply nmstate state: %s" % repr(e))

current_state = libnmstate.show()

if current_state != previous_state:
result["changed"] = True
if debug:
result["previous_state"] = previous_state
result["desired_state"] = desired_state
# A failed apply must not claim changed: nmstate verifies the
# applied state and rolls back on failure.
module.fail_json(msg="Failed to apply nmstate state: %s" % repr(e),
differences=differences)

result["state"] = current_state
result["state"] = libnmstate.show()

module.exit_json(**result)

Expand Down
Loading
Loading