I would like to loop from s0 to s60 and from s100 to s100 with this command:
- name: "Network scan at port 22 {{ nom_base }}"
when: inventory_hostname in groups['exos_switch']
wait_for:
port: 22
host: "{{ nom_base }}-{{ item }}"
state: started
timeout: 2
with_items:
- s0
- s1
- s2
- s3
- s4
- s5
....
- s60
- s100
...
- s110
Any idea?
In respect to your question and comment a solution like
- name: Show sequence
debug:
msg: "s{{ item }}"
with_sequence:
- "0-60"
- "100-110"
tags: seq
might work for you.
- name: "Network scan at port 22 {{ nom_base }}"
wait_for:
port: 22
host: "{{ nom_base }}-s{{ item }}"
state: started
timeout: 2
with_sequence:
- "0-60"
- "100-110"
when: inventory_hostname in groups['exos_switch']
Related
Below is a snippet from a playbook. How can I print a message after the command is run for each loop item. Something like.
image.pyc is now running for item1
image.pyc is now running for item2
...
and so on
- name: Image Server(s)
command: python3 image.pyc {{item}} "{{systemVersion}}"
register: async_out
async: 7200
poll: 0
with_items: "{{hostinfo_input.hosts}}"
In a nutshell, extremely simple, untested, to be adapted to your use case specifically in terms of error/output control:
- name: Image Server(s)
ansible.builtin.command: python3 image.pyc {{ item }} "{{ systemVersion }}"
register: async_out
async: 7200
poll: 0
with_items: "{{ hostinfo_input.hosts }}"
- name: Wait for commands to finish
ansible.builtin.async_status:
jid: "{{ item.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 720
delay: 10
loop: "{{ async_out.results }}"
- name: Show async command output
ansible.builtin.debug:
msg: "{{ item.stdout }}"
loop: "{{ async_out.results }}"
- name: Good guests always clean up after themselves
ansible.builtin.async_status:
jid: "{{ item.ansible_job_id }}"
mode: cleanup
loop: "{{ async_out.results }}"
References:
async in playbooks
async_status module
debug module
Registering variables with a loop
Have a look at Ansible documentation for the debug module: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html
E.g.use it like this:
# playbook.yaml
---
- hosts: localhost
connection: local
vars:
ip_addresses:
- 10.0.0.10
- 10.0.0.11
tasks:
- name: Task
include_tasks: task.yaml
loop: "{{ ip_addresses }}"
# task.yaml
---
- name: Command
ansible.builtin.shell: "echo {{ item }}"
register: output
- name: Message
ansible.builtin.debug:
msg: |
- ip_address={{ item }}
- ouput={{ output }}
Unfortunately this does not work with async. See https://github.com/ansible/ansible/issues/22716.
I have this simple playbook:
---
- hosts: all
become: yes
vars:
my_hosts:
- 192.168.0.1
- 192.168.0.2
- 192.168.0.3
tasks:
- name: Check ports
wait_for:
port: "{{ item.1 }}"
host: "{{ item.0 }}"
timeout: 10
loop: "{{ product(my_hosts) | product([443, 80443]) | list }}"
When I run it like so ...
$ ansible-playbook -i,192.168.2.2 run_wait_for.yml
... I get this error ...
fatal: [192.168.2.2]: FAILED! => {"msg": "'product' is undefined"}
What am I doing wrong?
Fix the syntax
loop: "{{ my_hosts | product([443, 80443]) }}"
For example
- debug:
msg: "Check host: {{ item.0 }} port: {{ item.1 }}"
loop: "{{ my_hosts|product([443, 80443]) }}"
gives (abridged)
msg: 'Check host: 192.168.0.1 port: 443'
msg: 'Check host: 192.168.0.1 port: 80443'
msg: 'Check host: 192.168.0.2 port: 443'
msg: 'Check host: 192.168.0.2 port: 80443'
msg: 'Check host: 192.168.0.3 port: 443'
msg: 'Check host: 192.168.0.3 port: 80443'
I've Ansible 2.9 and this worked to me:
---
- name: Playbook for Check Ports
hosts: localhost
connection: local
gather_facts: no
vars:
my_hosts:
- 192.168.0.1
- 192.168.0.2
- 192.168.0.3
ports:
- 443
- 80443
tasks:
- name: Check Ports.
debug:
msg: "Check host: {{ item[0] }} port: {{ item[1] }}"
loop: "{{ my_hosts | product(ports) | list }}"
...
In the following playbook, I have to use search in a when: statement. Can you please tell me what is wrong with the statement when: item.name is search (domain_list)? domain_list is an array variable defined in files.yml as shown below.
---
- hosts: localhost
gather_facts: False
connection: local
vars_files:
- files.yml
vars:
infra:
- name: ironman.vcloud-lab.com
connection_state: CONNECTED
- name: hulk.vcloud-lab.com
connection_state: CONNECTED
- name: captain.vcloud-lab.com
connection_state: DISCONNECTED
- name: hawkeye.vcloud-lab.com
connection_state: DISCONNECTED
tasks:
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{esxilist | default([]) + [item]}}"
with_items: "{{infra}}"
when: item.name is search (domain_list) ## <= please correct me here, it doesn't work
- name: Show connected esxi list information
debug:
var: esxilist
files.yml
---
domain_list:
- ironman
- captain
Select the short name and test the presence in the list. For example, use regex_replace
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{ esxilist|default([]) + [item] }}"
loop: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name|regex_replace('^(.*?)\\.(.*)$', '\\1') }}"
gives
esxilist:
- connection_state: CONNECTED
name: ironman.vcloud-lab.com
- connection_state: DISCONNECTED
name: captain.vcloud-lab.com
The next option is to split the string and select the first item. For example
vars:
_short: "{{ item.name.split('.')|first }}"
gives the same result.
See How to create a Minimal, Reproducible Example. For example
- hosts: localhost
vars:
domain_list: [a, b]
infra:
- {name: a.vcloud-lab.com, state: CONNECTED}
- {name: b.vcloud-lab.com, state: DISCONNECTED}
- {name: c.vcloud-lab.com, state: CONNECTED}
tasks:
- set_fact:
l1: "{{ l1|default([]) + [item] }}"
with_items: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name.split('.')|first }}"
- debug:
var: l1
I have a question, can we pass a range of inputs with vars_prompt.
Can we achieve with_sequence
example:
From the below playbook, i am able to pass multiple port numbers split by ",".
prompt: Please enter the port name: 0/1,0/2,0/3,0/4,0/5
But when I want to pass the input of ports in a range.
prompt: Please enter the port name: 0/1-0/5
Do we have option how to run, will that works with nested loop
- hosts: localhost
gather_facts: no
vars_prompt:
- name: port_name
prompt: Please enter the port name
private: no
tasks:
- add_host:
name: "{{port_name}}"
groups: dynamically_created_hosts
with_items: "{{port_name.split(',')}}"
- name: Change port speed
port_speed_change:
switch_ip: "{{ip_address}}"
user: "{{user}}"
password: "{{password}}"
checkmode: "{{ansible_check_mode}}"
name: "{{items}}"
speed: "{{port_speed}}"
I think that's not possible directly with a library, however, you can achieve your purpose in several ways, one is using lists and loops.
Please consider two elements in your input:
I1/S1-I2/S2
I=Interface
S=Subport
In order to achieve this without errors it is necesary to run 3 loops:
Add elements in a list from your I1/S1 to I1/S_Max
Add middle elements from I_middle/0 to I_middle/S_Max
Add last elements I2/0 to I2/S2
First of all you collect your port_name variable, it is really important that you respect the sinxtax of input string:
- name: port_name
prompt: Please enter port name (format X/Y-X/Z, e.g 0/1-0/5)
private: no
Create 1st loop:
####1st cycle, first port first sub_loop
#1st sub_loop, first entry subport to maximum subport
- set_fact: port_sub_loop={{port_sub_loop|default([]) | union([item]) }}
with_sequence: start="{{ S1 }}" end="{{ S_MAX }}"
- name: Add first elements (first interface, first sub_loop)
set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ I1 }}"
- "{{ port_sub_loop }}"
2nd block is tricky, because it has to escape in case I1 >= I2 -2, you cannot use when condition with loops, so I decided to use a block with a rescue:
####2nd cycle, middle elements, subports are from 0 to S_MAX... There is a rescue because you cannot add a condition "with_sequence", in case I1 >= I2 -2 it will continue with rescue
#Set port interfaces loop ignoring first and last port
- name: run middle loop
block:
- set_fact: port_interfaces_middle={{port_interfaces_middle|default([]) | union([item]) }}
with_sequence: start="{{ I1|int + 1 }}" end="{{ I2|int - 1 }}"
#Set complete port sub_interfaces loop from 0 to S_MAX
- set_fact: port_sub={{port_sub|default([]) | union([item]) }}
with_sequence: start="0" end="{{ S_MAX }}"
# - name: Add middle elements (next ports starting from subport 0)
- set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ port_interfaces_middle }}"
- "{{ port_sub }}"
rescue:
- debug: msg='Not executed middle block'
Last part add elements for last I2:
#Set last interface sub_loop, from 0 to S2
- set_fact: port_sub_last={{port_sub_last|default([]) | union([item]) }}
with_sequence: start="0" end="{{ S2 }}"
- name: Add last elements (from last_interface)
set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ I2 }}"
- "{{ port_sub_last }}"
FINAL SCRIPT
vars_prompt:
- name: port_name
prompt: Please enter port name (format X/Y-X/Z, e.g 0/1-0/5)
private: no
tasks:
#Get first interface
- set_fact: I1={{ port_name | regex_search("^\d+") | int }}
#Get second interface
- set_fact: I2={{ port_name | regex_search("\-\d+") | regex_replace('^\-', '') | int }}
#Get first sub_interface
- set_fact: S1={{ port_name | regex_search("\d+\-") | regex_replace('\-$', '') | int }}
#Get second sub_interface
- set_fact: S2={{ port_name | regex_search("\d+$") | int }}
#Set highest/lowest subinterface value
- set_fact: S_MAX="{{ S1 if (S1 >= S2) else S2 }}"
- set_fact: S_MIN="{{ S1 if (S1 <= S2) else S2 }}"
####1st cycle, first port first sub_loop
#1st sub_loop, first entry subport to maximum subport
- set_fact: port_sub_loop={{port_sub_loop|default([]) | union([item]) }}
with_sequence: start="{{ S1 }}" end="{{ S_MAX }}"
- name: Add first elements (first interface, first sub_loop)
set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ I1 }}"
- "{{ port_sub_loop }}"
####2nd cycle, middle elements, subports are from 0 to S_MAX... There is a rescue because you cannot add a condition "with_sequence", in case I1 >= I2 -2 it will continue with rescue
#Set port interfaces loop ignoring first and last port
- name: run middle loop
block:
- set_fact: port_interfaces_middle={{port_interfaces_middle|default([]) | union([item]) }}
with_sequence: start="{{ I1|int + 1 }}" end="{{ I2|int - 1 }}"
#Set complete port sub_interfaces loop from 0 to S_MAX
- set_fact: port_sub={{port_sub|default([]) | union([item]) }}
with_sequence: start="0" end="{{ S_MAX }}"
# - name: Add middle elements (next ports starting from subport 0)
- set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ port_interfaces_middle }}"
- "{{ port_sub }}"
rescue:
- debug: msg='Not executed middle block'
####3rd cycle, last port until last element
#Set last interface sub_loop, from 0 to S2
- set_fact: port_sub_last={{port_sub_last|default([]) | union([item]) }}
with_sequence: start="0" end="{{ S2 }}"
- name: Add last elements (from last_interface)
set_fact: ports="{{ ports|default([]) | union([item.0+'/'+item.1]) }}"
with_nested:
- "{{ I2 }}"
- "{{ port_sub_last }}"
- debug: msg="{{ ports }}"
I don't add port_speed_change cause it must be a local library, I don't know how you are using it.
Regards
I'm wondering if it is possible to perform a loop in the hostvars folder when using Ansible?
Here is what I've tried but haven't had success in making it work - or is it just not possible to do?
---
list_pool: 'list ltm pool {{ items }}'
with_items:
- 'abc123'
- 'def456'
I would use the "list_pool" variable in a playbook afterward:
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "{{ list_pool }}"
validate_certs: no
delegate_to: localhost
Not sure what you mean when you say you want to loop over hostvars folder.
From what I can interpret from your tasks is: "You need to execute big-ip command list ltm <pool-name> for multiple pools in the list list_pool"
If that's what you're after, this should work:
- name: Set list_pool fact
set_fact:
list_pool: "{{ list_pool | default([]) + [item] }}"
with_items:
- 'abc123'
- 'def456'
- name: List pool
bigip_command:
server: "{{ some_server }}"
user: "{{ some_user }}"
password: "{{ some_password }}"
commands:
- "list ltm {{ item }}"
validate_certs: no
delegate_to: localhost
with_items: "{{ list_pool }}"
I got this working with the following solution:
hostvars file would look like this:
---
pre_checks:
checks:
pool:
- name: "pool_123"
- name: "pool_456"
...
And the play would look like this:
--output truncated---
- name: Fetch device host_vars
set_fact:
device_config: "{{ ((lookup('file','{{playbook_dir}}/host_vars/{{inventory_hostname}}.yml')) | from_yaml) }}"
- name: Check pool
bigip_command:
server: "{{ inventory_hostname }}"
user: "{{ remote_username }}"
password: "{{ remote_passwd }}"
commands:
- "list ltm pool {{ item }}"
validate_certs: no
with_items:
- "{{ device_config.pre_checks | json_query('checks.pool[*].name') }}"
delegate_to: localhost
when: "'active' in Active_LTM['stdout'][0]"
register: Pre_Checks
- name: Verify pool
debug: var=item
with_items: "{{ Pre_Checks.results | map(attribute='stdout_lines') | list }}"