Automating Network Design in NVIDIA Air with Ansible and Git - 夜莺博客

Automating Network Design in NVIDIA Air with Ansible and Git

NVIDIA Air is a free cloud-based network simulation platform where every part of the network — topology, config, validation — can be coded, versioned and automated. The intended workflow is Infrastructure as Code: store your switch configs in Git, deploy them with Ansible from the built-in management server, and validate the result. This article explains the building blocks: why manual CLI config does not scale, what goes into your inventory and playbooks, and how to use the oob-mgmt-server as the automation jumpbox.

Why Manual Configuration Does Not Scale

Bringing up a simple CLOS network — one spine, two Cumulus Linux or SONiC leaves, two Ubuntu servers — takes a hundred or so CLI commands. Multiply that by 10, 50 or 100 nodes and it is simply not feasible by hand. The fix is to treat configuration as data: store it in files (NVUE YAML for Cumulus Linux, config_db.json for SONiC, FRR configs, ZTP scripts) and apply it automatically.

The scale argument is really an argument about repeatability. A single engineer can type a VLAN correctly on a single switch. That same engineer, at 2 a.m., typing two hundred commands across forty devices, will make a mistake — and unlike a script, a human error is not reproducible, which makes it hard to find. Code that is versioned can be reviewed, diffed, tested in a simulator, and rolled back. NVIDIA Air gives you a free place to do exactly that: a disposable, full-fidelity copy of the network where a broken change costs you nothing but a simulation reset.

Version Control with Git

GitLab or GitHub is the recommended external home for your configuration files — most NVIDIA Air demo simulations store their configs in a Git repo. You keep full version history and rollback ability while treating the simulated network exactly like a physical one.

A layout that works well mirrors the network's structure rather than the automation tool's:

network-as-code/
  inventories/
    air-clos-demo/hosts.ini
  group_vars/
    leaves.yml
    spines.yml
  host_vars/
    leaf01.yml
    leaf02.yml
  playbooks/
    deploy.yml
    validate.yml
  templates/
    nvue.j2
    frr.j2

Group variables hold what all leaves share, host variables hold what is unique to a device, and templates hold the shape of the configuration. Nothing device-specific belongs in a playbook. When a change is needed, it should show up as a one-line diff in a host variable or a group variable — if a change requires editing the playbook itself, the data model is probably wrong.

Commit discipline matters too. Write commit messages that describe the intent of the change rather than the mechanics, tag known-good states before maintenance windows, and use branches so that a new topology variant can be simulated without disturbing the baseline. In a lab, a branch is free; in production, branching discipline is what makes rollback a two-minute operation.

Ansible for Deployment and Validation

Ansible is agentless — it connects over SSH, needs no daemons or databases, and lets you deploy configuration to different operating systems in the same network from one playbook run:

ansible-playbook -i {inventory} playbooks/{playbook}

You define managed nodes and credentials in an inventory file and automation steps in YAML playbooks.

A minimal inventory for an Air simulation keeps the out-of-band network's reachability explicit, because that is how the control node talks to every switch and server:

[spines]
spine01 ansible_host=10.0.0.11
spine02 ansible_host=10.0.0.12

[leaves]
leaf01 ansible_host=10.0.0.21
leaf02 ansible_host=10.0.0.22

[switches:children]
spines
leaves

[all:vars]
ansible_user=cumulus
ansible_ssh_pass=CumulusLinux!
ansible_network_os=cumulus
ansible_connection=ssh

The connection type is the detail that trips people up first. For Cumulus Linux and SONiC, the devices run a full Linux userland, so Ansible can use the ordinary ssh connection and the shell or command modules. That is simpler than the network-device connection plugins used for classical CLI operating systems, and it means you can push files with the copy module and restart services with systemd, not just issue configuration commands.

A first playbook that pushes a file and validates it looks like this:

- name: Deploy leaf configuration
  hosts: leaves
  gather_facts: false
  tasks:
    - name: Push NVUE configuration
      ansible.builtin.copy:
        src: "configs/{{ inventory_hostname }}.yaml"
        dest: /tmp/{{ inventory_hostname }}.yaml
        mode: "0644"

    - name: Apply the configuration with NVUE
      ansible.builtin.command:
        cmd: nv config replace /tmp/{{ inventory_hostname }}.yaml
      changed_when: true

    - name: Save the applied configuration
      ansible.builtin.command:
        cmd: nv config apply -y
      changed_when: true

Two habits make this reliable. First, make tasks idempotent where the platform allows it — a rerun should not produce a spurious change. Second, separate deployment from validation into two playbooks, so that the validation run is read-only and safe to execute against a network you are not allowed to modify. Read-only validation playbooks are the ones you will actually run on every change, and they are the ones that catch drift.

Validating What You Deployed

Deploying configuration is half the job; proving the network works is the other half. Air simulations ship with an out-of-band management network, which is exactly the path a validation playbook should use so that its traffic never competes with the data plane it is testing.

ansible switches -i inventories/air-clos-demo/hosts.ini -m shell \
  -a "ip -brief addr show | grep -c UP"

ansible leaves -i inventories/air-clos-demo/hosts.ini -m shell \
  -a "vtysh -c 'show ip bgp summary'"

ansible servers -i inventories/air-clos-demo/hosts.ini -m shell \
  -a "ping -c 3 10.10.10.2"

