How to compare interfaces - ansible

A host might have more than 2 interfaces ["lo","eth0","eth1"]
I want to run a when condition if host has only 2 interfaces ["lo","eth0"]
when: 'ansible_interfaces == 2'
but it returns:
"ansible_interfaces == 2": false
It has 2 interfaces why is it false?

you are not comparing the count of elements in ansible_interfaces to 2, but the value of variable ansible_interfaces to 2.
you should use:
when: ansible_interfaces|length == 2

Related

Ansible if else while iterating over a dictionary

I have a dictionary of dictionaries collecting data from openshift using prometheus. Now I intend to add values in all the dictionaries. But some projects don't have quota and hence some pods don't have request/limit set for cpu and memory. I am trying the following and it fails in case the key:value is not there.
If possible I want to use if else such that, if the variable exists then add the variable else use the value as 0.
- name: Total section for Projects
set_fact:
pod_count_total: "{{ (pod_count_total|int) + (item.value.pod_count|int)}}"
total_cpu_request: "{{ (total_cpu_request|float |round(2,'ceil')) + (item.value.cpu_request|float |round(2,'ceil'))}}"
total_cpu_limit: "{{ (total_cpu_limit|float |round(2,'ceil')) + (item.value.cpu_limit|float |round(2,'ceil'))}}"
total_memory_request: "{{ (total_memory_request|float |round(2,'ceil')) + (item.value.memory_request|float |round(2,'ceil'))}}"
total_memory_limit: "{{ (total_memory_limit|float |round(2,'ceil')) + (item.value.memory_limit|float |round(2,'ceil'))}}"
with_dict: "{{all_project}}"
Dictionary of dictionaries is like
ok: [127.0.0.1] => {
"msg": {
"openshift-web-console": {
"cpu_usage": 0.015,
"memory_used": 0.04,
"cpu_request": 0.301,
"memory_request": 0.293,
"pod_count": 3
},
"srv-test": {
"cpu_usage": 0.013,
"memory_used": 0.02,
"pod_count": 5
},
"test": {
"cpu_usage": 0.001,
"memory_used": 0.0,
"pod_count": 1
},
"openshift-monitoring": {
"cpu_limit": 1.026,
"cpu_request": 0.556,
"cpu_usage": 0.786,
"memory_limit": 1.866,
"memory_request": 1.641,
"memory_used": 0.14,
"pod_count": 98
}
}
}
If possible I want to use if else such that, if the variable exists then add the variable else use the value as 0.
The thing you are looking for is the default filter
total_memory_request: "{{ (
total_memory_request | default(0)
| float | round(2,'ceil')
) + (
item.value.memory_request | default(0)
| float | round(2,'ceil')
) }}"
There's a subtlety in that if the variable exists but is the empty string, you'll need to pass in the 2nd parameter to default to have it act in a python "truthiness" way: {{ "" | default(0, true) | float }} -- that might not apply to you, but if it does, you'll be glad to know what that 2nd param does

How to dynamically select json object to be used in an Ansible task?

Is it possible to select which json object to use based on some dynamic criteria?
I have a yml file:
- name: Get data
uri:
url: "foo/get_data/{{item.name}}"
return_content: yes
with_items: "{{stuff_names}}"
register: app_out
- name: Use data
uri:
url: "foo/use_data/item.json[0].id"
method: POST
with_items: "{{ app_out.results }}"
Where the call to foo/get_data/<name> returns a json array with 1 or 2 items:
Sample from foo/get_data/bar1:
[{"id": 1, "type": "x"},{"id": 2, "type": "y"}]
Sample from foo/get_data/bar2:
[{"id": 1, "type": "x"}]
In the "Use data" task is there a way to specify which json object in the array to use based on if an object exists in item or the size of item etc? Right now its hard coded to the first object item.json[0].
For example,
if item contains an object of type "y" then use that object, otherwise use item of type "x".
or
if size of item is > 1, use item of type "x".
EDIT:
Or perhaps even a separate ansible task to prune the registered app_out?
Q: "if item contains an object of type "y" then use that object, otherwise use item of type "x"
A: The play below implements this logic, I think.
- hosts: localhost
vars:
results:
- {"id": 1, "type": "x"}
- {"id": 2, "type": "y"}
- {"id": 3, "type": "z"}
tasks:
- set_fact:
my_list: "{{ results|
selectattr('type', 'defined')|
selectattr('type', 'equalto','y')|list }}"
- set_fact:
my_list: "{{ results|
selectattr('type', 'defined')|
selectattr('type', 'equalto','x')|list }}"
when: my_list|length == 0
- name: Use type y, otherwise use type x
debug:
var: my_list
gives
"my_list": [
{
"id": 2,
"type": "y"
}
]

Ansible - Calculate the vars in when condition and select the closest value from ansible_facts

