Example below uses Python Scapy module to generate an ICMP Request with the TOS value 184 (DSCP 46/EF) which is useful for testing QOS.
from scapy.all import *
send(IP(dst=”192.168.1.200″,tos=184)/ICMP(id=1,length=256,seq=57))
Example below uses Python Scapy module to generate an ICMP Request with the TOS value 184 (DSCP 46/EF) which is useful for testing QOS.
from scapy.all import *
send(IP(dst=”192.168.1.200″,tos=184)/ICMP(id=1,length=256,seq=57))
It is useful to know when a task has made a change and it should be possible to re-run the same task again and no changes are made. Some commands sent to the switch may return a changed state even though the configuration has changed before. This can be the case when using abbreviated commands. There are also different ways to apply the same configuration and it is worth inspecting the running configuration to see the syntax of the command in there which might be different to what was sent.
For example, you can create a new VLAN with one line and then give the VLAN a name with another. If you do this the playbook will always come back as changed=1 even though it made no changes on subsequent plays. By checking the running config you can see that the two command lines are converted to a single line and also includes quotes around the VLAN name.
Altering the Jinga2 source file used as a template to match the expected result in the running config will change the behaviour. Running the playbook for the first time will change the config and state will be changed=1. Subsequent runs of the playbook will not change anything as the configuration is found in the running config with the exact same string and syntax. The state will be changed=0.
$ ansible-playbook voss_config_vlan.yml
PLAY [PLAY 1: Manage VLANs with voss_config and jinja2] ********************************************
TASK [TASK 1: Apply config via SSH] ****************************************************************
changed: [r1]
RUNNING HANDLER [HANDLER 1: Display changes] *******************************************************
ok: [r1] => {
“msg”: [
“vlan create 300 name \”Sales\” type port-mstprstp 0″,
“vlan create 400 name \”IT\” type port-mstprstp 0″
]
}
PLAY RECAP *****************************************************************************************
r1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ ansible-playbook voss_config_vlan.yml
PLAY [PLAY 1: Manage VLANs with voss_config and jinja2] ********************************************
TASK [TASK 1: Apply config via SSH] ****************************************************************
ok: [r1]
PLAY RECAP *****************************************************************************************
r1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
###
VLAN extract from running config:
vlan create 300 name “Sales” type port-mstprstp 0
vlan create 400 name “IT” type port-mstprstp 0
Jinga2 template:
$ cat templates/vlans.j2
{% for vlan in vlans %}
vlan create {{ vlan.vid }} name “{{ vlan.description }}” type port-mstprstp 0
{% endfor %}
Add site-package yamllint to inspect YAML/YML files for errors.
pip install –user yamllint
Yamllint has two default configuration files: default.yaml and relaxed.yaml with pre-set rules. Can extend a configuration file and alter one of the rules.
Mylint.yaml:
# Mylint.yaml is my own configuration file for yamllint
# It extends the default.yaml by adjusting some options.
extends: default
rules:
new-lines: disable
###
Did this to avoid the error about the EOL character on the first line (—).
1:4 error wrong new line character: expected \n (new-lines)
from netmiko import ConnectHandler
voss1 = {‘device_type’: ‘extreme_vsp’, ‘host’: ‘192.168.1.11’, ‘username’: ‘rwa’, ‘password’: ‘rwa’}
net_connect = ConnectHandler(**voss1)
net_connect.find_prompt()
net_connect.enable()
net_connect.send_command(‘terminal more disable’)
output = net_connect.send_command(‘show run’)
print(output)
net_connect.send_command(‘terminal more enable’)
savedoutput = open(“switch” + voss1[‘host’], “w”)
savedoutput.write(output)
savedoutput.close
https://pypi.org/project/ciscoconfparse/
Building configuration files from a template…
VOSS.J2
This took me some time to get to work using Windows / Cygwin and my GNS3 VOSS simulated switch.
I struggled with getting Ansible + SSH to work from within Cygwin using key based authentication. But I wanted to test Ansible and Ansible-Playbook against VOSS so I persevered with simple user and password authentication which I got to work using a combination of files (ansible.cfg, inventory file = hosts and a test.yml file to run a single show command and display the output).
Hosts file
First stage, was to get a ‘Pong’ response to my ‘Ping’:
-bash-4.4$ ansible -i ./inventory/hosts voss -u rwa -m ping -c network_cli -e ansible_network_os=voss
192.168.211.10 | SUCCESS => {
“ansible_facts”: {
“discovered_interpreter_python”: “/usr/bin/python”
},
“changed”: false,
“ping”: “pong”
Next, I wanted to run Ansible which would login to the switch and run a simple command:
-bash-4.4$ ansible -i ./inventory/hosts voss -u rwa -m voss_command -a “commands=’show clock'”
192.168.211.10 | SUCCESS => {
“ansible_facts”: {
“discovered_interpreter_python”: “/usr/bin/python”
},
“changed”: false,
“stdout”: [
“Sat Jun 13 13:02:15 2020 UTC”
],
“stdout_lines”: [
[
“Sat Jun 13 13:02:15 2020 UTC”
]
]
}
Now that Ansible could login and carry out a task I progressed to put a task into a Playbook:
when: output.stdout is search(‘VOSS1’)
debug:
$ cat playbook1.yml
—
– name: “Play 1: Capture sys-info”
hosts: routers
connection: network_cli
tasks:
– name: “Task 1: Show sys-info”
voss_command:
commands: show sys-info
register: result
– name: “Task 2: Print output”
debug:
msg: “{{ result }}”
– name: “Task 3: Create files folder”
file:
path: “outputs”
state: directory
run_once: true
– name: “Task 4: Write stdout to file”
copy:
content: “{{ result.stdout[0] }}\n”
dest: “outputs/{{ inventory_hostname }}.txt”
Get ARP table from VSP switches…
from nornir import InitNornir
from nornir.plugins.tasks.networking import netmiko_send_command
from nornir.plugins.functions.text import print_result
nr = InitNornir()
result = nr.run(
task=netmiko_send_command,
command_string=”show ip arp”
)
print_result(result)
Hosts.yaml
—
voss-1:
hostname: ‘192.168.1.10’
port: 22
username: ‘rwa’
password: ‘rwa’
platform: ‘extreme_vsp’
voss-2:
hostname: ‘192.168.1.11’
port: 22
username: ‘rwa’
password: ‘rwa’
platform: ‘extreme_vsp’
Device_Type: Extreme_VSP
Get list of IP interfaces…
from netmiko import ConnectHandler
voss1 = {‘device_type’: ‘extreme_vsp’, ‘host’: ‘192.168.1.10’, ‘username’: ‘rwa’, ‘password’: ‘rwa’}
net_connect = ConnectHandler(**voss1)
net_connect.find_prompt()
output = net_connect.send_command(‘show ip interface’)
print(output)
Making a configuration change, for example, disabling FTPD…
from netmiko import ConnectHandler
voss2 = {‘device_type’: ‘extreme_vsp’, ‘host’: ‘192.168.1.11’, ‘username’: ‘rwa’, ‘password’: ‘rwa’}
net_connect = ConnectHandler(**voss2)
net_connect.find_prompt()
net_connect.enable()
net_connect.send_config_set([‘no boot config flags ftpd’])
Device_Type: Extreme_ERS
from netmiko import ConnectHandler
ers1 = {‘device_type’: ‘extreme_ers’, ‘host’: ‘192.168.1.5’, ‘username’: ‘RW’, ‘password’: ‘securepasswd’}
net_connect = ConnectHandler(**ers1)
net_connect.find_prompt()
output = net_connect.send_command(‘show system’)
print(output)
Paramiko script which logs in to VOSS and sets the FTPD boot flag.
Devices.json
{
“voss-1”: {“ip”: “192.168.1.10”},
“voss-2”: {“ip”: “192.168.1.11”}
}
Commands.txt
enable
config t
boot config flags ftpd
exit
save config
exit
Verify changes made to running-config and configuration file on intflash.
show run | I ftpd
show grep ftpd config.cfg
VSP-8284XSQ-1:1#show run | i ftpd
boot config flags ftpd
VSP-8284XSQ-1:1#grep ftpd config.cfg
boot config flags ftpd
VSP-8284XSQ-1:1#
Script:
import paramiko, getpass, time, json
with open(‘devices.json’, ‘r’) as f:
devices = json.load(f)
with open(‘commands.txt’, ‘r’) as f:
commands = f.readlines()
username = input(‘Username: ‘)
password = getpass.getpass(‘Password: ‘)
max_buffer = 65535
def clear_buffer(connection):
if connection.recv_ready():
return connection.recv(max_buffer)
# Starts the loop for devices
for device in devices.keys():
outputFileName = device + ‘_output.txt’
connection = paramiko.SSHClient()
connection.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connection.connect(devices[device][‘ip’], username=username, password=password, look_for_keys=False, allow_agent=False)
new_connection = connection.invoke_shell()
output = clear_buffer(new_connection)
time.sleep(2)
new_connection.send(“terminal more disable\n”)
output = clear_buffer(new_connection)
with open(outputFileName, ‘wb’) as f:
for command in commands:
new_connection.send(command)
time.sleep(2)
output = new_connection.recv(max_buffer)
print(output)
f.write(output)
new_connection.close()