Example: Using Salt to Configure Devices Running Junos OS
Juniper Networks provides support for using Salt to manage devices running Junos OS. This example uses Salt to configure several devices running Junos OS.
Requirements
This example uses the following hardware and software components:
Three MX Series routers running Junos OS with NETCONF enabled
Salt master with the following requirements:
Salt version 3001 or later
Can ping and perform operations on the devices running Junos OS
Overview
This example defines a Salt state that configures BGP peering sessions on the target devices running Junos OS. Table 1 outlines the device hostnames, proxy IDs, and device-specific files.
Hostname |
Proxy ID |
Proxy Configuration File (/srv/pillar) |
BGP Data File (/srv/pillar/bgp) |
---|---|---|---|
r1 |
r1 |
r1-proxy.sls |
r1.sls |
r2 |
r2 |
r2-proxy.sls |
r2.sls |
r3 |
r3 |
r3-proxy.sls |
r3.sls |
The example uses the following components:
Jinja2 configuration template—Defines the BGP configuration as a Jinja template using
set
command format. The template uses the .set file extension to indicate the format.Pillar files—Define the device-specific configuration data required by the Jinja2 template. The data for each device is stored in a separate file under the /srv/pillar/bgp directory on the Salt master.
Pillar top file—Maps each pillar file to the appropriate proxy minion.
State file—Defines the state to apply to the target devices. In this case, the state file uses the
junos.install_config
function to apply the BGP configuration to a device.State top file—Maps the state file to the devices on which the state should be applied.
In this example, you create a Jinja2 template to generate the BGP configuration that gets loaded and committed on the device. The template substitutes in variables for device-specific configuration data so that it can be reused as needed. The template file is placed under the /srv/salt/configs directory on the Salt master.
You then create separate pillar files for each device and define
the device-specific configuration data in the file. Each pillar file
defines a BGP_data
key that contains all
of the variable parameters that are referenced in the configuration
template. The pillar top file maps each pillar file to its respective
proxy minion. Storing the data in pillars ensures that each proxy
minion can only access the data for that device.
The junos_bgp_config.sls state
file defines a state that uses the junos.install_config
function to apply the BGP configuration in the template. When invoked,
the function locks the configuration database, loads the configuration
specific to the target device, commits the configuration, and then
unlocks the database. The function includes the diffs_file
parameter to store the configuration differences in a file on the
proxy minion server. The filename uses the id
grain to generate device-specific filenames for the output files.
The state top file applies the junos_bgp_config
state to any minion with a BGP_data
pillar
key with any value. When you run a highstate or apply that state to
a device, Salt uses the template and pillar data to generate the configuration
specific to the target and then invokes the junos.install_config
function to load and commit the configuration on the device. If
the configuration is already applied, the junos.install_config
function does not reapply the configuration.
After you configure the devices, you then apply the junos_bgp_verify_peers
state. This state executes the get-bgp-neighbor-information
RPC on each device until
it returns a peer-state
value of Established
for each peer defined in that device’s
pillar data or times out. This state assumes that BGP is running on
the device.
Configuration
To configure the devices using Salt, perform the tasks included in this section.
Define the Pillar Data
Step-by-Step Procedure
To define the pillar data that is used with the Jinja2 template to generate the BGP configuration:
On the Salt master, create a separate file named /srv/pillar/bgp/hostname.sls for each managed device.
Define the data for host r1 in the r1.sls file.
BGP_data: loopback: 192.168.0.1 local_asn: 64521 neighbors: - interface: ge-0/0/0 name: to-r2 asn: 64522 peer_ip: 198.51.100.2 local_ip: 198.51.100.1 peer_loopback: 192.168.0.2 - interface: ge-0/0/1 name: to-r3 asn: 64523 peer_ip: 198.51.100.6 local_ip: 198.51.100.5 peer_loopback: 192.168.0.3
Define the data for host r2 in the r2.sls file.
BGP_data: loopback: 192.168.0.2 local_asn: 64522 neighbors: - interface: ge-0/0/0 name: to-r1 asn: 64521 peer_ip: 198.51.100.1 local_ip: 198.51.100.2 peer_loopback: 192.168.0.1 - interface: ge-0/0/1 name: to-r3 asn: 64523 peer_ip: 198.51.100.10 local_ip: 198.51.100.9 peer_loopback: 192.168.0.3
Define the data for host r3 in the r3.sls file.
BGP_data: loopback: 192.168.0.3 local_asn: 64523 neighbors: - interface: ge-0/0/0 name: to-r1 asn: 64521 peer_ip: 198.51.100.5 local_ip: 198.51.100.6 peer_loopback: 192.168.0.1 - interface: ge-0/0/1 name: to-r2 asn: 64522 peer_ip: 198.51.100.9 local_ip: 198.51.100.10 peer_loopback: 192.168.0.2
In the pillar top file, map the pillar file with the BGP data under the appropriate device ID to enable the device to access the data.
saltuser@salt-master:~$ cat /srv/pillar/top.sls base: 'r1': - r1-proxy - bgp/r1 'r2': - r2-proxy - bgp/r2 'r3': - r3-proxy - bgp/r3
Refresh the pillar data.
saltuser@salt-master:~$ sudo salt '*' saltutil.refresh_pillar r3: True r1: True r2: True
Define the Jinja2 Template
Step-by-Step Procedure
To create the Jinja2 template that is used to generate the BGP configuration:
Create a file named /srv/salt/configs/junos-config-bgp-template.set on the Salt master.
Add the BGP configuration template to the file and save it.
saltuser@salt-master:~$ cat /srv/salt/configs/junos-config-bgp-template.set {% if pillar.BGP_data %} set interfaces lo0 unit 0 family inet address {{ pillar.BGP_data.loopback }}/32 set policy-options policy-statement bgp-ecmp then load-balance per-packet set policy-options policy-statement bgp-in then accept set policy-options policy-statement bgp-out then next-hop self set policy-options policy-statement bgp-out then accept set protocols bgp group underlay type external set protocols bgp group underlay import bgp-in set protocols bgp group underlay export bgp-out set protocols bgp group underlay local-as {{ pillar.BGP_data.local_asn }} set protocols bgp group underlay multipath multiple-as set routing-options router-id {{ pillar.BGP_data.loopback }} set routing-options forwarding-table export bgp-ecmp {% for neighbor in pillar.BGP_data.neighbors %} set interfaces {{ neighbor.interface }} unit 0 description {{ neighbor.name }} set interfaces {{ neighbor.interface }} unit 0 family inet address {{ neighbor.local_ip }}/30 set protocols bgp group underlay neighbor {{ neighbor.peer_ip }} peer-as {{ neighbor.asn }} set protocols lldp interface {{ neighbor.interface }} {% endfor %} {% endif %}
Define the States
Define the Configuration State File
To define the state file that applies the configuration:
Create a file named /srv/salt/junos_bgp_config.sls on the Salt master.
Define a state that uses the
junos.install_config
function to apply the BGP configuration in the template.saltuser@salt-master:~$ cat /srv/salt/junos_bgp_config.sls Apply BGP configuration: junos.install_config: - name: salt://configs/junos-config-bgp-template.set - comment: Configuring BGP using Salt - diffs_file: /var/log/salt/output/{{ grains['id'] }}_junos_bgp_config_diff - template_vars: True
Note:If your template only includes Salt internal variables like pillar data, grain data, and functions, the
junos.install_config
function might need to definetemplate_vars: True
in order to render the template.In the top file, define the targets to which the junos_bgp_config state applies. The targets comprise all devices that have a
BGP_data
pillar item defined.saltuser@salt-master:~$ cat /srv/salt/top.sls base: BGP_data:*: - match: pillar - junos_bgp_config
Define the BGP Verification State File
To define the state that verifies that the BPG peers
have a peer-state
of Established
:
Create a file named /srv/salt/junos_bgp_verify_peers.sls on the Salt master.
Define a state that uses the
junos.rpc
function to execute theget-bgp-neighbor-information
RPC and then checks the RPC reply for apeer-state
ofEstablished
.saltuser@salt-master:~$ cat /srv/salt/junos_bgp_verify_peers.sls {% for peer in pillar['BGP_data']['neighbors'] %} validate_bgp_session_state_with_{{ peer['peer_ip'] }}: loop.until: - name: junos.rpc - condition: m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' - period: 5 - timeout: 20 - m_args: - get-bgp-neighbor-information - m_kwargs: neighbor-address: {{ peer['peer_ip'] }} {% endfor %}
Results
When you execute a highstate, the states in the top.sls
file are applied to the appropriate target devices.
saltuser@salt-master:~$ sudo salt '*' state.apply r1: ---------- ID: Apply BGP configuration Function: junos.install_config Name: salt://configs/junos-config-bgp-template.set Result: True Comment: Started: 07:07:14.449582 Duration: 3379.914 ms Changes: ---------- message: Successfully loaded and committed! out: True Summary for r1 ------------ Succeeded: 1 (changed=1) Failed: 0 ------------ Total states run: 1 Total run time: 3.380 s r2: ---------- ID: Apply BGP configuration Function: junos.install_config Name: salt://configs/junos-config-bgp-template.set Result: True Comment: Started: 07:07:14.485640 Duration: 3132.677 ms Changes: ---------- message: Successfully loaded and committed! out: True Summary for r2 ------------ Succeeded: 1 (changed=1) Failed: 0 ------------ Total states run: 1 Total run time: 3.133 s r3: ---------- ID: Apply BGP configuration Function: junos.install_config Name: salt://configs/junos-config-bgp-template.set Result: True Comment: Started: 07:07:14.388629 Duration: 3431.723 ms Changes: ---------- message: Successfully loaded and committed! out: True Summary for r3 ------------ Succeeded: 1 (changed=1) Failed: 0 ------------ Total states run: 1 Total run time: 3.432 s
Verification
Verifying the BGP Configuration
Purpose
Verify that the BGP session is established for each neighbor address.
Action
Apply the junos_bgp_verify_peers
state to the target devices and review the output.
saltuser@salt-master:~$ sudo salt '*' state.apply junos_bgp_verify_peers r1: ---------- ID: validate_bgp_session_state_with_198.51.100.2 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:01.825414 Duration: 125.241 ms Changes: ---------- ID: validate_bgp_session_state_with_198.51.100.6 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:01.950786 Duration: 148.944 ms Changes: Summary for r1 ------------ Succeeded: 2 Failed: 0 ------------ Total states run: 2 Total run time: 274.185 ms r3: ---------- ID: validate_bgp_session_state_with_198.51.100.5 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:02.849612 Duration: 99.527 ms Changes: ---------- ID: validate_bgp_session_state_with_198.51.100.9 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:02.949265 Duration: 165.041 ms Changes: Summary for r3 ------------ Succeeded: 2 Failed: 0 ------------ Total states run: 2 Total run time: 264.568 ms r2: ---------- ID: validate_bgp_session_state_with_198.51.100.1 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:02.811094 Duration: 143.335 ms Changes: ---------- ID: validate_bgp_session_state_with_198.51.100.10 Function: loop.until Name: junos.rpc Result: True Comment: Condition m_ret['rpc_reply']['bgp-information']['bgp-peer']['peer-state'] == 'Established' was met Started: 07:17:02.954551 Duration: 170.651 ms Changes: Summary for r2 ------------ Succeeded: 2 Failed: 0 ------------ Total states run: 2 Total run time: 313.986 ms
Meaning
The state file requires that the peer-state
is equal to Established
for each peer.
The output indicates that this condition is met for all peers on each
device. Alternatively, you can use the junos.cli
function to execute the show bgp summary
command on the
devices and review the output to verify that the BGP session is established
for each neighbor address.
Troubleshooting
Troubleshooting Configuration Load Errors
Problem
The Salt master generates a ConfigLoadError
error indicating that it failed to load the configuration on the
device because of a syntax error.
message: Could not load configuration due to : "ConfigLoadError(severity: error, bad_element: interface1, message: error: syntax error)"
Solution
Salt renders the Junos OS configuration by using the
Jinja2 template and the pillar data defined for that device. Salt
generates a syntax error when the Jinja2 template produces an invalid
configuration. To correct this error, update the Jinja2 template to
correct the element identified by the bad_element
key in the error message.