ansible find first undefined value in list - ansible

I have a list in ansible and I want to find the first "unused" item in that list.
Example list:
item001
item002
item004
item005
item100
item101
The prefix numbers in the items could be up to 999
In the example above the result should be item003.
Here is an example playbook of what I want
---
- name: Test
connection: local
hosts: localhost
vars:
my_list: []
list1:
- item107
- item002
- item004
- item001
- item007
- item101
- item604
tasks:
- name: Initialize a dummy list
set_fact:
my_list: "{{ my_list|sort }} + [ '{{ item }}' ]"
with_sequence: start=1 end=19 format=item%.3d
- name: print first unused value in my_list
debug:
msg: "{{ (my_list | difference(list1))[0] }}"
output:
PLAY [Test] *******************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Initialize a dummy list] ************************************************************************************************************************************************************************************
ok: [localhost] => (item=item001)
ok: [localhost] => (item=item002)
ok: [localhost] => (item=item003)
ok: [localhost] => (item=item004)
ok: [localhost] => (item=item005)
ok: [localhost] => (item=item006)
ok: [localhost] => (item=item007)
ok: [localhost] => (item=item008)
ok: [localhost] => (item=item009)
ok: [localhost] => (item=item010)
ok: [localhost] => (item=item011)
ok: [localhost] => (item=item012)
ok: [localhost] => (item=item013)
ok: [localhost] => (item=item014)
ok: [localhost] => (item=item015)
ok: [localhost] => (item=item016)
ok: [localhost] => (item=item017)
ok: [localhost] => (item=item018)
ok: [localhost] => (item=item019)
TASK [print first unused value in my_list] ************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "item003"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
But if I change with_sequence: start=1 end=19 to with_sequence: start=1 end=999 the tasks takes very long time and also prints 999 lines in the output I really would love to not see.

This actually worked better, didn't print any large output and was much quicker:
- name: Initialize a dummy list
set_fact:
my_list: "{{ lookup('sequence', 'start=1 count=999 format=item%.3d', wantlist=True) }}"

Related

Ansible: parsing a dictionary recursively

I'm struggling to implement something like a "recursive function" in Ansible, but it seems to be a slightly tricky task.
Let's say, I have a tree-like dictionary:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
What I need is to parse it, create some entities (let's say, a sub-directory or sub-domain, it doesn't matter what exactly) for each element which name ends with the / character and create some other entity (let's say a file or a DNS-record, it doesn't matter as well) for each element which name ends with anything else.
Here's a minimal example of the solution I'm trying to implement.
The playbook.yaml file:
- hosts:
- localhost
tasks:
- include_tasks: proceed_branch.yaml
vars:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
The proceed_branch.yaml file:
- debug:
msg: "{{ tree }}"
- debug:
msg: "Got {{ item.key }} = {{ item.value }}"
loop: "{{ tree | dict2items }}"
when: item.key is not regex('/$')
- include_tasks: proceed_branch.yaml
vars:
tree: "{{ item.value }}"
loop: "{{ tree | dict2items }}"
when: item.key is regex('/$')
And here's what I get as the output:
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
PLAY [localhost] ***************************************************************
TASK [include_tasks] ***********************************************************
Included: /tmp/ansible/proceed_branch.yaml for localhost
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"foo/": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
},
"alpha": 1
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'alpha', 'value': 1}) => {
"msg": "Got alpha = 1"
}
skipping: [localhost] => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3, 'baz/': {'delta': 4}}}})
TASK [include_tasks] ***********************************************************
fatal: [localhost]: FAILED! => {"msg": "Unable to look up a name or access an attribute in template string ({{ tree | dict2items }}).\nMake sure your variable name does not contain invalid characters like '-': dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. Unable to look up a name or access an attribute in template string ({{ tree | dict2items }}).\nMake sure your variable name does not contain invalid characters like '-': dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead."}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
What do you think, why dict2items gets the "undefined value" at some point?
Thanks in advance!
P.S. Here's what I expect to get as the result:
PLAY [localhost] ***************************************************************
TASK [include_tasks] ***********************************************************
included: /tmp/ansible/proceed_branch.yaml for localhost
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"alpha": 1,
"foo/": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
}
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'alpha', 'value': 1}) => {
"msg": "Got alpha = 1"
}
skipping: [localhost] => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3, 'baz/': {'delta': 4}}}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'alpha', 'value': 1})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3
, 'baz/': {'delta': 4}}}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'bravo', 'value': 2}) => {
"msg": "Got bravo = 2"
}
skipping: [localhost] => (item={'key': 'bar/', 'value': {'charlie': 3, 'baz/': {'delta': 4}}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'bravo', 'value': 2})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'bar/', 'value': {'charlie': 3, 'baz/': {'delta': 4}}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"baz/": {
"delta": 4
},
"charlie": 3
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'charlie', 'value': 3}) => {
"msg": "Got charlie = 3"
}
skipping: [localhost] => (item={'key': 'baz/', 'value': {'delta': 4}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'charlie', 'value': 3})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'baz/', 'value': {'delta': 4}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"delta": 4
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'delta', 'value': 4}) => {
"msg": "Got delta = 4"
}
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'delta', 'value': 4})
skipping: [localhost]
Found a pretty ugly solution, though it seems to be working. The point is to add a temporary variable (by setting a fact):
- hosts:
- localhost
tasks:
- include_tasks: proceed_branch.yaml
vars:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
- debug:
msg: "{{ tree }}"
- set_fact:
__t: "{{ tree }}"
- debug:
msg: "Got {{ item.key }} = {{ item.value }}"
loop: "{{ __t | dict2items }}"
when: item.key is not regex('/$')
- include_tasks: proceed_branch.yaml
vars:
tree: "{{ item.value }}"
loop: "{{ __t | dict2items }}"
when: item.key is regex('/$')
I hate how it looks like, though it works.
If anyone have a better idea, please, be so kind as to share.

