I'm trying to get a debug message from the results of a previous loop, but I can't get just the bit that I want from it. Ansible keeps giving me the entire result instead just the line I'm asking for.
Here are the 2 modules I'm using:
- name: Make the curl call
uri:
url: https://www.theurl.com
method: GET
return_content: yes
headers:
XXXX: "xxxxxxxxxx"
loop: "{{ simplelist }}"
register: this
- name: just testing
debug:
msg: "I just want to see: {{ item.json.section.test }}"
loop: "{{ this.results}}"
As you can see from the msg, I'm just trying to output that specific value, but what Ansible gives me is:
{
"ansible_loop_var": "item",
"_ansible_no_log": false,
"item": {
"content_length": "",
"cookies": {},
"via": "1.1 varnish",
"connection": "",
"vary": "Accept-Encoding",
"x_timer": "",
"access_control_allow_methods": "OPTIONS, HEAD, GET, PATCH, DELETE",
"x_cache_hits": "0",
"failed": false,
"access_control_allow_credentials": "true",
"content": blah blah blah,
"json": { the json },
"changed": false,
"msg": "I just want to see: False",
So it is setting the message, as you can see from the last line, and it is getting the correct value, but it's not outputting that message. How can I get just the message to be output? I tested and I know that I can get the value because the msg has False and I tested with doing a fail/when with that value.
What you are seeing looks like a verbose output of ansible-playbook running with the -v[vv] option. You can drop that option to decrease verbosity.
Meanwhile, even in non-verbose mode, and whatever module your are using, when going over a loop, ansible outputs a label for each iteration, roughly looking like the following (watch for the (item=....) part of the screen).
TASK [test] *******************************************************************************
ok: [localhost] => (item={'a': 1, 'b': 2}) => {
"msg": "This is the value of a: 1"
}
ok: [localhost] => (item={'a': 3, 'b': 4}) => {
"msg": "This is the value of a: 3"
}
By default, the label is a the full item your are currently looping over. But you can change this label in the loop_control parameter which can be a little too verbose for complex data structures. If you really want an empty label your can use the following example. But you will still get ok: [server1] => (item=) => prepended to each iteration output.
- name: just testing
debug:
msg: "I just want to see: {{ item.json.section.test }}"
loop: "{{ this.results }}"
loop_control:
label: ""
For more info see limiting loop output with label
Related
debug:
msg: "{{backup_facts | replace(',world',',{{custom}}')}}"
register: replace_csv
in the above code, I am trying to replace a string/word in a variable with a variable.
so in this case these are the vars:
backup_facts = "hello,world"
custom = "prab"
so the end results I would like is: "hello, prab" after the replace. But everytime I run the playbook, I get the following:
"replace_csv": {
"changed": false,
"failed": false,
"msg": "hello,{{custom}}"
}
I have attempted different variation such as putting a quotation around the curly brackets or not putting the curly brackets at all but no results.
Q: "The result is: hello,prab after the replace."
Keep it simple. Put the parameters into variables. This will simplify the code. For example,
- hosts: localhost
vars:
backup_facts: "hello,world"
custom: "prab"
tasks:
- debug:
msg: "{{ backup_facts|replace(old, new) }}"
vars:
old: ",world"
new: ",{{ custom }}"
gives (abridged)
ok: [localhost] =>
msg: hello,prab
So here is my current playbook
---
- hosts: SWITCHES
gather_facts: no
tasks:
- name: Show Interface Status
ios_command:
commands:
- show int status
register: out
- debug: var=out.stdout_lines
I basically want to take this script, and then disable all the ports in the "notconnect" state, meaning all the ports with nothing connected to them. Is there a way I can add a "when" statement to this, so that when "show interface status" comes back, it looks at all the ports that are not connected and disables them by applying the "shutdown" command to each interface? I think a "when" statement is what I am needing to do, but not sure where to get started with it. Or is there a better way to accomplish this?
Is there a python script that could accomplish this as well?
You should use ios_facts to retrieve a dictionary containing all the interfaces. Then you can iterate over that dictionary to shutdown the interfaces that are not connected.
If you run your playbook using the -vvv switch, you will see the all the variables collected by ios_facts.
I believe in Ansible 2.9 and later, Ansible gathers the actual network device facts if you specify "gather_facts: yes". With Ansible 2.8 or older, you need to use the "ios_facts" module.
---
- hosts: SWITCHES
gather_facts: no
tasks:
- name: gather IOS facts
ios_facts:
- name: Shutdown notconnect interfaces
ios_config:
lines: shutdown
parents: "interface {{ item.key }}"
with_dict: "{{ ansible_net_interfaces }}"
when: item.value.operstatus == "down"
Here is an example from part of a collected "ansible_net_interfaces" variable:
{
"ansible_net_interfaces": {
"GigabitEthernet0/0": {
"bandwidth": 1000000,
"description": null,
"duplex": "Full",
"ipv4": [],
"lineprotocol": "down",
"macaddress": "10b3.d507.5880",
"mediatype": "RJ45",
"mtu": 1500,
"operstatus": "administratively down",
"type": "RP management port"
},
"GigabitEthernet1/0/1": {
"bandwidth": 1000000,
"description": null,
"duplex": null,
"ipv4": [],
"lineprotocol": null,
"macaddress": "10b3.d507.5881",
"mediatype": "10/100/1000BaseTX",
"mtu": 1500,
"operstatus": "down",
"type": "Gigabit Ethernet"
},
"GigabitEthernet1/0/10": {
"bandwidth": 1000000,
"description": "Telefon/PC",
"duplex": null,
"ipv4": [],
"lineprotocol": null,
"macaddress": "null,
"mediatype": "10/100/1000BaseTX",
"mtu": 1500,
"operstatus": "down",
"type": "Gigabit Ethernet"
},
"GigabitEthernet1/0/11": {
"bandwidth": 1000000,
"description": null,
"duplex": null,
"ipv4": [],
"lineprotocol": null,
"macaddress": "10b3.d507.588b",
"mediatype": "10/100/1000BaseTX",
"mtu": 1500,
"operstatus": "down",
"type": "Gigabit Ethernet"
}
}
The value of the "ansible_net_interfaces" variable is a dictionary. Each key in that dictionary is the interface name, and the value is a new dictionary containing new key/value pairs. The "operstatus" key will have a value "down" when the interface is not connected.
Using "with_dict" in the "ios_config" task loops through all top-level key/value pairs in the dictionary, and you can use the variables in each key/value pair by referring to "{{ item.key }}" or "{{ item.value }}".
Using "when" in the "ios_config" task, you set a condition for when the task is to be executed. In this case we only want it to run when "operstatus" has a value of "down".
The "parents" parameter in the "ios_config" task specifies a new section where the configuration is to be entered, in this case the section is the interface configuration mode. The interface name is returned for each interface in the "ansible_net_interfaces" using the "{{ item.key }}" variable.
Refer to Ansibles documentation for these modules to get a better understanding of them:
https://docs.ansible.com/ansible/latest/collections/cisco/ios/ios_facts_module.html
https://docs.ansible.com/ansible/latest/collections/cisco/ios/ios_config_module.html
Hoping someone can help me figure out what I feel should be a simple nested string problem. I have the following tasks to randomly choose a string, save it to a variable and print it:
tasks:
- name: Debug Section 1
debug:
msg: "{{ item }}"
with_random_choice:
- MY_CHOICE1
- MY_CHOICE2
register: choice
- name: Set result to a fact
set_fact:
THE_CHOICE: "{{ choice.results }}"
- name: Debug Section 3
debug:
msg: "{{ THE_CHOICE }}"
The results return with:
PLAY [Testing variable] **********************************************************************************************************************************************************************************************************************
TASK [Debug Section 1] ***********************************************************************************************************************************************************************************************************************ok: [localhost] => (item=MY_CHOICE1) => {
"msg": "MY_CHOICE1"
}
TASK [Set result to a fact] ******************************************************************************************************************************************************************************************************************ok: [localhost]
TASK [Debug Section 3] ***********************************************************************************************************************************************************************************************************************ok: [localhost] => {
"msg": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"item": "MY_CHOICE1",
"msg": "MY_CHOICE1"
}
]
}
PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I would like the variable THE_CHOICE to just return the item, but I can't seem to get it to work. In the set_fact section I've tried the following:
THE_CHOICE: "{{ choice.results['item'] }}"
THE_CHOICE: "{{ choice.results.item }}"
THE_CHOICE: "{{ choice['results']['item'] }}"
All attempts result in something to the effect of this:
"The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'item'
Could anybody provide some insight into what I'm missing?
It would be simpler to use the random filter (since Ansible 1.6):
vars:
choices:
- MY_CHOICE1
- MY_CHOICE2
tasks:
- name: Set fact random
set_fact:
THE_CHOICE: "{{ choices | random }}"
About your original playbook, choice.results is a list, containing one result for each item of the loop. In your case, it only contains one result, because the with_random_choice loop only iterates once. So, in order to access your item, you must select the first result of the list with [0]:
- name: Set result to a fact
set_fact:
THE_CHOICE: "{{ choice.results[0].item }}"
I'd like to make my Ansible EIP creation idempotent. In order to do that I only want the task to run when Tag "Name" value "tag_1" doesn't exist.
However I'm not sure how I could add this as a 'when' at the end of a task.
"eip_facts.addresses": [
{
"allocation_id": "eipalloc-blablah1",
"domain": "vpc",
"public_ip": "11.11.11.11",
"tags": {
"Name": "tag_1",
}
},
{
"allocation_id": "eipalloc-blablah2",
"domain": "vpc",
"public_ip": "22.22.22.22",
"tags": {
"Name": "tag_2",
}
},
{
"allocation_id": "eipalloc-blablah3",
"domain": "vpc",
"public_ip": "33.33.33.33",
"tags": {
"Name": "tag_3",
}
}
]
(Tags are added later) I'm looking for something like:
- name: create elastic ip
ec2_eip:
region: eu-west-1
in_vpc: yes
when: eip_facts.addresses[].tags.Name = "tag_1" is not defined
What is the correct method of achieving this? Bear in mind the value can not exist in that parameter in the entire array, not just a single iteration.
Ok, I found a semi-decent solution
- name: Get list of EIP Name Tags
set_fact:
eip_facts_Name_tag: "{{ eip_facts.addresses | map(attribute='tags.Name') | list }}"
Which extracts the Name tag and puts them into an array
ok: [localhost] => {
"msg": [
"tag_1",
"tag_2",
"tag_3"
]
}
and then...
- debug:
msg: "Hello"
when: '"tag_1" in "{{ eip_facts_Name_tag }}"'
This will work, beware though, this doesn't do an exact string search. So if you did a search for just 'tag' that'd count as a hit too.
I am using the networktocode ntc-ansible module in Ansible to control Cisco IOS devices (switches currently). I successfully can use ntc_show_Command to get 'show version' and 'show ip int brief' and put the result into a local file. But when I use the -vvv at the end of the ansible-playbook command, I see structured JSON output in the terminal. How do I get a access the "module_args" and the "response" from the ntc_show_command, ie. if i use "show ip int brief" and i want to know the status of int gi1/0/5. How can I access that? And then... what playbook commands would/could I use to grab the specific data I am looking for?
Here is the output I can see when I run the playbook, with -vvv, but I don't know how to access the structured data
ok: [VTgroup_SW] => {
"changed": false,
"invocation": {
"module_args": {
"command": "show ip interface brief",
"connection": "ssh",
"connection_args": {},
"delay": "1",
"file": null,
"global_delay_factor": "1",
"host": "x.x.x.x",
"index_file": "index",
"key_file": null,
"local_file": "verification/192.168.2.101.cfg",
"optional_args": {},
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"platform": "cisco_ios",
"port": null,
"provider": null,
"secret": null,
"template_dir": "/home/melshman/.ansible/plugins/modules/ntc-
ansible/ntc-templates/templates",
"trigger_device_list": null,
"use_keys": false,
"use_templates": true,
"username": "admin"
}
},
"response": [
{
"intf": "Vlan1",
"ipaddr": "y.y.y.y",
"proto": "down",
"status": "administratively down"
},
{
"intf": "Vlan2",
"ipaddr": "x.x.x.x",
"proto": "up",
"status": "up"
},
TRUNCATED...
Here is the output of the Show ip int brief in the local file.
Interface IP-Address OK? Method Status
Protocol
Vlan1 172.16.x.xxx YES NVRAM administratively down down
Vlan2 192.168.x.xxx YES NVRAM up up
Vlan10 10.10.yy.xxx YES NVRAM administratively down down
Here is my play...
- name: VERIFY INTERFACES
ntc_show_command:
connection: ssh
platform: "cisco_ios"
command: 'show ip interface brief'
delay: 1 # delay before performing actions / commands in seconds
global_delay_factor: 1 # delay between commands
local_file: verification/{{ ansible_host }}.cfg
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
use_templates: True
template_dir: '/home/melshman/.ansible/plugins/modules/ntc-
ansible/ntc-templates/templates'
UPDATE I updated code based on suggestions as such:
I made progress, but I seem to still be missing something important. My first debug statement (below) is outputting the full output (not shown here), which works. I can set_fact successfully to 'interfaces'. I can loop through the interfaces variable and detect a condition set with the when statement, which I changed to look for all up/up interfaces. BUT... my output (shown below doesn't show the details yours showed. Mine shows (item=None) for all and an OK on items which match the criteria, but not the details your output shows (your second code block in original answer).
UPDATED - I modified the debug var to 'item.intf' and now I get intf name, but still not all details.
**QUESTION: How can I grab an output that shows only the interfaces that meet the criteria? Can this structured data be saved to a file?
UPDATE 2:::
I was able to use the copy module to copy {{ interfaces }} to a file as structured data, but when I try to just show the interfaces, and not the rest of the data, but using {{ interfaces.intf }} I get an error. (See below)
Question: How can I get a output / file that shows only the interface names?
Question: How can I get an output / file that shows only the interfaces names that are up/up?
My second debug statement (below) is outputting this, which identifies the up/up interfaces: UPDATED.
skipping: [VTgroup_SW] => (item=None)
ok: [VTgroup_SW] => (item=None) => {
"item.intf": "Vlan2"
}
skipping: [VTgroup_SW] => (item=None)
skipping: [VTgroup_SW] => (item=None)
skipping: [VTgroup_SW] => (item=None)
ok: [VTgroup_SW] => (item=None) => {
"item.intf": "GigabitEthernet1/0/27"
Additions to playbook
UPDATE 2
- debug:
var: output
- set_fact:
interfaces: "{{ output.response }}"
# shows all the intf that are up/up
- debug:
var: item.intf
when:
- '"up" in item.proto'
- '"up" in item.status'
loop: "{{ interfaces }}"
- name: Structured Data - INTERFACES
copy:
content: "{{ interfaces }}"
dest: "verification/interfaces_{{ansible_host}}.txt"
# This causes error (see traceback)
- name: Structured Data - INTERFACES with .intf
copy:
content: "{{ interfaces.intf }}"
dest: "verification/interfaces_{{ansible_host}}.txt"
Output from play; - name: Structured Data - INTERFACES (TRUNCATED)
[{"status": "administratively down", "intf": "Vlan1", "ipaddr": "172.16.0.253", "proto": "down"}, {"status": "up", "intf": "Vlan2", "ipaddr": "192.168.2.101", "proto": "up"}, {"status": "administratively down", "intf": "Vlan10", "ipaddr": "10.10.10.1", "proto": "down"}, {"status": "administratively down", "intf": "Vlan20", "ipaddr": "10.10.20.1", "proto": "down"}, {"status": "administratively down", "intf": "Vlan51", "ipaddr": "192.168.1.1", "proto": "down"}, ---- TRUNCATED
Traceback on last play; - name: Structured Data - INTERFACES with .intf
TASK [Structured Data - INTERFACES] ************************************************************************************
task path: /home/melshman/projects/ansible/from-kbyer-ios-pb.yml:117
fatal: [VTgroup_SW]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'intf'\n\nThe error appears to have been in '/home/melshman/projects/ansible/from-kbyer-ios-pb.yml': line 117, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Structured Data - INTERFACES\n ^ here\n"
You need to register the output and then start using debug and set_fact to retrieve the data structure elements that you want to retrieve:
Here I register the output and then only retrieve the 'FastEthernet4' interface.
- name: Cisco IOS Automation
hosts: pynet-rtr1
connection: local
gather_facts: no
tasks:
- name: VERIFY INTERFACES
ntc_show_command:
connection: ssh
platform: "cisco_ios"
command: 'show ip interface brief'
host: "{{ ansible_host }}"
username: "{{ ansible_user }}"
password: "{{ ansible_ssh_pass }}"
use_templates: True
template_dir: '/home/kbyers/ntc-templates/templates'
register: output
- debug:
var: output
- set_fact:
interfaces: "{{ output.response }}"
- debug:
var: item
when: '"FastEthernet4" in item.intf'
loop: "{{ interfaces }}"
Once again you could do another set_fact and save only this FastEthernet4 interface and then look at the 'proto' and 'status' fields (i.e. 'up' and 'up').
Here is what the very end of the execution looks like:
TASK [debug] *****************************************************************************************************************************************************************************
skipping: [pynet-rtr1] => (item={u'status': u'down', u'intf': u'FastEthernet0', u'ipaddr': u'unassigned', u'proto': u'down'})
skipping: [pynet-rtr1] => (item={u'status': u'down', u'intf': u'FastEthernet1', u'ipaddr': u'unassigned', u'proto': u'down'})
skipping: [pynet-rtr1] => (item={u'status': u'down', u'intf': u'FastEthernet2', u'ipaddr': u'unassigned', u'proto': u'down'})
skipping: [pynet-rtr1] => (item={u'status': u'down', u'intf': u'FastEthernet3', u'ipaddr': u'unassigned', u'proto': u'down'})
ok: [pynet-rtr1] => (item={u'status': u'up', u'intf': u'FastEthernet4', u'ipaddr': u'10.220.88.20', u'proto': u'up'}) => {
"item": {
"intf": "FastEthernet4",
"ipaddr": "10.220.88.20",
"proto": "up",
"status": "up"
}
}
skipping: [pynet-rtr1] => (item={u'status': u'down', u'intf': u'Vlan1', u'ipaddr': u'unassigned', u'proto': u'down'})
PLAY RECAP *******************************************************************************************************************************************************************************
pynet-rtr1 : ok=4 changed=0 unreachable=0 failed=0
Adding more info on the set_fact (i.e. here is how you could save a variable corresponding to only that one interface).
- set_fact:
intf_fa4: "{{ item }}"
when: '"FastEthernet4" in item.intf'
loop: "{{ interfaces }}"
- debug:
var: intf_fa4