In this article, I will first show you three examples on how you can use SaltStack to send a CLI command to a device. As we will see, different proxy minion types have their own function that you can use to send a command to a device.
After this, I’ll cover an example where we do the same thing using a single custom execution module function. We’ll use the example function to send a command to devices controlled by a netmiko, napalm or junos proxy minion.
The Netmiko proxy minion.
Netmiko uses the execution module called netmiko
. The function to have the proxy minion send a command to a device is called send_command
(same as when you use Netmiko outside of Salt).
To issue a command to a device called lab-netmiko-eos
, we do the following:
/ $ salt lab-netmiko-eos netmiko.send_command 'show version'
lab-netmiko-eos:
Arista DCS-7050TX-64-R
Hardware version: 01.01
Serial number: JPE14383000
System MAC address: 001c.739f.9b25
Software image version: 4.18.2.1F
Architecture: i386
Internal build version: 4.18.2.1F-5528501.41821F
Internal build ID: 171293b2-b2a9-40e2-a2a8-5e1fcab52f49
Uptime: 2 weeks, 3 days, 7 hours and 5 minutes
Total memory: 3569792 kB
Free memory: 1355716 kB
The NAPALM proxy minion.
Sending a command to a device called lab-napalm-eos
, which is managed using a NAPALM proxy minion, is done using net.cli
:
/ $ salt lab-napalm-eos net.cli 'show version'
lab-napalm-eos:
----------
comment:
out:
----------
show version:
Arista DCS-7280TR-48C6-R
Hardware version: 01.00
Serial number: JPE16161500
System MAC address: 444c.a8a5.359d
Software image version: 4.18.0F
Architecture: i386
Internal build version: 4.18.0F-4224134.4180F
Internal build ID: 54acf3af-762b-4cc0-83b7-3b2060a5c8c1
Uptime: 87 weeks, 6 days, 11 hours and 28 minutes
Total memory: 7760844 kB
Free memory: 5275120 kB
result:
True
As you can see, where the Netmiko proxy minion simply return a string, NAPALM returns a dictionary.
Side note: the NAPALM proxy minion uses the API to send the command to the device. So if you want to use NAPALM to manage an Arista device for instance, you’ll have to enable the API on the device.
The Junos proxy minion.
For the Juniper proxy minion, we use junos.cli
to issue a command to a device called lab-junos
:
/ $ salt lab-junos junos.cli 'show version'
lab-junos:
----------
message:
Hostname: ar02.mx
Model: mx240
Junos: 16.1R3-S8
< output omitted >
out:
True
Same as with NAPALM, the return is a dictionary and the proxy minion uses the API to send a command to the device.
The custom execution module function.
Let’s create a function in an execution module that can work with any proxy minion type. To be able to send commands to different proxy minion types, we would have to ensure that the function will:
- Check what proxy minion type it is dealing with
- Use the proper execution module to interface with the proxy minion
- Handle the output from the different proxy minions
Let’s go over the following example common.py
execution module that does just that:
def cli(command):
proxytype = __pillar__.get('proxy').get('proxytype')
send_command = {
'netmiko': 'netmiko.send_command',
'napalm': 'net.cli',
'junos' : 'junos.cli',
}
device_output = __salt__[send_command[proxytype]](command)
if proxytype == 'napalm':
return device_output['out'].get(command, None)
elif proxytype == 'junos':
return device_output.get('message', None)
elif proxytype == 'netmiko':
return device_output
The function starts out checking the proxy minion type by looking at the pillar:
proxytype = __pillar__.get('proxy').get('proxytype')
Next we see a dictionary that is used as a switch statement:
send_command = {
'netmiko': 'netmiko.send_command',
'napalm': 'net.cli',
'junos' : 'junos.cli',
}
We use this dictionary to check what execution module we should use in order to send a command to the proxy minion.
In this function, we want to be able to send a command to a NAPALM, Netmiko or Junos proxy minion. The following makes that possible:
device_output = __salt__[send_command[proxytype]](command)
We use __salt__
because we will be calling another salt execution module. Since we need to be able to call the execution module that is relevant to the proxy minion we are dealing with, we do a lookup in the send_command
dictionary. The lookup, send_command[proxytype]
, will ensure we use to the proper function for each proxy minion type.
The output we get in return is stored inside device_output
. Now, the only thing left is dealing with the different return values:
if proxytype == 'napalm':
return device_output['out'].get(command, None)
elif proxytype == 'junos':
return device_output.get('message', None)
elif proxytype == 'netmiko':
return device_output
For the NAPALM and Juniper proxy minion, we look for proper key in the return value. In case we are dealing with the Netmiko proxy minion, we simply return the string.
Testing our custom execution module.
We name the execution module common.py
and we place it in /srv/salt/_modules/
. After this, we sync it to all of the proxy minions using salt \* saltutil.sync_all
.
Now that the proxy minions have access to this new execution module, we are ready to check out the new function:
/ $ salt lab-junos common.cli 'show version'
lab-junos:
Hostname: ar02.mx
Model: mx240
Junos: 16.1R3-S8
< output omitted >
/ $ salt lab-napalm-eos common.cli 'show version'
lab-napalm-eos:
Arista DCS-7280TR-48C6-R
Hardware version: 01.00
Serial number: JPE16161500
System MAC address: 444c.a8a5.359d
Software image version: 4.18.0F
Architecture: i386
Internal build version: 4.18.0F-4224134.4180F
Internal build ID: 54acf3af-762b-4cc0-83b7-3b2060a5c8c1
Uptime: 87 weeks, 6 days, 11 hours and 55 minutes
Total memory: 7760844 kB
Free memory: 5275832 kB
/ $ salt lab-netmiko-eos common.cli 'show version'
lab-netmiko-eos:
Arista DCS-7050TX-64-R
Hardware version: 01.01
Serial number: JPE14383000
System MAC address: 001c.739f.9b25
Software image version: 4.18.2.1F
Architecture: i386
Internal build version: 4.18.2.1F-5528501.41821F
Internal build ID: 171293b2-b2a9-40e2-a2a8-5e1fcab52f49
Uptime: 2 weeks, 3 days, 7 hours and 34 minutes
Total memory: 3569792 kB
Free memory: 1398988 kB
Wrapping up:
Not only does this makes it easier for people who want to gather information from the Salt CLI, it can also simplify work in other custom execution modules. In other custom execution modules, you can call the function discussed here like this:
cmd_output = __salt__['common.cli'](command)
You can now start working with the string inside cmd_output
and, for instance, extract information from CLI output using regex, textfsm or do whatever else you can think of.