I’ve been talking about Infrastructure as Code / Automation / Python a lot with colleagues and peers so I figured I may as well make a quick intro to ansible post since it is just too hot out today.
I’ll preface this by saying, this is only really covered exam wise in the CCIE Written and possibly the cloud track but I figure it might be neat to see. If this is decently well received I might continue on and look at some of the other Evolving Technologies in the written.
Today I’m playing with Ansible 2.4 on a Centos 7 server. The topology isn’t especially relevant so I’m just running some Cisco and Juniper routers to play with.
So what’s Ansible? Ansible is a agentless and python based configuration management platform that we can use to configure network devices and servers based on a playbook that contains a bunch of instructions.
I’m not going to go very deep here so this is more of a kicking the tires since ansible can get pretty complex and has its own red hat lab exam.
DNS
To make things a bit easier we’ll start out by making some host entries so we can work with hostnames instead of IP addresses.
[root@centos01 /]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.10.21.111 r01.testlab.com 10.10.21.112 r02.testlab.com 10.10.21.113 r03.testlab.com 10.10.21.114 r04.testlab.com 10.10.21.115 r05.testlab.com 10.10.21.116 r06.testlab.com 10.10.21.250 sw01.testlab.com 10.10.21.251 sw02.testlab.com 10.10.21.252 sw03.testlab.com 10.10.21.253 sw04.testlab.com 10.10.2.56 vmx01.testlab.com
Ansible Config
Ansible by default will make sure the system knows about all the SSH keys it connects to, since this can be a pain when automating stuff I’m just going to disable the feature in
/etc/ansible/ansible.cfg
`host_key_checking = False` `host_key_auto_add = True`
Ansible Hosts
Next we define the routers into groups under /etc/ansible/hosts
, the hosts file supports ranges so r01 to r06 would be written as r0[1:6]
we can get a bit more complex but lets not get too ahead of ourselves. When we make a playbook we will call the groups we want to run actions against.
[cisco_routers] r0[1:6].testlab.com [cisco_switches] sw0[1:4].testlab.com [juniper] vmx01.testlab.com
We can also set default values for connections in the vars section.
[cisco_routers:vars] ansible_connection=local ansible_ssh_user=admin ansible_ssh_pass=meowcat [juniper:vars] ansible_connection=local ansible_ssh_user=admin ansible_ssh_pass=Meowcat!!!
Module and CLI
Ansible has a ton of default modules that do various tasks, as of 2.4 there are just under 1200 built-in modules that do everything from add configuration to Cisco routers to spinning up a cloud environment in AWS to setting up a 3 tier application on a bunch of servers.
[root@centos01 ansible]# ansible-doc -l | wc -l
1193
If we want to run a simple module we can just do it straight from the command line in ad hoc mode, here is an example of the ping
module which verifies ansible can connect to everything in a group.
[root@centos01 ansible]# ansible cisco_routers -m ping r05.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } r04.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } r02.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } r01.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } r03.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } r06.testlab.com | SUCCESS => { "changed": false, "failed": false, "ping": "pong" }
Though you’ll tend to get more value by doing a proper playbook.
Credentials
To make things a bit more modular we will add the login information to a file called secrets.yaml, most ansible files are in YAML, it is a simple language for assigning values. It is very white space sensitive, it also does not support the tab key so make sure you always just use spaces to get the indentation you want.
[root@centos01 ansible]# cat secrets.yaml --- creds: username: admin password: meowcat auth_pass: meowcat
Our first playbook!
Our first playbook is going to do a few things
[root@centos01 ansible]# cat cisco-router.yaml --- - hosts: cisco_routers gather_facts: yes connection: local tasks: - name: GET CREDENTIALS include_vars: secrets.yaml - name: DEFINE CONNECTION set_fact: connection: authorize: yes host: "{{ inventory_hostname }}" username: "{{ creds['username'] }}" password: "{{ creds['password'] }}" auth_pass: "{{ creds['auth_pass'] }}" - name: RUN SHOW INTERFACES ios_command: provider: "{{ connection }}" commands: - show ip interface brief register: ipbrief - debug: var=ipbrief.stdout_lines Let's break down what we are doing here. --- - hosts: cisco_routers gather_facts: no connection: local
The first section tells ansible what hosts
we are running the playbook against, in this case it is the Cisco routers group.
Ansible can optionally query the device to get various information like the device’s hostname and OS level but we’ll disable gather_facts
for now to save some time. There is actually vendor specific fact modules that we can use if we want info on the system, for example Cisco would use ios_facts
Lastly we set the connection to local
which means it will use OpenSSH to connect to the routers. We can also set it to be paramiko
if you want it to use the python ssh instead. There are a few other options as well that we don’t need to touch on today.
The rest of our playbook is the tasks that are going to be run. Each task has a name followed by an action. The GET CREDENTIALS task just reads our secrets.yaml file to get our passwords.
tasks: - name: GET CREDENTIALS include_vars: secrets.yaml
Next we tell ansible how to connect to the routers, I like to put this info in a DEFINE CONNECTIONS task to make it easier going forward. This section defines various variables that we can use in our other tasks.
- name: DEFINE CONNECTION set_fact: connection: authorize: yes host: "{{ inventory_hostname }}" username: "{{ creds['username'] }}" password: "{{ creds['password'] }}" auth_pass: "{{ creds['auth_pass'] }}"
Lastly we will have ansible run show ip interface brief
on each of my six routers. To do this we run the ios_command
module and tell it to use our connection provider we just made above. We need to add the provider to each command since ansible will connect to the router to execute each step though this behavior has recently changed in newer versions. To save the output we use the register
keyword to save the output as a variable and then we use the debug
command to display it on the screen.
- name: RUN SHOW INTERFACES ios_command: provider: "{{ connection }}" commands: - show ip interface brief register: ipbrief - debug: var=ipbrief.stdout_lines
Our First Playbook’s output
Once our playbook is done we run it with the ansible-playbook
command, it will run through each of the steps and display our show ip interface brief
output. If you are following along and you are seeing errors, check your whitespace, ansible can be pretty picky until you get used to it.
[root@centos01 ansible]# ansible-playbook cisco-router.yaml PLAY [cisco_routers] *********************************************************** TASK [GET CREDENTIALS] ********************************************************* ok: [r02.testlab.com] ok: [r03.testlab.com] ok: [r04.testlab.com] ok: [r01.testlab.com] ok: [r05.testlab.com] ok: [r06.testlab.com] TASK [DEFINE CONNECTION] ******************************************************* ok: [r01.testlab.com] ok: [r03.testlab.com] ok: [r02.testlab.com] ok: [r05.testlab.com] ok: [r04.testlab.com] ok: [r06.testlab.com] TASK [RUN SHOW INTERFACES] ***************************************************** ok: [r03.testlab.com] ok: [r05.testlab.com] ok: [r01.testlab.com] ok: [r02.testlab.com] ok: [r04.testlab.com] ok: [r06.testlab.com] TASK [debug] ******************************************************************* ok: [r03.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.113 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.13 10.11.33.33 YES manual up up ", "GigabitEthernet2.34 10.33.44.33 YES manual up up ", "Loopback0 172.16.254.3 YES TFTP up up " ] ] } ok: [r04.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.114 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.14 unassigned YES manual deleted down ", "GigabitEthernet2.24 10.22.44.44 YES manual up up ", "GigabitEthernet2.34 10.33.44.44 YES manual up up ", "Loopback0 172.16.254.4 YES TFTP up up " ] ] } ok: [r01.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.111 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.12 10.11.22.11 YES manual up up ", "GigabitEthernet2.13 10.11.33.11 YES manual up up ", "Loopback0 172.16.254.1 YES TFTP up up " ] ] } ok: [r02.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.112 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.12 10.11.22.22 YES manual up up ", "GigabitEthernet2.23 10.22.33.22 YES manual up up ", "GigabitEthernet2.24 10.22.44.22 YES manual up up ", "Loopback0 172.16.254.2 YES TFTP up up " ] ] } ok: [r05.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.115 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "Loopback0 172.16.254.5 YES TFTP up up " ] ] } ok: [r06.testlab.com] => { "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.116 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "Loopback0 172.16.254.6 YES TFTP up up " ] ] } PLAY RECAP ********************************************************************* r01.testlab.com : ok=4 changed=0 unreachable=0 failed=0 r02.testlab.com : ok=4 changed=0 unreachable=0 failed=0 r03.testlab.com : ok=4 changed=0 unreachable=0 failed=0 r04.testlab.com : ok=4 changed=0 unreachable=0 failed=0 r05.testlab.com : ok=4 changed=0 unreachable=0 failed=0 r06.testlab.com : ok=4 changed=0 unreachable=0 failed=0
Making a change
Viewing some show commands is cool but what about making a change. In this example we’ll push a MGMT ACL to each of our Cisco routers.
[root@centos01 ansible]# cat cisco-router.yaml --- - hosts: cisco_routers gather_facts: no connection: local tasks: - name: GET CREDENTIALS include_vars: secrets.yaml - name: DEFINE CONNECTION set_fact: connection: authorize: yes host: "{{ inventory_hostname }}" username: "{{ creds['username'] }}" password: "{{ creds['password'] }}" auth_pass: "{{ creds['auth_pass'] }}" - name: RUN SHOW INTERFACES ios_command: provider: "{{ connection }}" commands: - show ip interface brief register: ipbrief - debug: var=ipbrief.stdout_lines - name: CHECK ACLS ios_command: provider: "{{ connection }}" commands: - show ip access-list register: beforeacl - debug: var=beforeacl.stdout_lines - name: CREATE MGMT ACL ios_config: provider: "{{ connection }}" lines: - 10 permit ip 10.10.13.0 0.0.0.255 any - 20 permit ip 10.10.2.0 0.0.0.255 any - 30 permit ip 10.10.21.0 0.0.0.255 any - 40 deny ip any any log parents: ['ip access-list extended ACL_MGMT'] before: ['no ip access-list extended ACL_MGMT'] match: exact - name: CHECK MGMT ACLS ios_command: provider: "{{ connection }}" commands: - show ip access-list ACL_MGMT register: afteracl - debug: var=afteracl.stdout_lines - name: APPLY MGMT ACL ios_config: provider: "{{ connection }}" lines: - ip access-group ACL_MGMT in parents: ['interface g1'] match: exact
Let’s examine what we are doing from where we left off. First we’ll run another show command to see if there are any ACLs on the boxes. This uses the same logic as above.
- name: CHECK ACLS ios_command: provider: "{{ connection }}" commands: - show ip access-list register: beforeacl - debug: var=beforeacl.stdout_lines
Next we use the ios_config
module to delete ACL_MGMT if it exists and then push a new ACL with my LAN networks in it.
- name: CREATE MGMT ACL ios_config: provider: "{{ connection }}" lines: - 10 permit ip 10.10.13.0 0.0.0.255 any - 20 permit ip 10.10.2.0 0.0.0.255 any - 30 permit ip 10.10.21.0 0.0.0.255 any - 40 deny ip any any log parents: ['ip access-list extended ACL_MGMT'] before: ['no ip access-list extended ACL_MGMT'] match: exact
Lastly we’ll check our work and apply the ACL to my CSR’s mgmt interfaces.
- name: CHECK MGMT ACLS ios_command: provider: "{{ connection }}" commands: - show ip access-list ACL_MGMT register: afteracl - debug: var=afteracl.stdout_lines - name: APPLY MGMT ACL ios_config: provider: "{{ connection }}" lines: - ip access-group ACL_MGMT in parents: ['interface g1'] match: exact
Our Full Playbook Output
[root@centos01 ansible]# ansible-playbook cisco-router.yaml PLAY [cisco_routers] ************************************************************************************************************************************************************************ TASK [GET CREDENTIALS] ********************************************************************************************************************************************************************** ok: [r02.testlab.com] ok: [r03.testlab.com] ok: [r01.testlab.com] ok: [r04.testlab.com] ok: [r05.testlab.com] ok: [r06.testlab.com] TASK [DEFINE CONNECTION] ******************************************************************************************************************************************************************** ok: [r02.testlab.com] ok: [r01.testlab.com] ok: [r03.testlab.com] ok: [r05.testlab.com] ok: [r04.testlab.com] ok: [r06.testlab.com] TASK [RUN SHOW INTERFACES] ****************************************************************************************************************************************************************** ok: [r01.testlab.com] ok: [r05.testlab.com] ok: [r03.testlab.com] ok: [r04.testlab.com] ok: [r02.testlab.com] ok: [r06.testlab.com] TASK [debug] ******************************************************************************************************************************************************************************** ok: [r02.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.112 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.12 10.11.22.22 YES manual up up ", "GigabitEthernet2.23 10.22.33.22 YES manual up up ", "GigabitEthernet2.24 10.22.44.22 YES manual up up ", "Loopback0 172.16.254.2 YES TFTP up up" ] ] } ok: [r01.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.111 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.12 10.11.22.11 YES manual up up ", "GigabitEthernet2.13 10.11.33.11 YES manual up up ", "Loopback0 172.16.254.1 YES TFTP up up" ] ] } ok: [r03.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.113 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.13 10.11.33.33 YES manual up up ", "GigabitEthernet2.34 10.33.44.33 YES manual up up ", "Loopback0 172.16.254.3 YES TFTP up up" ] ] } ok: [r05.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.115 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "Loopback0 172.16.254.5 YES TFTP up up" ] ] } ok: [r04.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.114 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "GigabitEthernet2.14 unassigned YES manual deleted down ", "GigabitEthernet2.24 10.22.44.44 YES manual up up ", "GigabitEthernet2.34 10.33.44.44 YES manual up up ", "Loopback0 172.16.254.4 YES TFTP up up" ] ] } ok: [r06.testlab.com] => { "failed": false, "ipbrief.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "GigabitEthernet1 10.10.21.116 YES TFTP up up ", "GigabitEthernet2 unassigned YES manual up up ", "Loopback0 172.16.254.6 YES TFTP up up" ] ] } TASK [CHECK ACLS] *************************************************************************************************************************************************************************** ok: [r02.testlab.com] ok: [r01.testlab.com] ok: [r03.testlab.com] ok: [r05.testlab.com] ok: [r04.testlab.com] ok: [r06.testlab.com] TASK [debug] ******************************************************************************************************************************************************************************** ok: [r01.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } ok: [r02.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } ok: [r03.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } ok: [r04.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } ok: [r05.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } ok: [r06.testlab.com] => { "beforeacl.stdout_lines": [ [ "" ] ], "failed": false } TASK [CREATE MGMT ACL] ********************************************************************************************************************************************************************** changed: [r04.testlab.com] changed: [r01.testlab.com] changed: [r03.testlab.com] changed: [r05.testlab.com] changed: [r02.testlab.com] changed: [r06.testlab.com] TASK [CHECK MGMT ACLS] ********************************************************************************************************************************************************************** ok: [r03.testlab.com] ok: [r02.testlab.com] ok: [r01.testlab.com] ok: [r05.testlab.com] ok: [r04.testlab.com] ok: [r06.testlab.com] TASK [debug] ******************************************************************************************************************************************************************************** ok: [r02.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any (212 matches)", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any (3 matches)", " 40 deny ip any any log" ] ], "failed": false } ok: [r01.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any", " 40 deny ip any any log" ] ], "failed": false } ok: [r03.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any (214 matches)", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any", " 40 deny ip any any log" ] ], "failed": false } ok: [r04.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any (212 matches)", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any", " 40 deny ip any any log" ] ], "failed": false } ok: [r05.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any (270 matches)", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any (4 matches)", " 40 deny ip any any log" ] ], "failed": false } ok: [r06.testlab.com] => { "afteracl.stdout_lines": [ [ "Extended IP access list ACL_MGMT", " 10 permit ip 10.10.13.0 0.0.0.255 any (306 matches)", " 20 permit ip 10.10.2.0 0.0.0.255 any", " 30 permit ip 10.10.21.0 0.0.0.255 any", " 40 deny ip any any log" ] ], "failed": false } TASK [APPLY MGMT ACL] *********************************************************************************************************************************************************************** changed: [r03.testlab.com] changed: [r05.testlab.com] changed: [r02.testlab.com] changed: [r04.testlab.com] changed: [r01.testlab.com] changed: [r06.testlab.com] PLAY RECAP ********************************************************************************************************************************************************************************** r01.testlab.com : ok=10 changed=2 unreachable=0 failed=0 r02.testlab.com : ok=10 changed=2 unreachable=0 failed=0 r03.testlab.com : ok=10 changed=2 unreachable=0 failed=0 r04.testlab.com : ok=10 changed=2 unreachable=0 failed=0 r05.testlab.com : ok=10 changed=2 unreachable=0 failed=0 r06.testlab.com : ok=10 changed=2 unreachable=0 failed=0
Once it is done we can see the ACL is created and applied on the router, notice the output lists the number of changes that happens in the play through.
R11(config)#do sh access-list Extended IP access list ACL_MGMT 10 permit ip 10.10.13.0 0.0.0.255 any (16 matches) 20 permit ip 10.10.2.0 0.0.0.255 any 30 permit ip 10.10.21.0 0.0.0.255 any (7 matches) 40 deny ip any any log R11(config)#do sh run int g1 | in ACL ip access-group ACL_MGMT in
What about other vendors?
We can use the exact same logic to push configuration to a Juniper router as well only instead of ios modules we are using junos modules.
In this playbook we’ll check the routing table and then enable OSPF. Because I’m just doing one router I’m not using a credentials file and will just put the login directly in the playbook.
[root@centos01 ansible]# cat juniper.yaml --- - hosts: juniper gather_facts: no connection: local tasks: - name: DEFINE CONNECTION set_fact: connection: host: "{{ inventory_hostname }}" username: admin password: Meowcat!!! - name: RUN SHOW ROUTE junos_command: provider: "{{ connection }}" commands: - show route register: routes - debug: var=routes.stdout_lines - name: Enable OSPF junos_config: provider: "{{ connection }}" lines: - set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 #Juniper Output [root@centos01 ansible]# ansible-playbook juniper.yaml PLAY [juniper] ****************************************************************************************************************************************************************************** TASK [DEFINE CONNECTION] ******************************************************************************************************************************************************************** ok: [vmx01.testlab.com] TASK [RUN SHOW ROUTE] *********************************************************************************************************************************************************************** ok: [vmx01.testlab.com] TASK [debug] ******************************************************************************************************************************************************************************** ok: [vmx01.testlab.com] => { "failed": false, "routes.stdout_lines": [ [ "inet.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)", "+ = Active Route, - = Last Active, * = Both", "", "0.0.0.0/0 *[Access-internal/12] 19:06:32", " > to 10.10.2.1 via ge-0/0/1.0", "10.1.3.0/24 *[Direct/0] 1w6d 23:35:52", " > via ge-0/0/2.0", "10.1.3.1/32 *[Local/0] 1w6d 23:35:52", " Local via ge-0/0/2.0", "10.1.4.0/24 *[Direct/0] 1w6d 23:35:52", " > via ge-0/0/3.0", "10.1.4.1/32 *[Local/0] 1w6d 23:35:52", " Local via ge-0/0/3.0", "10.10.2.0/24 *[Direct/0] 19:06:33", " > via ge-0/0/1.0", "10.10.2.47/32 *[Local/0] 19:06:33", " Local via ge-0/0/1.0", "", "MGMT.inet.0: 3 destinations, 4 routes (3 active, 0 holddown, 0 hidden)", "+ = Active Route, - = Last Active, * = Both", "", "0.0.0.0/0 *[Static/5] 1w6d 23:35:40", " > to 10.10.2.1 via ge-0/0/0.0", " [Access-internal/12] 1w6d 23:35:39", " > to 10.10.2.1 via ge-0/0/0.0", "10.10.2.0/24 *[Direct/0] 1w6d 23:35:40", " > via ge-0/0/0.0", "10.10.2.56/32 *[Local/0] 1w6d 23:35:40", " Local via ge-0/0/0.0", "", "inet6.0: 1 destinations, 1 routes (1 active, 0 holddown, 0 hidden)", "+ = Active Route, - = Last Active, * = Both", "", "ff02::2/128 *[INET6/0] 2w1d 01:43:39", " MultiRecv" ] ] } TASK [Enable OSPF] ************************************************************************************************************************************************************************** changed: [vmx01.testlab.com] PLAY RECAP ********************************************************************************************************************************************************************************** vmx01.testlab.com : ok=4 changed=1 unreachable=0 failed=0
Conditions Conditions
We’ll wrap things up by briefly looking at conditions, we can execute a task on a device only if it meets certain criteria, in this example I’m going to configure OSPF only on a router with a IP address of 172.16.254.2
[root@centos01 ansible]# vi cisco-conditional.yaml --- - hosts: cisco_routers gather_facts: no connection: local tasks: - name: GET CREDENTIALS include_vars: secrets.yaml - name: DEFINE CONNECTION set_fact: connection: authorize: yes host: "{{ inventory_hostname }}" username: "{{ creds['username'] }}" password: "{{ creds['password'] }}" auth_pass: "{{ creds['auth_pass'] }}" - name: GATHER Loopback ios_command: provider: "{{ connection }}" commands: - show run interface l0 | in address register: rtrLoopback - name: CONFIGURE R02 when: rtrLoopback.stdout[0].count("172.16.254.2") > 0 ios_config: provider: "{{ connection }}" lines: - network 10.10.10.0 0.0.0.255 area 0 - network 2.2.2.2 0.0.0.0 area 2 parents: ['router ospf 1'] match: exact
Conditional Output
We can see that only R02 is changed
[root@centos01 ansible]# ansible-playbook cisco-conditional.yaml PLAY [cisco_routers] ************************************************************************************************************************************************************************ TASK [GET CREDENTIALS] ********************************************************************************************************************************************************************** ok: [r01.testlab.com] ok: [r02.testlab.com] ok: [r04.testlab.com] ok: [r03.testlab.com] ok: [r05.testlab.com] ok: [r06.testlab.com] TASK [DEFINE CONNECTION] ******************************************************************************************************************************************************************** ok: [r01.testlab.com] ok: [r02.testlab.com] ok: [r03.testlab.com] ok: [r04.testlab.com] ok: [r05.testlab.com] ok: [r06.testlab.com] TASK [GATHER Loopback] ********************************************************************************************************************************************************************** ok: [r03.testlab.com] ok: [r02.testlab.com] ok: [r04.testlab.com] ok: [r05.testlab.com] ok: [r01.testlab.com] ok: [r06.testlab.com] TASK [CONFIGURE R02] ************************************************************************************************************************************************************************ skipping: [r03.testlab.com] skipping: [r04.testlab.com] skipping: [r01.testlab.com] skipping: [r05.testlab.com] skipping: [r06.testlab.com] changed: [r02.testlab.com] PLAY RECAP ********************************************************************************************************************************************************************************** r01.testlab.com : ok=3 changed=0 unreachable=0 failed=0 r02.testlab.com : ok=4 changed=1 unreachable=0 failed=0 r03.testlab.com : ok=3 changed=0 unreachable=0 failed=0 r04.testlab.com : ok=3 changed=0 unreachable=0 failed=0 r05.testlab.com : ok=3 changed=0 unreachable=0 failed=0 r06.testlab.com : ok=3 changed=0 unreachable=0 failed=0
On R02 (Actually R12 for another lab but I digress) we can see OSPF is setup
R12(config)# do sh run | s router router ospf 1 network 2.2.2.2 0.0.0.0 area 2 network 10.10.10.0 0.0.0.255 area 0
Anyway I could spend all day talking about what tools like ansible can do for you but hopefully this peaks your interest a bit.
`
Hi Donald,
Hope you are doing great. At first i want to thank you for this nice article. As i am a beginner to automation and ansible i am facing following issue while trying to implement the “cisco-router.yaml” file. Please find the following information about the system information and error message :
OS : Ubuntu 18.04.2 LTS
Ansible Version :ansible 2.7.8
Python version = 2.7.15rc1
Error:
ERROR! ‘include_vars’ is not a valid attribute for a Play
The error appears to have been in ‘/etc/ansible/Cisco_devices.yaml’: line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
—
– hosts: Cisco_devices
^ here
Configuration :
—
– hosts: Cisco_devices
gather_facts: true
connection: local
tasks:
– name: GET CREDENTIALS
include_vars:
file: secrets.yaml
If you can help with any suggestion or guidance, i will be very grateful. Hope to hear from you soon. Thanks in advance.
Sincerely,
Nasir
you can check “ansible-doc include_vars” for more detail . i think it’s syntax error in yourfile like:
include_vars:
file: secrets.yaml