From 03053e62b8eea70c44c3d1143692b0d467eb7640 Mon Sep 17 00:00:00 2001
From: Michal Nasiadka <mnasiadka@gmail.com>
Date: Thu, 28 Mar 2024 12:47:50 +0100
Subject: [PATCH] Add support for Cumulus NVUE switches

Co-Authored-By: Mark Goddard <mark@stackhpc.com>

Change-Id: I900e3000e492b4eb668815ee6789c9d6e3637c68
---
 ansible/physical-network.yml                  | 12 +++
 ansible/roles/nvue-switch/README.md           | 73 +++++++++++++++++++
 ansible/roles/nvue-switch/defaults/main.yml   | 11 +++
 ansible/roles/nvue-switch/tasks/main.yml      |  7 ++
 .../nvue-switch/templates/nvue-config.j2      | 12 +++
 .../reference/physical-network.rst            | 15 +++-
 .../cumulus-nvue-switch-9f52d701f6c49712.yaml |  7 ++
 requirements.yml                              |  2 +
 8 files changed, 137 insertions(+), 2 deletions(-)
 create mode 100644 ansible/roles/nvue-switch/README.md
 create mode 100644 ansible/roles/nvue-switch/defaults/main.yml
 create mode 100644 ansible/roles/nvue-switch/tasks/main.yml
 create mode 100644 ansible/roles/nvue-switch/templates/nvue-config.j2
 create mode 100644 releasenotes/notes/cumulus-nvue-switch-9f52d701f6c49712.yaml

diff --git a/ansible/physical-network.yml b/ansible/physical-network.yml
index 45600e5d..c00eb642 100644
--- a/ansible/physical-network.yml
+++ b/ansible/physical-network.yml
@@ -36,6 +36,7 @@
       - junos
       - mellanox
       - nclu
+      - nvue
       - openvswitch
   tasks:
     - name: Fail if both interface name and description limits are specified
@@ -202,3 +203,14 @@
     - role: nclu-switch
       nclu_switch_config: "{{ switch_config }}"
       nclu_switch_interface_config: "{{ switch_interface_config }}"
+
+- name: Ensure Cumulus physical switches are configured with NVUE
+  hosts: switches_of_type_nvue:&switches_in_display_mode_False
+  gather_facts: no
+  roles:
+    - role: ssh-known-host
+
+    - role: nvue-switch
+      nvue_switch_config: "{{ switch_config }}"
+      nvue_switch_interface_config: "{{ switch_interface_config }}"
+      nvue_switch_save: "{{ switch_config_save }}"
diff --git a/ansible/roles/nvue-switch/README.md b/ansible/roles/nvue-switch/README.md
new file mode 100644
index 00000000..229f4d5c
--- /dev/null
+++ b/ansible/roles/nvue-switch/README.md
@@ -0,0 +1,73 @@
+NVUE Switch
+===========
+
+This role configures Cumulus switches using the `nvidia.nvue.command` Ansible
+module. It provides a fairly minimal abstraction of the configuration interface
+provided by the `nvidia.nvue.command` module, allowing for application of
+arbitrary switch configuration options.
+
+Requirements
+------------
+
+The switches should be configured to allow SSH access.
+
+Role Variables
+--------------
+
+`nvue_switch_config` is a list of NVUE commands to apply to the switch, and
+defaults to an empty list. Commands must be formatted without the `nv` prefix,
+which is added by the `nvidia.nvue.command` module before execution on the
+switch.
+
+`nvue_switch_interface_config` contains interface configuration. It is a dict
+mapping switch interface names to configuration dicts. Interfaces can be switch
+physical interfaces, but also special interfaces such as bridges or bonds. Each
+dict may contain the following items:
+
+- `description` - a description to apply to the interface.
+- `config` - a list of per-interface configuration, each applied with a `nv
+  set interface <interface-name>` prefix.
+
+Dependencies
+------------
+
+None
+
+Example Playbook
+----------------
+
+The following playbook configures hosts in the `nvue-switches` group. It
+applies global configuration to configure a BGP AS and add two EBGP neighbors
+using BGP Unnumbered, enables two host interfaces with jumbo frames, and
+attaches them to a traditional bridge called `bridge1` configured with an IP
+address.
+
+    ---
+    - name: Ensure Cumulus switches are configured with NVUE
+      hosts: nvue-switches
+      gather_facts: no
+      roles:
+        - role: nvue-switch
+          nvue_switch_config:
+            - "set router bgp autonomous-system 65000"
+            - "set router bgp neighbor swp51 interface remote-as external"
+            - "set router bgp neighbor swp52 interface remote-as external"
+          nvue_switch_interface_config:
+            swp1:
+              description: server1
+              config:
+                - "link mtu 9000"
+            swp2:
+              description: server2
+              config:
+                - "link mtu 9000"
+            bridge1:
+              config:
+                - "ip address 10.100.100.1/24"
+                - "ports swp1"
+                - "ports swp2"
+
+Author Information
+------------------
+
+- Michal Nasiadka (<mnasiadka@gmail.com>)
diff --git a/ansible/roles/nvue-switch/defaults/main.yml b/ansible/roles/nvue-switch/defaults/main.yml
new file mode 100644
index 00000000..3bdb3fd1
--- /dev/null
+++ b/ansible/roles/nvue-switch/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# List of configuration lines to apply to the switch.
+nvue_switch_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.
+nvue_switch_interface_config: {}
+
+# Whether to save the NVUE configuration to disk.
+nvue_switch_save: false
diff --git a/ansible/roles/nvue-switch/tasks/main.yml b/ansible/roles/nvue-switch/tasks/main.yml
new file mode 100644
index 00000000..dc65697c
--- /dev/null
+++ b/ansible/roles/nvue-switch/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Ensure Cumulus switches are configured with NVUE
+  nvidia.nvue.command:
+    assume_yes: true
+    atomic: true
+    save: "{{ nvue_switch_save | bool }}"
+    template: "{{ lookup('template', 'nvue-config.j2') }}"
diff --git a/ansible/roles/nvue-switch/templates/nvue-config.j2 b/ansible/roles/nvue-switch/templates/nvue-config.j2
new file mode 100644
index 00000000..05aa09b9
--- /dev/null
+++ b/ansible/roles/nvue-switch/templates/nvue-config.j2
@@ -0,0 +1,12 @@
+#jinja2: trim_blocks: True,lstrip_blocks: True
+{% for line in nvue_switch_config %}
+{{ line }}
+{% endfor %}
+{% for interface, config in nvue_switch_interface_config.items() %}
+{% for line in config.config %}
+set interface {{ interface }} {{ line }}
+{% endfor %}
+{% if config.description is defined %}
+set interface {{ interface }} description {{ config.description }}
+{% endif %}
+{% endfor %}
diff --git a/doc/source/configuration/reference/physical-network.rst b/doc/source/configuration/reference/physical-network.rst
index a6656cc3..96b0ad65 100644
--- a/doc/source/configuration/reference/physical-network.rst
+++ b/doc/source/configuration/reference/physical-network.rst
@@ -17,6 +17,8 @@ The following switch operating systems are currently supported:
 * Arista EOS
 * Cumulus Linux (via `Network Command Line Utility (NCLU)
   <https://docs.nvidia.com/networking-ethernet-software/cumulus-linux-44/System-Configuration/Network-Command-Line-Utility-NCLU/>`__)