How to skip a hostname from ansible inventory file?

My ansible inventory file has the following entry
[non_prod_servers]
oracle[1:13]
How can I eliminate hosts "oracle7" and "oracle10" from the above specification, without having to create the following entries?
[non_prod_servers]
oracle[1:6]
oracle[8:9]
oracle[11:13]
Essentially, looking for an elegant solution than what I have come up with.
Thanks in advance.
FR
IMHO, the functionality, you're looking for, is not available. You can use Python slicing inside the inventory. The inventory patterns do not apply inside the inventory file.
If you need it for automation, i.e. you want to control the process by a couple of variables the inventory and playbook below create the group dynamically
shell> cat hosts
[non_prod_servers]
localhost
[non_prod_servers:vars]
_name=oracle
_from=1
_to=13
_deny=[7,10]
shell> cat playbook.yml
---
- hosts: non_prod_servers
gather_facts: false
tasks:
- add_host:
hostname: "{{ _name }}{{ item }}"
groups: non_prod_servers_dyn
loop: "{{ range(_from, _to + 1)|difference(_deny) }}"
- hosts: non_prod_servers_dyn
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts_all
run_once: true
gives
shell> ansible-playbook -i hosts playbook.yml
PLAY [non_prod_servers] *************************************************
TASK [add_host] *********************************************************
ok: [localhost] => (item=1)
ok: [localhost] => (item=2)
ok: [localhost] => (item=3)
ok: [localhost] => (item=4)
ok: [localhost] => (item=5)
ok: [localhost] => (item=6)
ok: [localhost] => (item=8)
ok: [localhost] => (item=9)
ok: [localhost] => (item=11)
ok: [localhost] => (item=12)
ok: [localhost] => (item=13)
PLAY [non_prod_servers_dyn] **********************************************
TASK [debug] *************************************************************
ok: [oracle1] =>
ansible_play_hosts_all:
- oracle1
- oracle2
- oracle3
- oracle4
- oracle5
- oracle6
- oracle8
- oracle9
- oracle11
- oracle12
- oracle13

Trying to set conditional based on var type in ansible

