I am trying to get the pertinent fields from a vmware module output, I am filtering with json, but the says my variable target_vm_name is undefined. how do I get a variable with only the matching criteria which is filtered by the ip address of the list of hosts I want to iterate through?
code is below:
- name: Loop thru hosts and get IPs
hosts: rpa_test
vars:
ip_addr:
"{{ lookup('dig', ansible_host) }}"
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars is not at the right place inside the community.vmware.vmware_vm_info task ,
you should put it in the debug task:
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"
What I'm trying to solve: I have several servers (in this example 3, 1 nfs server, 2 clients - they all resolve back to localhost for this minimal example), and the clients need to access shares on the server, which are created using the playbook.
The IP addresses of the clients need to be added to the respective entries in /etc/exports as they go - the list is not predefined at any given time. (In my actual playbook I use ansible facts, for this example I've added them as a variable)
In Ansible lineinfile regexp to manage /etc/exports Vladimir was so kind as to help with an initial thing, which works, but doesn't seem to be idempotent. The entries / ip addresses are added correctly the first time round, but the second run the IP addresses get re-added to the entries in /etc/exports, causing nfs to bomb out at that time (double entries)
Correct /etc/exports:
bar 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)
foo 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)
What I'm getting:
bar 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check) 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)
foo 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check) 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)
I've been butting my head around it but I can't come up with anything that works. I've distilled it down to the following playbook:
$ ansible-playbook main.yml
Content of the main.yml:
- hosts: localhost
become: yes
vars:
client_ip: 192.168.34.46
nfs_server: localhost
shares:
- bar
- foo
tasks:
- name: Mountpoint management
ansible.builtin.include_tasks: task.yml
loop: "{{ shares | default([]) }}"
loop_control:
loop_var: volume
args:
apply:
delegate_to: "{{ nfs_server }}"
- hosts: localhost
become: yes
vars:
client_ip: 192.168.34.47
nfs_server: localhost
shares:
- bar
- foo
tasks:
- name: Mountpoint management
ansible.builtin.include_tasks: task.yml
loop: "{{ shares | default([]) }}"
loop_control:
loop_var: volume
args:
apply:
delegate_to: "{{ nfs_server }}"
Second file task.yml which is needed to do a loop:
---
---
- name: Ensure /etc/exports exists
ansible.builtin.file:
path: /etc/exports
owner: root
group: root
mode: '0644'
state: touch
changed_when: False
- name: Add host {{ client_ip }} to {{ volume }}
ansible.builtin.lineinfile:
path: "/etc/exports"
regex: '^{{ volume }}(\s+)({{ ip_regex }})*({{ mount_opts_regex }})*(\s*)(.*)$'
line: '{{ volume }}\g<1>{{ ip }}{{ mount_opts }} \g<5>'
backrefs: true
vars:
ip: "{{ client_ip }}"
ip_regex: '{{ client_ip | regex_escape() }}'
mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
mount_opts_regex: '\(.*?\)'
- name: Read /etc/exports
command: "cat {{ item }}"
register: result
check_mode: no
loop:
- /etc/exports
changed_when: False
- ansible.builtin.set_fact:
content: "{{ dict(_files|zip(_lines)) }}"
vars:
_lines: "{{ result.results|map(attribute='stdout_lines')|list }}"
_files: "{{ result.results|map(attribute='item')|list }}"
- name: Add new line to /etc/exports
ansible.builtin.lineinfile:
path: "/etc/exports"
line: '{{ volume }} {{ client_ip }}{{ mount_opts }}'
vars:
mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
loop: "{{ content }}"
when: content[item] | select('search', volume)|length == 0
Well, thinking it through I finally went with constructing two lists, mapping those into a dict and checking if the values are already present. If they are, don't do anything, if not, add it.
main.yaml:
- hosts: localhost
become: yes
vars:
client_ip: 192.168.34.46
nfs_server: localhost
shares:
- bar
- foo
tasks:
- name: Mountpoint management
ansible.builtin.include_tasks: task.yml
loop: "{{ shares | default([]) }}"
loop_control:
loop_var: volume
args:
apply:
delegate_to: "{{ nfs_server }}"
- hosts: localhost
become: yes
vars:
client_ip: 192.168.34.47
nfs_server: localhost
shares:
- bar
- foo
tasks:
- name: Mountpoint management
ansible.builtin.include_tasks: task.yml
loop: "{{ shares | default([]) }}"
loop_control:
loop_var: volume
args:
apply:
delegate_to: "{{ nfs_server }}"
task.yaml:
---
- name: Ensure /etc/exports exists
ansible.builtin.file:
path: /etc/exports
owner: root
group: root
mode: '0644'
state: touch
changed_when: false
- name: Read /etc/exports
command: "cat /etc/exports"
register: result
check_mode: no
changed_when: false
- ansible.builtin.set_fact:
share_names: "{{ share_names | default([]) + [item.split(' ')[0]] }}"
share_values: "{{ share_values | default([]) + [item.split(' ')[1:]] }}"
loop:
"{{ result.stdout_lines }}"
- ansible.builtin.set_fact:
share_content: "{{ dict(share_names | zip(share_values)) }}"
- name: Add host {{ client_ip }} to {{ volume }}
ansible.builtin.lineinfile:
path: "/etc/exports"
regex: '^{{ volume }}(\s+)({{ ip_regex }})*({{ mount_opts_regex }})*(\s*)(.*)$'
line: '{{ volume }}\g<1>{{ client_ip }}{{ mount_opts }} \g<5>'
backrefs: true
vars:
ip_regex: '{{ client_ip | regex_escape() }}'
mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
mount_opts_regex: '\(.*?\)'
str: '{{ client_ip }}{{ mount_opts }}'
when: volume in share_content and str not in share_content[volume]
- name: Add new line to /etc/exports
ansible.builtin.lineinfile:
path: "/etc/exports"
line: '{{ volume }} {{ client_ip }}{{ mount_opts }}'
vars:
mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
loop: "{{ share_content }}"
when: volume not in share_content
Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.
I use ansible for create and run lxc containers in proxmox.
Run container task:
- name: "DHCP IP"
proxmox:
...
hostname: "{{ item }}"
...
pubkey: "{{ pubkey }}"
with_items:
- "{{ (servers_name_suggested | union(servers_name_list)) | unique }}"
register: output_dhcp
when: not static_ip
- set_fact:
vmid: "{{ output_dhcp.results[0].msg | regex_search('[0-9][0-9][0-9]') }}"
- name: "Start container {{ vmid }}"
proxmox:
vmid: "{{ vmid }}"
api_user: root#pam
api_password: "{{ api_password }}"
api_host: "{{ api_host }}"
state: started
when: start_lxc
It's work if launched one container, one item in task "DHCP IP". If I set
two or more item, my task started only first container. Because I am setting
output_dhcp.results[0].msg
How I can take information about all containers, as example, if I will create tree containers:
output_dhcp.results[1].msg
output_dhcp.results[2].msg
and received to
- name: "Start container {{ vmid }}"
proxmox:
vmid: "{{ vmid }}"
for run all my new cotainers.
The output_dhcp.results is a list, if you extract only the first item with a [0] you will only have the first item.
You need to transform the list into another list you can iterate over in the "Start container" task:
- set_fact:
vmids: "{{ output_dhcp.results | map(attribute='msg') | map('regex_search', '[0-9][0-9][0-9]') | list }}"
- name: "Start container {{ item }}"
proxmox:
vmid: "{{ item }}"
api_user: root#pam
api_password: "{{ api_password }}"
api_host: "{{ api_host }}"
state: started
with_items: "{{ vmids }}"
when: start_lxc
To explain the transformation part:
output_dhcp.results | map(attribute='msg') => get the msg attribute of each item of the output_dhcp.results list (http://jinja.pocoo.org/docs/dev/templates/#map)
| map('regex_search', '[0-9][0-9][0-9]') => apply the regex_search on each item of the list
| list => transform the generator into a list
I have 3 task in my ansible yml file as below.
---
- name: Instance provisioning
local_action:
module: ec2
region: "{{ vpc_region }}"
key_name: "{{ ec2_keypair }}"
instance_type: "{{ instance_type }}"
image: "{{ ec2_image}}"
zone: "{{ public_az }}"
volumes:
- device_name: "{{ device }}"
volume_type: "{{ instance_volumetype }}"
volume_size: "{{ volume }}"
delete_on_termination: "{{ state }}"
instance_tags:
Name: "{{ instance_name }}_{{ release_name }}_APACHE"
environment: "{{ env_type }}"
vpc_subnet_id: "{{ public_id }}"
assign_public_ip: "{{ public_ip_assign }}"
group_id: "{{ sg_apache }},{{ sg_internal }}"
wait: "{{ wait_type }}"
register: ec2
- name: adding group to inventory file
lineinfile:
dest: "/etc/ansible/hosts"
regexp: "^\\[{{ release_name }}\\]"
line: "[{{ release_name }}]"
state: present
- name: adding apache ip to hosts
lineinfile:
dest: "/etc/ansible/hosts"
line: "{{ item.private_ip }} name=apache dns={{ item.public_dns_name }}
with_items: ec2.instances
Now i want to check the exit status of each task whether it is success or failure.
If any one of the task fails my other task should not execute.
Please advice how to write an ansible playbook
In your first task, you have register the output to ec2.
now use fail module to stop the play if the task fails.
Ex.
register: ec2
fail:
when: "ec2.rc == 1"
here rc is the return code of the command .. we are assuming 1 for fail and 0 for success.
use fail module after every task.
Let me know if it works for you ..
Register a variable in each task and then check it in the next task. See http://docs.ansible.com/ansible/playbooks_tests.html#task-results
This is already the default behavior in Ansible. If a task fails, the Playbook aborts and reports the failure. You don't need to build in any extra functionality around this.
Maybe playbook blocks and it's error handling is to help you?
Kumar
if You want to check each task output if it is success or failure do this,
---
- name: Instance provisioning
local_action:
module: ec2
region: "{{ vpc_region }}"
key_name: "{{ ec2_keypair }}"
instance_type: "{{ instance_type }}"
image: "{{ ec2_image}}"
zone: "{{ public_az }}"
volumes:
- device_name: "{{ device }}"
volume_type: "{{ instance_volumetype }}"
volume_size: "{{ volume }}"
delete_on_termination: "{{ state }}"
instance_tags:
Name: "{{ instance_name }}_{{ release_name }}_APACHE"
environment: "{{ env_type }}"
vpc_subnet_id: "{{ public_id }}"
assign_public_ip: "{{ public_ip_assign }}"
group_id: "{{ sg_apache }},{{ sg_internal }}"
wait: "{{ wait_type }}"
register: ec2
- name: adding group to inventory file
lineinfile:
dest: "/etc/ansible/hosts"
regexp: "^\\[{{ release_name }}\\]"
line: "[{{ release_name }}]"
state: present
when: ec2 | changed
register: fileoutput
- name: adding apache ip to hosts
lineinfile:
dest: "/etc/ansible/hosts"
line: "{{ item.private_ip }} name=apache dns={{ item.public_dns_name }}
with_items: ec2.instances
when: fileoutput | changed
In your code register a variable in each and every Task if The Task has Changed to True, The Followed Task will execute otherwise it will skip that Task.