+* Cumulus Linux (via `NVIDIA User Experience command line utility (NVUE)
+  <https://docs.nvidia.com/networking-ethernet-software/cumulus-linux/System-Configuration/NVIDIA-User-Experience-NVUE/>`__)
 * Dell OS 6
 * Dell OS 9
 * Dell OS 10
@@ -211,11 +213,20 @@ Configuration for these devices is applied using the ``nclu`` Ansible module.
 
 ``switch_type`` should be set to ``nclu``.
 
+Cumulus Linux (with NVUE)
+-------------------------
+
+Configuration for these devices is applied using the ``nvidia.nvue.command``
+Ansible module.
+
+``switch_type`` should be set to ``nvue``.
+
 SSH configuration
 ^^^^^^^^^^^^^^^^^
 
-As with any non-switch host in the inventory, the ``nclu`` module relies on the
-default connection parameters used by Ansible:
+As with any non-switch host in the inventory, the ``nclu`` and
+``nvidia.nvue.command`` modules rely on the default connection parameters used
+by Ansible:
 
 * ``ansible_host`` is the hostname or IP address.  Optional.
 
diff --git a/releasenotes/notes/cumulus-nvue-switch-9f52d701f6c49712.yaml b/releasenotes/notes/cumulus-nvue-switch-9f52d701f6c49712.yaml
new file mode 100644
index 00000000..6a481c90
--- /dev/null
+++ b/releasenotes/notes/cumulus-nvue-switch-9f52d701f6c49712.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Adds support for configuring Cumulus switches using the `NVIDIA User
+    Experience command line utility (NVUE)
+    <https://docs.nvidia.com/networking-ethernet-software/cumulus-linux/System-Configuration/NVIDIA-User-Experience-NVUE/>`__.
+    This is integrated with the ``kayobe physical network configure`` command.
diff --git a/requirements.yml b/requirements.yml
index 1221b56d..d8937217 100644
--- a/requirements.yml
+++ b/requirements.yml
@@ -5,6 +5,8 @@ collections:
     version: stable/2024.1
   - name: dellemc.os10
     version: 1.1.1
+  - name: nvidia.nvue
+    version: 1.2.0
   - name: openstack.cloud
     version: '<3'
   - name: stackhpc.linux
-- 
GitLab