Ansible Deeper Dive
Ansible Deeper Dive
ipSpace.net AG
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Revision history
2017-06-27 Ansible assemble module
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Review: How Ansible Playbooks Really Work
[ios]
r1.lab.local location=Rack-1
1.3: Host variables
[nxos]
s1.lab.local location=Rack-2
.
├── group_vars
│ ├── ios.yml 2.2: Variables in group_vars/group.yml
│ ├── nxos.yml
│ └── all.yml 2.1: Variables in group_vars/all.yml
└── host_vars
├── r1.lab.local.yml
└── s1.lab.local.yml
[ios]
r1.lab.local location=Rack-1 to host_vars/r1.lab.local.yml
[nxos]
s1.lab.local location=Rack-2 to host_vars/s1.lab.local.yml
[all:vars]
ansible_user=cisco
ansible_ssh_pass=cisco
snmp_community=cisco
to group_vars/all.yml
[email protected]
snmp_host=172.16.1.12
syslog_host=172.16.1.12
---
- hosts: all
connection: local
vars:
dir: configs
tasks:
- template: src={{ansible_device_os}}/common.j2
dest={{dir}}/{{inventory_hostname}}.txt
--- ---
- hosts: ios ansible_user: cisco
vars_files: ansible_ssh_pass: cisco
- /secure/passwords
tasks:
- raw: "show arp"
---
- tasks:
- raw: "show arp"
register: show
- set_fact: extra_fact=123
Registered variables:
• Ansible can store the results of a module in a variable (dictionary)
• Typical keys stored in the dictionary: stdout, stdout_lines
• Use debugging to find what else a module can return
Additional facts:
• set_fact module sets (or replaces) variable values
13This material is copyrighted
© ipSpace.net 2016 and licensed for the sole use by Mikel Maeso
Ansible([email protected]
Deeper Dive [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Set_fact Example
---
- hosts: nxos
tasks:
Create JSON printout
- nxos_command:
commands: "show ip arp | json"
provider: "{{cli}}"
Convert JSON printout to variable
register: result
- set_fact: json_result="{{ result.stdout[0] }}"
- set_fact: arp_table="{{ json_result.TABLE_vrf.
ROW_vrf.TABLE_adj }}"
---
- hosts: all
connection: local
tasks:
- template: src={{ansible_device_os}}/common.j2
dest={{dir}}/{{inventory_hostname}}.txt
vars:
dir: configs
Format
• “key=value key=value key=value” format (quotes are needed for multiple
key/value pairs)
• Quoted JSON string
• “@somefile.json” includes extra variables from JSON file
---
- hosts: nxos
tasks:
- nxos_nxapi:
provider: "{{cli}}"
state: "{{API|default('started')}}"
…
- tasks:
- raw: "{{show arp}}"
register: show
- local_action: >
copy content={{show.stdout}}
dest={{inventory_hostname}}.arp.txt
Demo 12
20This material is copyrighted
© ipSpace.net 2016 and licensed for the sole use by Mikel Maeso
Ansible([email protected]
Deeper Dive [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Reference:
Play and Task
Execution
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
How Ansible Playbooks Really Work
Cleanup
---
- name: execute show arp
gather_facts: no
tasks:
- raw: "show arp"
when: "'ios' in group_names"
register: show
---
- name: generate configurations
connection: local
serial: 10
tasks:
- template: src={{ansible_device_os}}/common.j2
dest=configs/{{inventory_hostname}}.txt
name: create common part of device configuration
---
---
- name: Create directory
connection: local
gather_facts: no
tasks:
- file: path=configs state=absent
run_once: true
A task executed on Ansible host might not have to be executed more than once
• Example: delete or create a directory for configuration files
• Solution: use run_once to execute the task once (in context of a random host)
---
- name: Create directory
tasks:
- file: path=configs state=absent
run_once: true
delegate_to: localhost
---
tasks:
- name: Create directory on local file system
local_action: file path={{output}} state=directory
run_once: true
- raw: "{{show_arp}}"
register: show
---
- hosts: all
name: Deploy configurations
gather_facts: no
tasks:
- name: copy configuration into the device running config
local_action: >
command /usr/bin/sshpass -p {{ansible_ssh_pass}}
/usr/bin/scp configs/{{inventory_hostname}}.txt
{{ansible_user}}@{{inventory_hostname}}:running-config
• command module does not use shell use full path to scp and sshpass
• Use sshpass to pass password to SCP
• Use ansible_ssh_pass and ansible_user variables to authenticate SCP
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Default Ansible Error Handling
---
- hosts: ios
tasks:
- ios_command:
commands: show version
provider: "{{cli}}"
register: result
- fail: msg="Wrong Cisco IOS version"
when: "not ('Version {{version}}' in result.stdout[0])"
• fail module fails a play (on a host) with custom error message
• Always use together with when expression
---
- hosts: ios
tasks:
- ios_command:
commands: show version
provider: "{{cli}}"
register: result
failed_when: >
not ('Version {{version}}' in result.stdout[0])
• Some tasks might succeed but are still a failure based on their results
• failed_when expression is evaluated after the task is completed
• Task is failed if the failed_when expression is true
• Equivalent to a subsequent fail task with when condition
---
- hosts: ios
any_errors_fatal: true
tasks:
- ios_command:
commands: show version
provider: "{{cli}}"
register: result
failed_when: >
not ('Version {{version}}' in result.stdout[0])
---
- name: generate device configurations
connection: local
tasks:
- assert:
that:
- syslog_host is defined
- snmp_host is defined
msg: One of the NMS servers is not defined
• Assert module fails if at least one of the that conditions is not true
• Used to validate inputs or device state
---
- hosts: ios
tasks:
- block:
- list of actions
rescue:
- actions to execute on failure
always:
- actions to execute at the end no matter what
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
The File Module
---
- hosts: localhost
connection: local
tasks:
- file: path=version_report.txt state=absent
- file: path=version_report.txt state=touch
---
- hosts: localhost
connection: local
tasks:
- file: path=version_report.txt state=absent
- file: path=version_report.txt state=touch
- lineinfile:
dest: version_report.txt
regexp: "{{inventory_hostname}}"
line: "{{inventory_hostname}} has wrong IOS version"
when: "not ('Version {{version}}' in result.stdout[0])"
The module does not use file locking. Use serial: 1 when using lineinfile on localhost
40This material is copyrighted
© ipSpace.net 2016 and licensed for the sole use by Mikel Maeso
Ansible([email protected]
Deeper Dive [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Adding Blocks to Text Files
- blockinfile:
dest: results.txt
marker: "### {mark} {{inventory_hostname}}"
block: result.stdout[0]
The module does not use file locking. Use serial: 1 when using this module on localhost
41This material is copyrighted
© ipSpace.net 2016 and licensed for the sole use by Mikel Maeso
Ansible([email protected]
Deeper Dive [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Combining Multiple
Files into an
Output File
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Assembling Multiple Files into a Single Output File
- assemble:
src: directory_path
regex: file_matching_pattern (optional)
dest: file_path
- assemble:
src: directory_path
regex: file_matching_pattern (optional)
dest: file_path
connection: local
- assemble:
src: directory_path
regex: file_matching_pattern (optional)
dest: file_path
connection: local
• Create configuration snippets with Ansible roles and combine them into device
configuration
• Create reports for every managed device and combine them into a single
summary report
post_tasks:
- assemble:
src: "{{build_dir}}/{{inventory_hostname}}"
dest: "{{build_dir}}/{{inventory_hostname}}.conf"
- assemble:
src: {{configs}}/changes
dest: {{configs}}/changes.txt
delegate_to: localhost
run_once: true
Change files created for individual managed devices are combined into a single
changes.txt file
• Assembly process is delegated to localhost and executed only once
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Ansible Loops
Sometimes you have to execute a single task multiple times:
• For every item in a list
• For every key/value pair in a dictionary
• For first file found
• Until a certain condition is met
Also possible:
• Looping over nested lists
• Looping over parallel sets of data
• Looping over a list of files (or all files matching a pattern)
• Executing a task block or an included task list in a loop
---
- hosts: ios
tasks:
- name: "Ping targets from IOS devices"
ios_command: commands="ping {{item}}" …
register: results
with_items: "{{ping_target}}"
---
…
Executes the Ansible task for every item in ping_target:
with_items list - '172.16.1.1'
• with_items value must be a list (not a string) - '172.16.1.12'
• item variable contains current item value - '172.16.1.100'
- '172.16.1.105'
• Results variable is a list of module results
(example: results[0].stdout[0])
---
- hosts: ios
tasks:
- name: "Ping targets from IOS devices"
ios_command: commands="show standby neigh {{item.key}}" …
register: results
with_dict: "{{intefaces}}"
---
…
Executes the Ansible task for every key/value pair in interfaces:
with_dict dictionary
Fa0/0: { ip: … }
• item.key contains current key value Fa0/1: { ip: … }
• item.value contains current value
• Use item.value.ip to access interface IP address
• Results variable is a list of module results
---
- hosts: ios
tasks:
- name: "Check interface status"
ios_command: commands="show interface dialer 1" …
register: ifstate
until: ifstate.stdout[0].find("protocol is up") > 0
retries: 5
delay: 10
• Executes an Ansible task until the condition is met or the task has been
retried too many times
• Use to check interface status, OSPF/BGP neighbor state…
---
- hosts: all
tasks:
- include_vars: "{{ item }}"
with_first_found:
- nodes.yml
- "{{ inventory_dir }}/nodes.yml"
• Executes the task with item set to the path to the first file found in the list
• Use to specify alternate file paths (locations of configuration files)
This material is copyrighted and licensed for the sole use by Mikel Maeso ([email protected] [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Extracting Attributes from Lists of Dictionaries
…
- set_fact:
target_list: "{{ vlans|map(attribute='id')|list }}"
vlans:
- { id: "100", name: "mgmt", subnet: "172.16.1.0/24"}
- { id: "101", name: "web", subnet: "192.168.201.0/24"}
…
- fail: msg="Extra VLAN configured on {{inventory_hostname}}"
when: "{{ vlans_list | difference(target_list) }}"
name: |
vlans|selectattr('id','equalto',target_vlan)|
map(attribute='name')|first
vlans:
- { id: "100", name: "mgmt", subnet: "172.16.1.0/24"}
- { id: "101", name: "web", subnet: "192.168.201.0/24"}
Required operation:
• Find VLAN name for specified VLAN ID
Steps
• Start with the vlans list of dictionaries
• Select all dictionaries from the list where the id key has value equal to target_vlan
• Select attribute name from all selected dictionaries
• selectattr and map are generators select the first item from the generator
- ios_command:
commands: "show ip interface brief | exclude Interface"
register: printout
- set_fact:
intf: |
{{printout.stdout_lines[0] |
map('regex_findall','^([A-Za-z]+[0-9./]+)') |
map('join') | list }}
Steps
• regex_findall performs a regular expression match and returns a list of matched groups
(in our case, the list has a single item, but it’s still a list)
• regex_findall within a map returns a list of groups for every input item we get a list of
lists of groups
• join within a map merges inner lists into strings we get a list of strings (interface
names)
• map is a generator we have to use list filter to make a list out of its output
More @ http://automation.ipspace.net/Example:Ansible_Regex
59This material is copyrighted
© ipSpace.net 2016 and licensed for the sole use by Mikel Maeso
Ansible([email protected]
Deeper Dive [85.87.178.33]). More information at http://www.ipSpace.net/Webinars
Questions?