SaltStack for Network Device Configuration Management - 夜莺博客

SaltStack for Network Device Configuration Management

SaltStack manages network devices through proxy minions: a lightweight process per device that speaks the vendor's transport (NETCONF, SSH, NAPALM) while the master keeps the usual Salt command model. For teams that need imperative push at scale plus declarative state, it fills a gap that Ansible's per-run connection model and Nornir's pure-Python approach both leave open. This guide covers the proxy setup, the modules worth using, and a first state-driven configuration push.

Architecture in practice

  • Salt master — holds pillar data (per-device credentials and variables) and the state tree.
  • Proxy minion — one process per device, running on the master or a dedicated proxy host.
  • Execution modules — napalm, junos, netconfig, ntp, bgp and friends, selected by the proxy's proxytype.
# /etc/salt/proxy  (on the proxy host)
master: salt-master.example.net
id: core-sw1
# /etc/salt/pillar/core-sw1.sls
proxy:
  proxytype: napalm
  driver: ios
  host: 10.10.10.11
  username: automation
  password: '{{ salt['vault'].read_secret('net/automation', 'password') }}'
  optional_args:
    secret: '<enable-secret>'
    port: 22

Starting proxies and verifying reachability

salt-proxy --proxyid=core-sw1 -l info --daemon
salt 'core-sw1' test.ping
salt 'core-sw1' napalm.get_facts
salt 'core-sw1' net.config
salt 'core-sw1' net.cli "show ip interface brief"

test.ping returning True proves only that the proxy process is alive; always confirm with a device-level call such as napalm.get_facts before trusting automation output.

Gathering facts into grains

salt 'core-*' napalm.get_facts
salt 'core-*' grains.items | grep -E "vendor|model|os_version"
# custom grains make targeting reliable
# /srv/salt/_grains/network.py
def network_grains():
    import napalm
    # populate site/role from pillar rather than parsing the device
    return {"network": {"site": "hq", "role": "access"}}

Targeting by parsed model strings is fragile across platforms. Derive site and role from pillar (which is the source of truth) and use grains for the parts only the device knows.

salt -G 'network:role:access' test.ping

A first declarative state

# /srv/salt/ntp.sls
ntp_servers:
  netconfig.managed:
    - template_name: salt://templates/ntp.jinja
    - debug: true

# /srv/salt/templates/ntp.jinja
{%- set servers = pillar.get('ntp_servers', ['10.10.10.53']) %}
ntp server {{ servers | join(' ') }}
# always diff before committing
salt 'core-sw1' state.apply ntp test=True
salt 'core-sw1' state.apply ntp

netconfig.managed renders the template, compares it with the running configuration, and applies only the difference through the proxy. Running with test=True first is the equivalent of a dry run and is the habit that prevents surprise outages.

Where Salt fits best

  • Large fleets where minutes matter and pushes should be parallel across thousands of devices.
  • Environments that want the same tool for servers and network devices.
  • Teams already comfortable with YAML/Jinja and a master-agent trust model; if the estate is small and mostly read-only, Nornir or Ansible will be less to operate.

Related reading: Nornir network automation inventory and tasks, Ansible Jinja2 network configuration templating, and NAPALM getters and configuration diff.

原文链接:https://docs.saltproject.io/en/latest/topics/network_automation/index.html