Multiple with_items in an Ansible module block - linux

I want to create multiple logical volumes with a variable file but it return a sintax error found character that cannot start any token, I have tried in different ways but still doesn't work
main.yml
---
- name: playbook for create volume groups
hosts: localhost
become: true
tasks:
- include_vars: vars.yml
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item.var1 }}"
size: "{{ item.var2 }}"
with_items:
- { var1: "{{ var_lv_name }}", var2: "{{ var_lv_size }}" }
vars.yml
var_lv_name:
- lv05
- lv06
var_lv_size:
- 1g
- 1g

Use with_together. Test it first. For example,
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
with_together:
- "{{ var_lv_name }}"
- "{{ var_lv_size }}"
gives (abridged)
msg: 'Create lv: lv05 size: 1g'
msg: 'Create lv: lv06 size: 1g'
Optionally, put the declaration below into the file vars.yml
var_lv: "{{ var_lv_name|zip(var_lv_size) }}"
This creates the list
var_lv:
- [lv05, 1g]
- [lv06, 1g]
Use it in the code. The simplified task below gives the same results
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
loop: "{{ var_lv }}"

The previous answer it's totally correct but In my humble opinion we should be getting into the new way to do the things with loop and filters.
Here's my answer:
---
- name: playbook for create volume groups
hosts: localhost
gather_facts: no
become: true
vars_files: vars.yml
tasks:
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item[0] }}"
size: "{{ item[1] }}"
loop: "{{ var_lv_name | zip(var_lv_size) | list }}"
In this answer you're using the new way to use loops with keyword loop and using filters like zip and turning the result into a list type for iteration in the loop.

Related

Ansible sorting doesn't pick the highest number

I'm trying to get few docker tags from Artifactory docker api and pick the latest tag in the list using Anisble sort but when I try sort its picking the single digit value over double digits ( example 9 over 15)
here are some example tags output list
the api output looks similar to this:
registered to tag_output
{
1.1.0-ab.7-9,
1.1.0-ab.8-10,
1.1.0-ab.9-12,
}
With the regex it picks up last char in the string after "-" which i'm trying to sort.
https://regex101.com/r/O7xH4u/1
using the same regex in the Ansible tasks when doing the sort, ansible is picking 1.1.0-ab.7-9 as it thinks 9 is greater then 12 (however, if we add '0' before 9 then it picks 12)
- name: Get tags
command: curl -u "{{artifactoryUser}}:{{artifactoryPwd}}" https://artifactory-mars.cd.genesaas.io/v2/xxxx/releases-candidates/1.1.0/from-service/tags/list
register: tag_output
- name: SetFact TAG
set_fact:
DCM_TAG: "{{ _dict|dict2items| sort(attribute='key')| map(attribute='value')|last }}"
vars:
_index: "{{ (tag_output.stdout | from_json).tags|map('regex_replace', '^(.*)-(.*)$', '\\2')|list }}"
_dict: "{{ dict(_index|zip((tag_output.stdout | from_json).tags)) }}"
output:
TASK [SetFact TAG] **********************************************************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"DCM_TAG": "1.1.0-ab.7-9"}, "changed": false}
but I need expected output:
1.1.0-ab.9-12
Put the tags into a list
tags_list: "{{ tag_output.keys()|list }}"
gives
tags_list:
- 1.1.0-ab.7-9
- 1.1.0-ab.8-10
- 1.1.0-ab.9-12
Parse the attributes
- set_fact:
tags: "{{ tags|d([]) + [{'tag': item,
'major': major|int,
'minor': minor|int}] }}"
loop: "{{ tags_list }}"
vars:
ext: "{{ item|split('.')|last }}"
major: "{{ ext|split('-')|first }}"
minor: "{{ ext|split('-')|last }}"
gives
tags:
- major: 7
minor: 9
tag: 1.1.0-ab.7-9
- major: 8
minor: 10
tag: 1.1.0-ab.8-10
- major: 9
minor: 12
tag: 1.1.0-ab.9-12
Now, find the largest major and minor, and select the tag
- debug:
msg: |
Latest version: {{ tags_max.major }}-{{ tags_max.minor }}
DCM_TAG: {{ DCM_TAG }}
vars:
major_max: "{{ tags|map(attribute='major')|max }}"
tags_major_max: "{{ tags|selectattr('major', 'eq', major_max|int) }}"
minor_max: "{{ tags_major_max|map(attribute='minor')|max }}"
tags_max: "{{ tags_major_max|selectattr('minor', 'eq', minor_max|int)|first }}"
DCM_TAG: "{{ tags_max.tag }}"
gives
Latest version: 9-12
DCM_TAG: 1.1.0-ab.9-12
Note
Testing multiple minors, e.g.
tag_output:
{
1.1.0-ab.7-9,
1.1.0-ab.8-10,
1.1.0-ab.9-10,
1.1.0-ab.9-12,
1.1.0-ab.9-7,
}
gives the correct result too
Latest version: 9-12
DCM_TAG: 1.1.0-ab.9-12

