Time | Topic |
---|---|
Day 1 | |
15:30 - 16:00 | Network Programmability & Automation |
16:00 - 16:30 | Ansible 101 |
16:30 - 17:00 | Exercise 1 |
Day 2 | |
15:30 - 16:00 | Ansible 102 |
16:00 - 16:45 | Exercise 2 |
16:45 - 17:00 | StackStorm 101 |
(6) It is easier to move a problem around (for example, by moving
the problem to a different part of the overall network
architecture) than it is to solve it.
(6a) (corollary). It is always possible to add another level of
indirection.
(11) Every old idea will be proposed again with a different name and
a different presentation, regardless of whether it works.
(11a) (corollary). See rule 6a.
---
- hosts: all
connection: local
gather_facts: no
tasks:
- name: configure the login banner
net_banner:
banner: "{{ 'motd' if ansible_network_os == 'nxos' else 'login' }}"
text: "{{ network_banner }}"
state: present
when: network_banner is defined
- name: configure the MOTD
net_banner:
banner: motd
text: "{{ network_motd }}"
state: present
when: network_motd is defined and ansible_network_os != 'nxos'
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/13.3R5/junos">
<software-information>
<host-name>M320-TEST-re0</host-name>
<product-model>m320</product-model>
<product-name>m320</product-name>
<junos-version>13.3R5.9</junos-version>
</software-information>
<cli>
<banner>{master}</banner>
</cli>
</rpc-reply>
---
parameter_defaults:
ControlPlaneDefaultRoute: "192.0.2.1"
ControlPlaneSubnetCidr: 24
DnsServers:
- "192.168.23.1"
EC2MetadataIp: "192.0.2.1"
ExternalAllocationPools:
- end: "10.0.0.250"
start: "10.0.0.4"
ExternalNetCidr: "10.0.0.1/24"
NeutronExternalNetworkBridge: ""
{
"parameter_defaults": {
"ControlPlaneDefaultRoute": "192.0.2.1",
"ControlPlaneSubnetCidr": "24",
"DnsServers": [
"192.168.23.1"
],
"EC2MetadataIp": "192.0.2.1",
"ExternalAllocationPools": [
{
"end": "10.0.0.250",
"start": "10.0.0.4"
}
],
"ExternalNetCidr": "10.0.0.1/24",
"NeutronExternalNetworkBridge": ""
}
}
module configuration {
namespace "http://xml.juniper.net/xnm/1.1/xnm";
prefix junos;
organization
"Juniper Networks, Inc.";
revision "2015-09-11" {
description "Initial revision";
}
typedef ipv4addr {
type string;
}
grouping juniper-config {
container backup-router {
description "IPv4 router to use while booting";
leaf address {
description "Address of router to use while booting";
type ipv4addr;
mandatory true;
}
...
https://raw.githubusercontent.com/Juniper/yang/master/14.2/configuration.yang
{% for key, value in vlanDict.iteritems() -%}
vlan {{ key }}
name {{ value }}
{% endfor %}
>>> vlanDict = {
>>> 123: 'TEST-VLAN-123',
>>> 234: 'TEST-VLAN-234',
>>> 345: 'TEST-VLAN-345'}
>>> from jinja2 import Environment
>>> env = Environment(loader=FileSystemLoader('./Templates/'))
>>> template = env.get_template('ourtemplate')
>>> print template.render(vlanDict)
vlan 123
name TEST-VLAN-123
vlan 234
name TEST-VLAN-234
vlan 345
name TEST-VLAN-345
[barcelona-dc]
switch01
switch02
[madrid-dc]
172.31.200.1
switch03
[barcelona-cpe]
vmx1
[madrid-cpe]
172.22.3.1
[barcelona:children]
barcelona-dc
barcelona-cpe
[all:vars]
ntp_server=10.20.30.4
[barcelona:vars]
ntp_server=192.168.0.1
[madrid:vars]
ntp_server=10.0.0.1
[barcelona-dc]
switch01 ntp_server=192.168.0.3
switch02
File: group_vars/barcelona-dc.yml
---
snmp:
contact: Ausias March
location: Barcelona Data Center, Passeig Colon
communities:
- community: public
type: ro
- community: private
type: rw
How will we access this data?: snmp.communitites[0].type
It's the file that contain your automation instructions
---
- name: PLAY 1 - Configure Interface Speed
hosts: barcelona-dc
connection: local
gather_facts: no
tasks:
- name: TASK1 - Get interface information
ios_command:
commands:
- show run | include interfaces
provider:
username: myusername
password: mypassword
host: "{{ inventory_hostname }}"
Experiment with basic Ansible automation, learning by doing
All you need is here: https://github.com/chadell/ansible-cumulus-vyos
From the mgmt server:
Time | Topic |
---|---|
Day 1 | |
15:30 - 16:00 | Network Programmability & Automation |
16:00 - 16:30 | Ansible 101 |
16:30 - 17:00 | Exercise 1 |
Day 2 | |
15:30 - 16:00 | Ansible 102 |
16:00 - 16:45 | Exercise 2 |
16:45 - 17:00 | StackStorm 101 |
Note: to find out the parameters of each module (plus some examples), you can use the ansible-doc utility: ansible-doc ios_config
group_vars/barcelona-dc.yml
---
snmp:
contact: Ausias March
location: Barcelona Data Center, Passeig Colon
communities:
- community: public
type: ro
- community: privat
type: rw
group_vars/madrid-dc.yml
---
snmp:
contact: Francisco de Quevedo
location: Madrid Data Center, Paseo de la Castellana
communities:
- community: publico
type: ro
- community: privado
type: rw
group_vars/all.yml
---
base_provider:
username: vagrant
password: vagrant
host: "{{ inventory_hostname}}"
templates/snmp/ios.j2
snmp-server location {{ snmp.location }}
snmp-server contact {{ snmp.contact }}
{% for community in snmp.communities %}
snmp-server community {{ community.community }} {{ community.type | upper }}
{% endfor %}
templates/snmp/junos.j2
set snmp location {{ snmp.location }}
set snmp contact {{ snmp.contact }}
{% for community in snmp.communities %}
{% if community.type | lower == "rw" %}
set snmp community {{ community.community }} authorization read-write
{% elif community.type | lower == "ro" %}
set snmp community {{ community.community }} authorization read-only
{% endif %}
{% endfor %}
We will use the template module. It use the src parameter as the proper template to use and the dest parameter to point to the location where to store the rendered configuration (it assumes the folders already exist)
# play definition omitted
tasks:
- name: GENERATE CONFIGS FOR EACH OS
template:
src: "./snmp/{{ os }}.j2"
dest: "./configs/snmp/{{ inventory_hostname }}.cfg
and run it!
$ ansible-playbook -i inventory.cfg snmp.yml
What is os? and inventory_hostname?
[eos]
eos-spine1
eos-spine2
[eos:vars]
os=eos
Let's use the eos_config module to deploy the SNMP configuration from previous example
- name: PLAY 2 - ENSURE EOS SNMP CONFIGS ARE DEPLOYED
hosts: eos
connection: local
gather_facts: no
tasks:
- name: DEPLOY CONFIGS FOR EOS
eos_config:
src: "./configs/snmp/{{ inventory_hostname }}.cfg"
provider: "{{ base_provider }}"
Notes:
base_provider:
username: vagrant
password: vagrant
host: "{{ inventory_hostname }}
Even Ansible is used often to deploy configurations it also makes possible to automate the collection of data from network devices.
In this part we will analyse two key methods for gathering data:
The core facts module return the following data as JSON (so it could be used in the playbook!):
Core facts modules | Result |
---|---|
ansible_net_version | The operation system version running on the remote device |
ansible_net_hostname | The configured hostname of the device |
ansible_net_config | The current active config from the device |
ansible_net_interfaces | A hash of all interfaces running on the system |
Even by default the gather_facts provides all this information, in network devices that don't let remote python code execution (non Linux based NOS), we have to use specific facts modules (i.e. ios_facts):
---
- name: PLAY 1 - COLLECT FACTS FOR IOS
hosts: iosxe
connection: local
gather_facts: no
tasks:
- name: COLLECT FACTS FOR IOS
ios_facts:
provider: "{{ base_provider }}
In order to view the facts that are being returned from the module you can run the playbook in verbose mode or simply yse the debug module with the var parameter while referencing a valid facts key:
# play definition omitted
tasks:
- name: COLLECT FACTS FROM IOS
ios_facts:
provider: "{{ base_provider }}"
- name: DEBUG OS VERSION
debug:
var: ansible_net version
- name: DEBUG HOSTNAME
debug:
var: ansible_net_hostname
To get the return data (JSON) from a module you can use the verbose mode, but there is also another way, using the register task attribute, which allows you to save the JSON response data as a variable
- name: ISSUE SHOW COMMAND
ios_command:
command:
- show run | include snmp-server community
provider: "{{ base_provider }}"
register: snmp_data
The register's associated value is the variable you want to save the data in
Since the snmp_data variable is now created (or registered), the debug module can be used to view the data. After viewing it, you need to understand the data structure to use it, even you could get it from the ansible-doc help.
- name: DEBUG COMMAND STRING RESPONSE WITH JINJA SHORTHAND SYNTAX
debug:
var: snmp_data.stdout.0
- name: DEBUG COMMAND STRING RESPONSE WITH STANDARD PYTHON SYNTAX
debug:
var: snmp_data['stdout'][0]
or just use it with templates as {{ snmp_data['stdout'][0] }}
tasks:
- name: RETRIEVE VLANS JSON RESPONSE
eos_command:
commands:
- show vlan brief | json
provider: "{{ base_provider}}"
register: vlan_data
- name: CREATE EXISTING_VLANS FACT TO SIMPLIFY ACCESSING VLANS
set_fact:
existing_vlans_ids: "{{ vlan_data.stdout.0.vlans.keys() }}"
- name: PERFORM COMPLIANCE CHECKS
assert:
that:
- "'20' in existing_vlans_ids"
- name: PLAY CREATE REPORTS
hosts: "iosxe,eos,nxos"
connection: local
gather_facts: no
tasks:
- name: GENERATE DEVICE SPECIFIC REPORTS
template:
src: ./reports/facts.j2
dest: ./reports/facts/{{ inventory_hostname }}.md
- name: CREATE MASTER REPORT
assemble:
src: ./reports/facts/
dest: ./reports/master-report.md
delimiter: "---"
run_once: true
Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.
roles/
common/
tasks/
templates/
vars/
defaults/
meta/
webservers/
tasks/
defaults/
---
- hosts: webservers
roles:
- common
- webservers
All of the examples we've reviewd in this chapter have used Ansible core modules (included in Ansible installation) However, there is an active community for 3rd-party modules.
Network Automation and Programmability Abstraction Layer with Multi-vendor support is an open source community developing mutli-vendor network automation integrations
Declarative configuration management (napalm_install_config): NAPALM focuses on the desired state configuration. You deploy the new configuration (no commands) and NAPALM abstracts away how this operates per vendor and makes it so you don't have to micromanage device configurations.
Obtaining configuration and operational state from devices: The module napalm_get_facts is used to obtain a base set of facts and other information (usch as route entries, MAC table, etc.). The big benefit is that it the data is preparsed and normalised for all vendors supported.
Installing 3rd party modules is quite straightforward:
Extend Ansible feautures to provision a network
All you need is here: https://github.com/chadell/ansible-cumulus-vyos