I am writing a ansible playbook to select unused disks from ansible_devices. If the server has more than one unused disks, I want to pick the one same as input size/or closest to it, Size variable is user input.
following is my code:-
-name: Print disk result
- "{{ min_value }}.00 GB" <= item.value.size <= "{{ max_value }}.00 GB"
vars:
min_value: "{{ size - 2 }}"
max_value: "{{ size + 2 }}"
The item.value.size is like this for disks:-
"size": "50.00 GB" for disk1
"size": "5.00 GB" for disk2
I am getting this error:-
ERROR! Syntax Error while loading YAML.
expected <block end>, but found '<scalar>'
The error appears to have been in '/home/bhatiaa/disk5.yml': line 25, column 32, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- not item.value.links.ids
-
The error comes from this line:
- "{{ min_value }}.00 GB" <= item.value.size <= "{{ max_value }}.00 GB"
There are a few problems here. Fundamentally, you're trying to perform a numeric comparison (<=) on non-numeric values (50.00 GB, and that's never going to work. But that's not the source of your error. The error crops up because you're starting the value with a quote ("), so the YAML parser expects the entire line to be quoted, like this:
- '"{{ min_value }}.00 GB" <= item.value.size <= "{{ max_value }}.00 GB"'
That gets rid of your error message, but it's still problematic in several ways. In addition to the "numeric comparison with non-numeric values" problem, in a when conditional you're already in a Jinja template context so you don't need the {{ and }} markers. You'd want to write the expression something like this:
- '"%s.00 GB" % min_value <= item.value.size <= "%s.00 GB" % max_value
But while syntactically correct, that still suffers from the first problem I identified. we really need to come up with numeric values to use. One option would be to assume that sizes are always specified in GB and just strip it off, as in:
- min_value <= int(item.value.size[:-3]) <= max_value
Another option would be to calculate the disk size using sectors and sectorsize instead, like this:
- min_value <= (item.value.sectors * item.value.sectorsize) <= max_value
This would require min_value and max_value to be specified in bytes.
Hopefully there's enough here to point you in the right direction.

Ansible Debug msg redirection as a file

I am looking for some directions to fix an issue where i am not able to save or redirect the Ansible debug output to a JSON or md file.
- debug:
msg:
- "{{ item.results['show ip route'].splitlines() }}"
- "{{ item.results['show ip route summary'].splitlines() }}"
- "{{ item.results['show ip route 0.0.0.0'].splitlines() }}"
loop:
- "{{ out2 }}"
The above mentioned debug module runs at the very end of my playbook. The playbook mainly uses "napalm_cli" network module to collect few outputs from a device. The "napalm_cli" module output is not nicely formatted,so, i have to use splitlines.
Now i am trying to save below output as a file
ok: [lab1-r1] => (item={'failed': False, u'changed': False, u'results': {u'show ip route': u'Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP\n D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area \n N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2\n E1 - OSPF external type 1, E2 - OSPF external type 2\n i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2\n ia - IS-IS inter area, * - candidate default, U - per-user static route\n o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP\n a - application route\n + - replicated route, % - next hop override, p - overrides from PfR\n\nGateway of last resort is not set\n\n 172.16.0.0/16 is variably subnetted, 2 subnets, 2 masks\nC 172.16.10.0/24 is directly connected, GigabitEthernet0/1\nL 172.16.10.1/32 is directly connected, GigabitEthernet0/1', u'show ip route summary': u'IP routing table name is default (0x0)\nIP routing table maximum-paths is 32\nRoute Source Networks Subnets Replicates Overhead Memory (bytes)\nconnected 0 2 0 136 360\nstatic 0 0 0 0 0\napplication 0 0 0 0 0\ninternal 1 440\nTotal 1 2 0 136 800', u'show ip route 0.0.0.0': u'% Network not in table'}}) => {
"msg": [
[
"Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP",
" D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area ",
" N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2",
" E1 - OSPF external type 1, E2 - OSPF external type 2",
" i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2",
" ia - IS-IS inter area, * - candidate default, U - per-user static route",
" o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP",
" a - application route",
" + - replicated route, % - next hop override, p - overrides from PfR",
"",
"Gateway of last resort is not set",
"",
" 172.16.0.0/16 is variably subnetted, 2 subnets, 2 masks",
"C 172.16.10.0/24 is directly connected, GigabitEthernet0/1",
"L 172.16.10.1/32 is directly connected, GigabitEthernet0/1"
],
[
"IP routing table name is default (0x0)",
"IP routing table maximum-paths is 32",
"Route Source Networks Subnets Replicates Overhead Memory (bytes)",
"connected 0 2 0 136 360",
"static 0 0 0 0 0",
"application 0 0 0 0 0",
"internal 1 440",
"Total 1 2 0 136 800"
],
[
"% Network not in table"
]
]
}
Also, i would like to get rid of the content between
ok: [lab1-r1] => napalm_cli non formatted output
"msg": [
Any ideas or thoughts.
Thank You
NN
I believe you may want JSON callback (there is also a yaml one, and XMPP, and a whole list of them). The instructions for enabling them are in the fine manual, but the very short version is to just define an environment variable when calling ansible-playbook:
env ANSIBLE_STDOUT_CALLBACK=json ansible-playbook ...
(it works with ansible, too, if you just wanted to run a single task)

In Ruby how do I search a string line by line and only show lines that begin with 1

What i am wanting to do is to ssh into a ubiquiti device, run brctl showmacs br0 and only retrieve the mac addresses on the local port (1) for instance:
1 d4:ca:6d:ec:aa:fe no 0.05
would be printed/put/written-to-file because it begins with a 1 while:
2 4c:5e:0c:d5:ba:95 no 38.62
will not.
Strings respond to []; so you could take your collection #collection and :select where x[0] == '1'.
only_ones = #collection.select{|x| x[0] == '1' }
You can use SSHKit to run a remote command:
on 'ubiquiti.yourdomain.com' do
output = capture(:brctl, 'showmacs br0')
puts output.lines.select{|line| line.start_with? "1"}
end

Resources