Ansible - load multiplne local yml files and merge their content

my host_vars file has about 5k lines of yml code. So I would like to have separate yml files - one file per one service.
Simplified example:
user#test $ cat production/split_configs/a.example.net.yml
my_array:
- a.example.net
user#test $ cat production/split_configs/b.example.net.yml
my_array:
- b.example.net
user#test $ cat webhosts.yml
- hosts: myservers
pre_tasks:
- name: merge ansible arrays
tags: always
delegate_to: localhost
block:
- name: find config files
find:
paths: production/configs/
patterns: '*.yml'
register: find_results
- name: aaa
debug:
msg: "{{ find_results.files }}"
- name: bbb
debug:
msg: "{{ item.path }}"
with_items: "{{ find_results.files }}"
- name: ccc
debug:
msg: "{{ lookup('file', 'production/configs/a.example.net.yml') }}"
- name: ddd
debug:
msg: "{{ lookup('file', item.path) }}"
loop: "{{ find_results.files }}"
tasks:
- name: eee
debug:
msg: "{{ my_array }}"
The goal is to merge content of both arrays an print the merged content in task eee:
my_array:
- a.example.net
- b.example.net
Task aaa print informations about files (path, mode, uid, ...) - it works.
Tasks bbb, and ddd print nothing. I do not understand why.
Task ccc print content of file. But the path is written in playbook :-(
After load files I need to merge them. My idea is to use something like set_fact: my_array="{{ my_array + my_array }}" in task with with_items: "{{ find_results.files }}". Is it good idea? Or how better I can do it?
For example, the tasks below do the job
- include_vars:
file: "{{ item }}"
name: "my_prefix_{{ item|regex_replace('\\W', '_') }}"
with_fileglob: production/split_configs/*
- set_fact:
my_vars: "{{ my_vars|d({})|
combine(lookup('vars', item),
recursive=True,
list_merge='append') }}"
loop: "{{ q('varnames', 'my_prefix_.*') }}"
give
my_vars:
my_array:
- a.example.net
- b.example.net
You can use the simple cat commnd to merge the files into the one file and later include var file for example -
- raw: "cat production/split_configs/* >my_vars.yml"
- include_vars: file=my_vars.yml name=my_vars
will give you the result -
my_vars:
my_array:
- a.example.net
- b.example.net

Extend Volume Group using Ansible

I have trying to extend the VG via ansible passing the pvname by variable, however I really don't understand why is not working.
Below you can see my code.
Variable file:
new_disk:
- diskname: /dev/sdc
pvname: /dev/sdb1, dev/sdc1
vgname: datavg
lvm_settings:
- lv_name: datalv
lv_size: +100%FREE
fs_name: ansible_fs_test
lvpath: /dev/mapper/datavg-datalv
filesystem_type: ext4
tasks file:
include_vars: "{{ vm_name }}.yml"
- name: First disk partition settings
block:
- name: Create a new primary partition
community.general.parted:
device: "{{ item.diskname }}"
number: 1
state: present
with_items: "{{ new_disk }}"
register: partition_status
rescue:
- name: Debug messages to check the error
debug:
msg: "{{ partition_status }}"
- name: Extending the Volume Group
community.general.lvg:
vg: "{{ vgname }}"
pvs: "{{ pvname }}"
pvresize: yes
Below, you can see the error message:
TASK [resize_fs_linux : Extending the Volume Group] **********************************************************************************************************************************************************fatal: [10.1.33.225]: FAILED! => {"changed": false, "msg": "Device /home/icc-admin/ dev/sdc1 not found."}
Do you know have any idea why is not working?
I really appreciate your help and time
Best Regards,
For it works that way:
Variable file
diskname:
- /dev/sdb
- /dev/sdc
disks_settings:
- vgname: datavg
pvname:
- /dev/sdb1
- /dev/sdc1
lvm_settings:
- vgname: datavg
lv_name: datalv
lv_size: +100%FREE
fs_name: ansible_fs_test
lvpath: /dev/mapper/datavg-datalv
filesystem_type: ext4
Tasks file:
---
# tasks file for resize_fs_linux
- include_vars: "{{ vm_name }}.yml"
- name: First disk partition settings
block:
- name: Create a new primary partition
community.general.parted:
device: "{{ item }}"
number: 1
state: present
with_items: "{{ diskname }}"
register: partition_status
run_once: true
rescue:
- name: Debug messages to check the error
debug:
msg: "{{ partition_status }}"
- name: Extending the Volume Group
community.general.lvg:
vg: "{{ item.vgname }}"
pvs: "{{ item.pvname }}"
pvresize: yes
with_items: "{{ disks_settings }}"
- name: Increasing the filesystems
community.general.lvol:
vg: "{{ vgname }}"
lv: "{{ item.lv_name }}"
size: "{{ item.lv_size }}"
resizefs: true
with_items: "{{ lvm_settings }}"

Ansible to execute a task only when multiple files exist

I want to execute a task only when multiple files exist . if only single file is exist i need to ignore this task. How can i achieve this.
Am unable to achieve with the below playbook
---
- name: Standardize
hosts: test
gather_facts: false
vars:
file_vars:
- {id: 1, name: /etc/h_cm}
- {id: 2, name: /etc/H_CM}
tasks:
- block:
- name: Check if both exists
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: cm_result
- name: Move both files
shell: mv "{{ item.item }}" /tmp/merged
with_items: "{{ cm_result.results }}"
when: item.stat.exists
After check if both exist task, you can add a set fact task like this one:
- name: set facts
set_fact:
files_exist: "{{ (files_exist | default([])) + [item.stat.exists] }}"
with_items: "{{ cm_result.results }}"
And you change your move files task to:
- name: Move both files
debug:
msg: "{{ item.stat.exists }}"
with_items: "{{ cm_result.results }}"
when: false not in files_exist
You have to specify shell: mv "{{ item.item.name }}" /tmp/merged insted of shell: mv "{{ item.item }}" /tmp/merged
Check the below works?:
- name: Standardize
hosts: test
gather_facts: false
become: yes ## If needed
vars:
file_vars:
- {id: 1, name: /etc/h_cm}
- {id: 2, name: /etc/H_CM}
tasks:
- block:
- name: Check if both file exists
stat:
path: "{{ item.name }}"
with_items: "{{ file_vars }}"
register: cm_result
- debug:
var: item.stat.exists
loop: "{{ cm_result.results }}"
- name: Crate a dummy list
set_fact:
file_state: []
- name: Add true to list if file exists
set_fact:
file_state: "{{ file_state }} + ['{{ item.stat.exists }}']"
loop: "{{ cm_result.results }}"
when: item.stat.exists == true
- name: Move both files
shell: mv "{{ item.item.name }}" /tmp/merged
loop: "{{ cm_result.results }}"
when: file_state|length > 1

Ansible vmware_guest optional disk with Ansible Tower survey

I have a playbook for the creation of a VM from a template in VMware ESXi 6.7. My playbook is below. I want to only configure the second (and possible subsequent) disks if the DISK1_SIZE_GB variable is > 0. This is not working. I've also tried using 'when: DISK1_SIZE_GB is defined' with no luck. I'm using a survey in Ansible Tower, with the 2nd disk configuration being an optional answer. In this case I get an error about 0 being an invalid disk size, or when I check for variable definition I get an error about DISK1_SIZE_GB being undefined. Either way, the 'when' conditional doesn't seem to be working.
If I hardcode the size, as in the first 'disk' entry, it works fine .. same if I enter a valid size from Ansible Tower. I need to NOT configure additional disks unless the size is defined in the Tower survey.
Thanks!
---
- name: Create a VM from a template
hosts: localhost
gather_facts: no
tasks:
- name: Clone a template to a VM
vmware_guest:
hostname: "{{ lookup('env', 'VMWARE_HOST') }}"
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: 'false'
name: "{{ HOSTNAME }}"
template: RHEL-Server-7.7
datacenter: Production
folder: Templates
state: poweredon
hardware:
num_cpus: "{{ CPU_NUM }}"
memory_mb: "{{ MEM_MB }}"
disk:
- size_gb: 20
autoselect_datastore: true
- size_gb: "{{ DISK1_SIZE_GB }}"
autoselect_datastore: true
when: DISK1_SIZE_GB > 0
networks:
- name: "{{ NETWORK }}"
type: static
ip: "{{ IP_ADDR }}"
netmask: "{{ NETMASK }}"
gateway: "{{ GATEWAY }}"
dns_servers: "{{ DNS_SERVERS }}"
start_connected: true
wait_for_ip_address: yes
AFAIK this can't be accomplished in a single task. You were on the right track with when: DISK1_SIZE_GB is defined if disk: was a task and not a parameter though. Below is how I would approach this.
Create two survey questions:
DISK1_SIZE_GB - integer - required answer - enforce a non-zero
minimum value such as 20 (since you're deploying RHEL)
DISK2_SIZE_GB - integer - optional answer - no minimum or maximum
value
Create disk 1 in your existing vmware_guest task:
disk:
- size_gb: "{{ DISK1_SIZE_GB }}"
autoselect_datastore: true
Create a new vmware_guest_disk task which runs immediately afterwards and conditionally adds the second disk:
- name: Add second hard disk if necessary
vmware_guest_disk:
hostname: "{{ lookup('env', 'VMWARE_HOST') }}"
username: "{{ lookup('env', 'VMWARE_USER') }}"
password: "{{ lookup('env', 'VMWARE_PASSWORD') }}"
validate_certs: 'false'
name: "{{ HOSTNAME }}"
datacenter: Production
folder: Templates
state: poweredon
disk:
- size_gb: "{{ DISK2_SIZE_GB }}"
autoselect_datastore: true
when: DISK2_SIZE_GB is defined

Resources