Examples
Tasks and runbooks#
Single Task#
# as one comand with config.yaml
$ nornir_cli nornir-netmiko init -u username -p password \
filter --hosts -a 'name__contains=dev_1 device_role__name__contains=leaf' \
netmiko_send_command --command_string "display clock"
[
"dev_1"
]
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:15:23+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# as one comand without config.yaml
$ nornir_cli nornir-netmiko init -u username -p password -c "" -f \
'inventory={"plugin":"NetBoxInventory2", "options": {"nb_url": "your_netbox_domain", \
"nb_token": "your_netbox_token", "ssl_verify": false}} \
runner={"plugin": "threaded", "options": {"num_workers": 50}} \
logging={"enabled":true, "level": "DEBUG", "to_console": true}' \
filter --hosts -a 'name__contains=dev_1 \
device_role__name__contains=leaf' netmiko_send_command --command_string "display clock"
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:20:53+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# as many commands with config.yaml
$ nornir_cli nornir-netmiko init -u username -p password
$ nornir_cli nornir-netmiko filter --hosts -a -s 'name__contains=dev_1 device_role__name__contains=leaf'
[
"dev_1"
]
$ nornir_cli nornir-netmiko netmiko_send_command --command_string "display clock"
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:30:13+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# of course, the same thing can be done without a configuration file
Or the same, but with json string arguments:
# as one comand with config.yaml
$ nornir_cli nornir-netmiko init -u username -p password \
filter --hosts -a 'name__contains=dev_1 device_role__name__contains=leaf' \
netmiko_send_command '{"command_string":"display clock"}'
[
"dev_1"
]
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:15:23+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# as one comand without config.yaml
$ nornir_cli nornir-netmiko init -u username -p password -c "" -f \
'{"inventory":{"plugin":"NetBoxInventory2", "options": {"nb_url": "your_netbox_domain", \
"nb_token": "your_netbox_token", "ssl_verify": false}}, \
"runner":{"plugin": "threaded", "options": {"num_workers": 50}} \
"logging":{"enabled":true, "level": "DEBUG", "to_console": true}}' \
filter --hosts -a 'name__contains=dev_1 \
device_role__name__contains=leaf' netmiko_send_command '{"command_string":"display clock"}'
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:20:53+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# as many commands with config.yaml
$ nornir_cli nornir-netmiko init -u username -p password
$ nornir_cli nornir-netmiko filter --hosts -a -s 'name__contains=dev_1 device_role__name__contains=leaf'
[
"dev_1"
]
$ nornir_cli nornir-netmiko netmiko_send_command '{"command_string":"display clock"}'
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-21 23:30:13+03:00
Sunday
Time Zone(Moscow) : UTC+03:00
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
# of course, the same thing can be done without a configuration file
Task chains#
# any chain of commands in a group/plugin is possible
$ nornir_cli nornir-scrapli init -u username -p password \
-co '{"scrapli": {"platform": "huawei_vrp", "extras":{"ssh_config_file": true}}}' \
filter --hosts -s 'name=dev_1' send_command '{"command":"display clock"}' \
send_interactive '{"interact_events":[["save", "Are you sure to continue?[Y/N]", \
false], ["Y", "Save the configuration successfully.", true]]}'
[
"dev_1"
]
send_command********************************************************************
* dev_1 ** changed : False *****************************************************
vvvv send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
2021-03-19 14:12:38+03:00
Friday
Time Zone(Moscow) : UTC+03:00
^^^^ END send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
send_interactive****************************************************************
* dev_1 ** changed : True ******************************************************
vvvv send_interactive ** changed : True vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
^^^^ END send_interactive ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=1 failed=0
OK : 1
CHANGED : 1
FAILED : 0
Custom Nornir runbooks#
How to add a previously written Nornir runbook in nornir_cli
And here is an example of this runbook(btw, all examples are here):
# nornir_cli/custom_commands/dhcp/cmd_dhcp_snooping.py
import os
from nornir.core.plugins.connections import ConnectionPluginRegister
from nornir_cli.common_commands import custom, print_stat
from nornir_jinja2.plugins.tasks import template_file
from nornir_netmiko import netmiko_send_command, netmiko_send_config
@custom
def cli(ctx):
"""
Configure dhcp snooping
"""
def _get_trusted_untrusted(task):
ConnectionPluginRegister.auto_register()
# Get parameters in format:
# [ { 'description': 'NNI',
# 'mac_address': 'xxxx-yyyy-zzzz',
# 'mtu': '',
# 'name': 'Ethernet0/0/1'},]
intfs = task.run(
task=netmiko_send_command,
name="interfaces list",
command_string="disp int",
use_textfsm=True,
textfsm_template=os.path.join(
os.path.dirname(os.path.abspath(__file__)), "templates/disp_int.template"
),
)
# Get trusted interfaces
task.host["trusted"] = [
i["name"] for i in intfs.result if "NNI" in i["description"]
]
# Get untrusted interfaces
task.host["untrusted"] = [
i["name"]
for i in intfs.result
if "NNI" not in i["description"] and not i["mtu"]
]
# Render j2 template
template = task.run(
task=template_file,
path=os.path.join(
os.path.dirname(os.path.abspath(__file__)), "templates"
),
template="dhcp_snooping.j2",
)
# Configure commands from j2 template
task.host["template"] = template.result
task.run(
task=netmiko_send_config,
name="Configure dhcp snooping",
config_commands=template.result,
cmd_verify=False,
exit_config_mode=False,
)
result = ctx.nornir.run(task=_get_trusted_untrusted, on_failed=True)
# add result to ctx.result for print_result, write_result, write_results
# `netmiko_send_config` doesn't return data, so `result` will be empty
# ctx.result = result
# Show statistic
print_stat(ctx.nornir, result)
# nornir_cli/custom_commands/dhcp/templates/dhcp_snooping.j2
dhcp enable
dhcp snooping enable
#
{% for intf in host.untrusted %}
interface {{ intf }}
dhcp snooping enable no-user-binding
Y
dhcp snooping check dhcp-chaddr enable
{% endfor %}
#
{% for intf in host.trusted %}
interface {{ intf }}
dhcp snooping enable no-user-binding
Y
dhcp snooping trusted
{% endfor %}
#
q
q
save
Y
# nornir_cli/custom_commands/dhcp/templates/disp_int.template
Value NAME (\S+)
Value DESCRIPTION (.*)
Value MAC_ADDRESS (\w+-\w+-\w+)
Value MTU (\d+)
Start
^\S+ current state.* -> Continue.Record
^${NAME} current state.*
^Description:${DESCRIPTION}
^.* Maximum Transmit Unit is ${MTU}
^.* Hardware address is ${MAC_ADDRESS}
Textfsm#
$ nornir_cli nornir-netmiko netmiko_send_command --command_string "disp int" --use_textfsm True --textfsm_template nornir_cli/custom_commands/templates/disp_int.template
netmiko_send_command************************************************************
* dev_1 ** changed : False *****************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
[ { 'description': 'NNI',
'mac_address': 'f898-ef49-b5d0',
'mtu': '',
'name': 'Ethernet0/0/1'},
{ 'description': '',
'mac_address': 'f898-ef49-b5d0',
'mtu': '',
'name': 'Ethernet0/0/2'},
...
]
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
routeros#
nornir_cli
is not compatible with nornir-routeros
since 0.3.0
version. nornir-routeros
has been removed from nornir_cli 1.2.0
.
But starting from nornir_cli
version 1.3.0
, you can pass any additional arguments as a json string to Nornir plugin commands. This feature allows you to run any Nornir plugin command without an unique set of parameters.
config.yaml:
# Simple Nornir configuration file
inventory:
plugin: SimpleInventory
options:
host_file: "inventory/hosts.yaml"
# Single host inventory
dev_1:
hostname: 10.1.2.3
username: username
password: password
port: 8728
connection_options:
routerosapi:
extras:
use_ssl: False
$ nornir_cli nornir-routeros init -c "/home/user/config.yaml" routeros_command '{"path":"/", "command":"ping", "address":"1.1.1.1", "count":4}'
# or
# $ nornir_cli nornir-routeros init -c "/home/user/config.yaml" routeros_command --path "/" --command "ping" '{"address":"1.1.1.1", "count":4}'
routeros_command****************************************************************
* dev_1 ** changed : False *****************************************************
vvvv routeros_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
[ { 'avg-rtt': b'22ms',
'host': b'1.1.1.1',
'max-rtt': b'22ms',
'min-rtt': b'22ms',
'packet-loss': b'0',
'received': b'1',
'sent': b'1',
'seq': b'0',
'size': b'56',
'time': b'22ms',
'ttl': b'56'},
...
^^^^ END routeros_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
nornir-paramiko#
nornir-paramiko
does not work correctly with network devices, because it uses exec_command
(see here) instead of send
and recv
. It works correctly with linux/unix
:
# Simple Nornir configuration file
inventory:
plugin: SimpleInventory
options:
host_file: "inventory/hosts.yaml"
# Single host inventory
host_1:
hostname: 10.3.2.1
username: username
password: password
connection_options:
paramiko:
extras:
allow_agent: False
look_for_keys: False
$ nornir_cli nornir-paramiko init paramiko_command --command "pwd"
paramiko_command****************************************************************
* host_1 ** changed : False ****************************************************
vvvv paramiko_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
/home/user
^^^^ END paramiko_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
host_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
nornir-pyxl#
As instance, Untitled.xlsx
file has single sheet - Sheet1
# Simple Nornir configuration file
inventory:
plugin: SimpleInventory
options:
host_file: "inventory/hosts.yaml"
# Single host inventory
dev_1:
hostname: 10.1.2.3
username: username
password: password
SITE_ID CLLI SYSTEM NAME NTP SERVER 1IP NTP SERVER 2IP NTP SERVER 3IP NTP SERVER 4IP
Q345501 PHNZAZ PHNZAZ-63569 192.168.1.100 192.168.1.102/32 192.168.100.3 time.ntp.com
nornir_cli nornir-pyxl init pyxl_ez_data --workbook Untitled.xlsx --sheetname Sheet1
pyxl_ez_data********************************************************************
* dev_1 ** changed : False *****************************************************
vvvv pyxl_ez_data ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
[ { 'clli': 'PHNZAZ',
'ntp_server_1ip': '192.168.1.100',
'ntp_server_2ip': '192.168.1.102/32',
'ntp_server_3ip': '192.168.100.3',
'ntp_server_4ip': 'time.ntp.com',
'site_id': 'Q345501',
'system_name': 'PHNZAZ-63569'}]
^^^^ END pyxl_ez_data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dev_1 : ok=1 changed=0 failed=0
OK : 1
CHANGED : 0
FAILED : 0
nornir-pyxl
includes pyxl_map_data
command, but it does not work int nornir_cli
and was added to command exceptions
--help#
The help option can be used anywhere, for example:
$ nornir_cli nornir-netmiko init -u username -p password filter \
--hosts -a -s 'name__contains=dev_1 device_role__name__contains=leaf' \
netmiko_save_config --help
Usage: nornir_cli nornir-netmiko netmiko_save_config [OPTIONS] [ARGUMENTS]
Execute Netmiko save_config method
Arguments: cmd(str, optional): Command used to save the configuration.
confirm(bool, optional): Does device prompt for confirmation before
executing save operation confirm_response(str, optional): Response send
to device when it prompts for confirmation
Returns: nornir Result attributes(optional), statistic, progress
bar(optional)
Options:
--pg_bar Progress bar flag
--print_result / --no_print_result
print_result from nornir_utils [default:
print_result]
--print_stat / --no_print_stat Print Result statistic for Nornir object
[default: print_stat]
--cmd TEXT [default: ]
--confirm BOOLEAN [default: False]
--confirm_response TEXT [default: ]
--help Show this message and exit.
$ nornir_cli nornir-netmiko init --help -u username -p password \
filter --hosts -a -s 'name__contains=dev_1 device_role__name__contains=leaf' \
netmiko_save_config
Usage: nornir_cli nornir-netmiko init [OPTIONS]
Initialize nornir with a configuration file, with code or with a combination
of both.
Options:
-c, --config_file PATH Path to configuration file [default:
config.yaml]
-f, --from_dict TEXT InitNornir dictionary arguments (json
string)
-co, --connection_options TEXT Specify any connection parameters (json
string)
-d, --dry_run BOOLEAN Whether to simulate changes or not
[default: False]
-u, --username TEXT Default username
-p, --password TEXT Default password
-cou, --count INTEGER Number of elements you want to show
-g, --groups Show groups list
-h, --hosts Show hosts list
-i, --inventory [hosts|groups|defaults|all]
Show hosts, groups or defaults inventory
--help Show this message and exit.
$ nornir_cli nornir-netmiko init -u username -p password filter --help \
--hosts -a -s 'name__contains=dev_1 device_role__name__contains=leaf' \
netmiko_save_config
Usage: nornir_cli nornir-netmiko filter [OPTIONS] [F]
Do simple or advanced filtering that will enable us to operate on groups of
hosts based on their properties.
Options:
-a, --advanced_filter Use an advanced filtering (string)
-cou, --count INTEGER Number of elements you want to show
-g, --groups Show groups list
-h, --hosts Show hosts list
-i, --inventory [hosts|groups|defaults|all]
Show hosts, groups or defaults inventory
-s, --save Save filtered Nornir object to pickle file
for later use
--help Show this message and exit.
Logging#
By default, Nornir logs to a nornir.log
file.
For logging to console configure logging
parameter in config.yaml
or do init
from dictionary, as instance:
$ nornir_cli nornir-netmiko init -c "" -f 'inventory={"plugin":"NetBoxInventory2", \
"options": {"nb_url": "http://your_netbox_domain", "nb_token": "your_netbox_token", \
"ssl_verify": false}} runner={"plugin": "threaded", "options": {"num_workers": 50}} \
logging={"enabled":true, "level": "DEBUG", "to_console": true}'