Ad-hoc commands work well for spot checks, but anything you expect to run more than twice belongs in a playbook with assertions, so that the result is a pass or fail rather than a wall of text someone has to read:

- name: Validate BGP sessions are Established
  hosts: leaves
  gather_facts: false
  tasks:
    - name: Collect BGP summary
      ansible.builtin.command:
        cmd: vtysh -c "show ip bgp summary json"
      register: bgp
      changed_when: false

    - name: Assert every neighbour is Established
      ansible.builtin.assert:
        that:
          - (bgp.stdout | from_json).ipv4Unicast.peers.values()
            | selectattr('state', 'ne', 'Established') | list | length == 0
        success_msg: "All BGP sessions Established"
        fail_msg: "At least one BGP session is down"

This is the pattern that turns a simulation into a test harness. Once assertions exist, a topology change can be validated the same way a software change is: run the playbook, look at the result, and let the pipeline decide whether the change is good.

The oob-mgmt-server Jumpbox

Every NVIDIA Air simulation with the out-of-band management network enabled gets an oob-mgmt-server Ubuntu node. It is connected to all other nodes on the OOB network, has a built-in outbound connection, and comes with Ansible and Git preinstalled — the ideal place to run your playbooks. Copy your scripts and configs in via scp (which also mimics the file-transfer workflow of a real air-gapped environment), then run the playbook from there.

Using the jumpbox rather than your laptop has real advantages. Every node is directly reachable over a flat management network with no NAT and no VPN in the path, so inventory addresses stay simple. The tooling version is the one NVIDIA ships, which removes "works on my machine" drift. And because the jumpbox is itself part of the simulation, you can destroy and rebuild the entire environment — jumpbox included — and know that the automation is reproducible from zero.

A typical session looks like this:

# On your workstation
export SIM_HOST=nvidia@
scp -r network-as-code/ $SIM_HOST:~/

# On the jumpbox
ssh nvidia@
cd ~/network-as-code
git init && git add -A && git commit -m "Baseline CLOS configs"
ansible-playbook -i inventories/air-clos-demo/hosts.ini playbooks/deploy.yml
ansible-playbook -i inventories/air-clos-demo/hosts.ini playbooks/validate.yml

If the simulation has outbound internet access, you can also clone the repository directly from GitHub or GitLab on the jumpbox, which is closer to the real workflow and keeps the diff history in one place. Where the simulation has no outbound access, git bundle is a neat way to move a full repository across as a single file.

Cumulus Linux and SONiC in the Same Run

One of the more useful properties of Air is that a single simulation can contain both Cumulus Linux and SONiC nodes, plus Ubuntu hosts. That lets you exercise a heterogeneous inventory with one set of playbooks, dispatching on the operating system rather than duplicating automation per vendor.

- name: Configure SONiC switches
  hosts: sonic
  gather_facts: false
  tasks:
    - name: Push config_db.json
      ansible.builtin.copy:
        src: "sonic/{{ inventory_hostname }}-config_db.json"
        dest: /etc/sonic/config_db.json
    - name: Reload configuration
      ansible.builtin.command:
        cmd: config reload -y

In practice this means your inventory groups do the vendor selection, and each group carries the small amount of platform-specific logic it needs. The bulk of the automation — transport, credential handling, validation, reporting — is shared.

Inventory Hygiene and Handling Secrets

Two things derail automated rollouts more often than any technical limitation, and both are preventable. The first is inventory drift: a switch is added to the simulation but never added to hosts.ini, so it keeps a default configuration and quietly breaks validation. The second is secrets in plain text. Committing an SSH password or an API key to the same repository that holds your configuration is a straightforward security failure, and it survives in history even after you delete the line.

The practical fix for both is to generate the inventory rather than hand-maintain it, and to keep credentials out of the repository entirely. Air makes this easier than production because the management network topology is known and stable — a small script that reads the simulation's node list and writes the inventory file keeps the two in sync.

ansible-inventory -i inventories/air-clos-demo/hosts.ini --list

# Credentials from the environment, not from the repo
export ANSIBLE_HOST_KEY_CHECKING=False
ansible-playbook -i inventories/air-clos-demo/hosts.ini playbooks/deploy.yml   -e "ansible_ssh_pass=$LAB_PASSWORD"

In a real deployment the same pattern is served by Ansible Vault or by an external secret store that the playbook reads at run time. Establish the habit in Air, where a leaked lab password costs nothing, so that it is already muscle memory before the same playbook touches production hardware. Add a .gitignore for vault password files and local inventories on day one, not after the first near miss.

Making It a Habit

The workflow NVIDIA is pointing at is not really about Air at all; Air is the sandbox where the habit is cheap to build. The habit is: write the configuration as data, keep it in Git, deploy it with a playbook, and validate it with assertions. Do that in a free simulation for a few weeks and the jump to production hardware is mostly a matter of changing addresses in an inventory file.

Getting started is free at air.nvidia.com — try the Cumulus Linux Workshop or SONiC Workshop labs for a guided walkthrough. See also our Ansible network automation complete guide for the wider toolchain, the Ansible Jinja2 templating guide for generating configs from a single source of truth, and for the SONiC side of a mixed inventory, see SONiC config_db.json: save, reload and replace safely and SONiC installation and configuration.

原文链接:https://developer.nvidia.com/blog/automating-network-design-in-nvidia-air-with-ansible-and-git/