I can't seem to find a filter for matching a string versus a list versus a dictionary. My goal is to run different task blocks based on a given variable is a certain variable type. Here is how it would be wrote up:
- name: BLOCK of stuff based on our var is a string
when: var | type == 'string'
block:
# bunch of tasks
- name: BLOCK of stuff based on our var is a list
when: var | type == 'array'
block:
# bunch of tasks
- name: BLOCK of stuff based on our var is a dictionary
when: var | type == 'dict'
block:
# bunch of tasks
You can have a look at the built-in jinja2 tests. With those available, the only difficulty is that there is no test to specifically find lists. So you have to check for vars being sequences and exclude strings and dicts.
Here is an example playbook:
--
- hosts: localhost
gather_facts: false
vars:
test_vars:
- "I'm a string"
- ['I', 'am', 'a', 'list']
- {this: is, a: dict}
- 10 # This is an integer
- 2.34 # and here we have a float
tasks:
- name: Check if var is a string
debug:
msg: this is a string
when: item is string
loop: "{{ test_vars }}"
- name: Check if var is a list
debug:
msg: this is a list
when:
- item is sequence
- item is not string
- item is not mapping
loop: "{{ test_vars }}"
- name: Check if var is a dict
debug:
msg: this is a dict
when: item is mapping
loop: "{{ test_vars }}"
which gives:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [Check if var is a string] ********************************************************************************************************************************************************************************************************
ok: [localhost] => (item=I'm a string) => {
"msg": "this is a string"
}
skipping: [localhost] => (item=['I', 'am', 'a', 'list'])
skipping: [localhost] => (item={'this': 'is', 'a': 'dict'})
skipping: [localhost] => (item=10)
skipping: [localhost] => (item=2.34)
TASK [Check if var is a list] **********************************************************************************************************************************************************************************************************
skipping: [localhost] => (item=I'm a string)
ok: [localhost] => (item=['I', 'am', 'a', 'list']) => {
"msg": "this is a list"
}
skipping: [localhost] => (item={'this': 'is', 'a': 'dict'})
skipping: [localhost] => (item=10)
skipping: [localhost] => (item=2.34)
TASK [Check if var is a dict] **********************************************************************************************************************************************************************************************************
skipping: [localhost] => (item=I'm a string)
skipping: [localhost] => (item=['I', 'am', 'a', 'list'])
ok: [localhost] => (item={'this': 'is', 'a': 'dict'}) => {
"msg": "this is a dict"
}
skipping: [localhost] => (item=10)
skipping: [localhost] => (item=2.34)
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible - Looping and Debug/Register

I have the following task.
However, it doesn't output the output for each ping that is produced, and I only get a 1 x output. When there should be x 5.
Task
tasks:
- name: "Check Connectivity (ping)"
nxos_ping:
provider: "{{ nxos_ssh }}"
source: "{{ hostvars[inventory_hostname]['lo0_ipaddr'] }}"
vrf: default
dest: "192.168.1.{{item}}"
with_sequence: start=1 end=5
register: out
- debug:
msg:
- "command: {{ out['results'][0]['commands'][0] }}"
Example
TASK [Check Connectivity (ping)] ***************************************************************************************************
ok: [spine-nxos-1] => (item=1)
ok: [spine-nxos-2] => (item=1)
ok: [spine-nxos-2] => (item=2)
ok: [spine-nxos-1] => (item=2)
ok: [spine-nxos-1] => (item=3)
ok: [spine-nxos-2] => (item=3)
ok: [spine-nxos-1] => (item=4)
ok: [spine-nxos-2] => (item=4)
ok: [spine-nxos-1] => (item=5)
ok: [spine-nxos-2] => (item=5)
TASK [debug] ***********************************************************************************************************************
ok: [spine-nxos-1] => {
"msg": [
"command: ping 192.168.1.1 count 5 source 192.168.1.1 vrf default"
]
}
ok: [spine-nxos-2] => {
"msg": [
"command: ping 192.168.1.1 count 5 source 192.168.1.2 vrf default"
]
}
Your code is working correct, as you only print out the first element of the registered output. If you want to see all your commands you should replace the last line of your playbook with:
- "command {{ out | json_query('results[*].commands[*]') }}"
or loop through your output corresponding to your sequence:
debug:
msg:
- "command {{ out.results[item|int].commands[0]}}"
with_sequence: start=0 end=2

In Ansible, how can I iterate over stdout with an array?

Ansible v2.6.3
I have the simple task, which gets the AWS ARNs in my jenkins ECS cluster
tasks:
- command: aws ecs list-container-instances --cluster jenkins
register: jenkins_ecs_containers
- debug: var=jenkins_ecs_containers.stdout
and has the following output
TASK [debug] *******************************************************************
ok: [localhost] => {
"jenkins_ecs_containers.stdout": {
"containerInstanceArns": [
"arn:aws:ecs:us-east-1:arn0",
"arn:aws:ecs:us-east-1:arn1"
]
}
}
How can I iterate over the ARNs? I tried
- debug: var=item
with_items: jenkins_ecs_containers.stdout.containerInstanceArns
gives
TASK [debug] *******************************************************************
ok: [localhost] => (item=jenkins_ecs_containers.stdout.containerInstanceArns) => {
"item": "jenkins_ecs_containers.stdout.containerInstanceArns"
}
or
- debug: var=item
with_items: "{{ jenkins_ecs_containers.stdout.containerInstanceArns }}"
gives
TASK [debug] *******************************************************************
fatal: [localhost]: FAILED! => {"msg": "'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'containerInstanceArns'"}
to retry, use: --limit #/Users/cfouts/git-repos/ansible/playbooks/loop.retry
Thanks!
I created a file with your output. So I used set_fact. Otherwise, it's just a string, not a JSON object:
tasks:
- command: cat files/stdout.txt
register: result
- debug: var=result.stdout
- set_fact:
jenkins_ecs_containers: "{{ result.stdout }}"
- debug:
msg: "{{ item }}"
with_items: "{{ jenkins_ecs_containers.containerInstanceArns }}"
This gave me the following output:
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [command] *****************************************************************
changed: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"result.stdout": {
"containerInstanceArns": [
"arn:aws:ecs:us-east-1:arn0",
"arn:aws:ecs:us-east-1:arn1"
]
}
}
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => (item=None) => {
"msg": "arn:aws:ecs:us-east-1:arn0"
}
ok: [localhost] => (item=None) => {
"msg": "arn:aws:ecs:us-east-1:arn1"
}
PLAY RECAP *********************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0
You can iterate over like this:
- debug:
msg: "{{ item[1] }}"
with_subelements:
- "{{ jenkins_ecs_containers }}"
- containerInstanceArns
Go through this link, it will make it clearer.

Resources