ansible CSV header options when appending with for loop - python-3.x

i'm trying to gather network devices data using ansible and add to csv file which is working fine for me but then now i would like to have the headers to the match to whats been gathered .
- name: Playbook to collect ntp,snmp_facts and put into csv file
hosts: all
connection: network_cli
gather_facts: true
# check_mode: yes
vars:
output_path: "./reports/"
filename: "device_report_{{ date }}.csv"
vendor: CISCO
tasks:
- name: CSV - Generate output filename
set_fact: date="{{lookup('pipe','date +%Y%m%d')}}"
run_once: true
- name: CSV - Create file and set the header
lineinfile:
dest: "{{ output_path }}/{{ filename }}"
**line: hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,snmp_server_1,snmp_server_2,snmp_server_3,snmp_server_4,snmp_server_5,snmp_server_6**
create: true
state: present
- import_tasks: /path/playbooks/facts/ntp_facts/ntp_facts_get.yml
# - import_tasks: /path/playbooks/facts/snmp_facts/snmp_facts_get_2960.yml
# - import_tasks: /path/playbooks/facts/snmp_facts/snmp_facts_get_not_2960.yml
- import_tasks: /path/playbooks/facts/snmp_facts/snmp_another_test.yml
- import_tasks: /path/playbooks/facts/dns_facts/dns_facts_domain_name_get.yml
- name: CSV - Getting all the data just before printing to csv
set_fact:
csv_tmp: >
{{ ansible_net_hostname|default('N/A') }},
{{ ansible_host|default('N/A') }},
{{ ansible_net_image|default('N/A') }},
{{ ansible_net_iostype|default('N/A') }},
{{ ansible_net_model|default('N/A') }},
{{ ansible_net_serialnum|default('N/A') }},
{{ ansible_net_system|default('N/A') }},
{{ ansible_net_version|default('N/A') }},
{{ ntp_servers.gathered.servers[0].server|default('N/A') }},
{{ ntp_servers.gathered.servers[1].server|default('N/A') }},
{{ ntp_servers.gathered.servers[0].vrf|default('N/A') }},
{% set snmp_list = [] %}
{% for snmp_host in snmp_hosts %}
{% set snmp_list = snmp_list.append(snmp_host.host ~ ',' ~ snmp_host.version) %}
{% endfor %}
{{ snmp_list|join(',') }},
{{ domain_name[0]|default('N/A') }},
- name: check whats up with this csv_tmp
debug:
var: csv_tmp
- name: CSV - Write information into .csv file
lineinfile:
insertafter: EOF
dest: "{{ output_path }}/{{ filename }}"
line: "{{ csv_tmp }}"
- name: CSV - Blank lines removal
lineinfile:
path: "./{{ output_path }}/{{ filename }}"
state: absent
regex: '^\s*$'
when appending for each device using csv_tmp i have the for loop for snmp
{% for snmp_host in snmp_hosts %}
{% set snmp_list = snmp_list.append(snmp_host.host ~ ',' ~ snmp_host.version) %}
{% endfor %}
{{ snmp_list|join(',') }},
So i would not know how many snmp hosts are configured so i was thinking if there are some better ways to have this achieved or somehow have a dynamic option to generate header or have an option to make sure that each of value {{ ansible_net_hostname|default('N/A') }}, into certain specified values first and then remove any empty column .
I have a bit of time constraint so reaching out here for help .

Given the inventory for testing
shell> cat hosts
test_11 ansible_host=10.1.0.61
test_13 ansible_host=10.1.0.63
Create a dictionary first. Declare the below variables in vars. For example, in group_vars
shell> cat group_vars/all/csv_content.yml
csv_content_dict_str: |
hostname: {{ ansible_net_hostname|d('N/A') }}
ip_address: {{ ansible_host|d('N/A') }}
image: {{ ansible_net_image|d('N/A') }}
iostype: {{ ansible_net_iostype|d('N/A') }}
model: {{ ansible_net_model|d('N/A') }}
serialnum: {{ ansible_net_serialnum|d('N/A') }}
system: {{ ansible_net_system|d('N/A') }}
version: {{ ansible_net_version|d('N/A') }}
ntp_server_1: {{ ntp_servers.gathered.servers[0].server|d('N/A') }}
ntp_server_2: {{ ntp_servers.gathered.servers[1].server|d('N/A') }}
vrf: {{ ntp_servers.gathered.servers[0].vrf|d('N/A') }}
{% for snmp_host in snmp_hosts|d([]) %}
snmp_server_{{ loop.index }}: {{ snmp_host.host }}
snmp_server_version_{{ loop.index }}: {{ snmp_host.version }}
{% endfor %}
domain_name: {{ domain_name[0]|d('N/A') }}
csv_content_dict: "{{ csv_content_dict_str|from_yaml }}"
csv_content: |
{{ csv_content_dict.keys()|join(',') }}
{{ csv_content_dict.values()|join(',') }}
Without any facts collected, this gives
ok: [test_11] =>
csv_content_dict:
domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.61
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
system: N/A
version: N/A
vrf: N/A
ok: [test_13] =>
csv_content_dict:
domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.63
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
system: N/A
version: N/A
vrf: N/A
Write the files
- copy:
dest: "{{ output_path }}/{{ filename }}"
content: "{{ csv_content }}"
gives
shell> ssh admin#test_11 cat /tmp/reports/device_report_2023-01-22.csv
hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,domain_name
N/A,10.1.0.61,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A
shell> ssh admin#test_13 cat /tmp/reports/device_report_2023-01-22.csv
hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,domain_name
N/A,10.1.0.63,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A
Example of a complete playbook to write the report
shell> cat write_report.yml
- hosts: all
vars:
output_path: /tmp/reports
filename: "device_report_{{ date }}.csv"
tasks:
- set_fact:
date: "{{ '%Y-%m-%d'|strftime }}"
run_once: true
- file:
state: directory
path: "{{ output_path }}"
- block:
- debug:
var: csv_content_dict
- debug:
msg: |
{{ csv_content }}
when: debug|d(false)|bool
- copy:
dest: "{{ output_path }}/{{ filename }}"
content: "{{ csv_content }}"
Example of a complete playbook to read the report
shell> cat read_report.yml
- hosts: all
vars:
output_path: /tmp/reports
filename: "device_report_{{ date }}.csv"
tasks:
- set_fact:
date: "{{ '%Y-%m-%d'|strftime }}"
run_once: true
- community.general.read_csv:
path: "{{ output_path }}/{{ filename }}"
register: report
- debug:
var: report.list
Create, or collect facts. For example, create host_vars for testing
shell> cat host_vars/test_11/test_network_facts.yml
snmp_hosts:
- host: snmp1.example.com
version: SNMPv3
- host: snmp2.example.com
version: SNMPv3
Write the report to test_11
shell> ansible-playbook write_report.yml -l test_11
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [test_11]
TASK [set_fact] ******************************************************************************
ok: [test_11]
TASK [file] **********************************************************************************
ok: [test_11]
TASK [debug] *********************************************************************************
skipping: [test_11]
TASK [debug] *********************************************************************************
skipping: [test_11]
TASK [copy] **********************************************************************************
changed: [test_11]
PLAY RECAP ***********************************************************************************
test_11: ok=4 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Take a look at the file
shell> ssh admin#test_11 cat /tmp/reports/device_report_2023-01-22.csv
hostname,ip_address,image,iostype,model,serialnum,system,version,ntp_server_1,ntp_server_2,vrf,snmp_server_1,snmp_server_version_1,snmp_server_2,snmp_server_version_2,domain_name
N/A,10.1.0.61,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,snmp1.example.com,SNMPv3,snmp2.example.com,SNMPv3,N/A
Read the file from test_11
shell> ansible-playbook read_report.yml -l test_11
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [test_11]
TASK [set_fact] ******************************************************************************
ok: [test_11]
TASK [community.general.read_csv] ************************************************************
ok: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
report.list:
- domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.61
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
snmp_server_1: snmp1.example.com
snmp_server_2: snmp2.example.com
snmp_server_version_1: SNMPv3
snmp_server_version_2: SNMPv3
system: N/A
version: N/A
vrf: N/A
PLAY RECAP ***********************************************************************************
test_11: ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Q: "It's overwriting every time since some hosts have 5 SNMP configured and some only 2."
A: This can't happen if you write reports into separate files at the remote hosts. Add hours-minutes-seconds %H-%M-%S to the name of the CSV file
- set_fact:
date: "{{ '%Y-%m-%d-%H-%M-%S'|strftime }}"
run_once: true
Then, a new file will be created each time you run the playbook. For example,
shell> ansible-playbook write_report.yml
will create two files
shell> ssh admin#test_11 ls -la /tmp/reports
total 34
drwxr-xr-x 2 root wheel 4 Jan 23 03:30 .
drwxrwxrwt 13 root wheel 27 Jan 23 03:30 ..
-rw-r--r-- 1 root wheel 283 Jan 22 08:57 device_report_2023-01-22.csv
-rw-r--r-- 1 root wheel 283 Jan 23 03:30 device_report_2023-01-23-04-30-00.csv
shell> ssh admin#test_13 ls -la /tmp/reports
total 34
drwxr-xr-x 2 root wheel 4 Jan 23 03:30 .
drwxrwxrwt 10 root wheel 17 Jan 23 03:30 ..
-rw-r--r-- 1 root wheel 161 Jan 22 08:30 device_report_2023-01-22.csv
-rw-r--r-- 1 root wheel 161 Jan 23 03:30 device_report_2023-01-23-04-30-00.csv
The next option is to write all files to the controller (localhost). For example, create the directories
- file:
state: directory
path: "{{ output_path }}/{{ inventory_hostname }}"
delegate_to: localhost
and write the files
- copy:
dest: "{{ output_path }}/{{ inventory_hostname }}/{{ filename }}"
content: "{{ csv_content }}"
delegate_to: localhost
Then, each time you run the playbook new files will be created at the controller
shell> tree /tmp/reports/
/tmp/reports/
├── test_11
│   └── device_report_2023-01-23-04-49-27.csv
└── test_13
└── device_report_2023-01-23-04-49-27.csv
2 directories, 2 files
You can easily read the reports from the files at the controller. For example, give the CSV files
shell> tree /tmp/reports/
/tmp/reports/
├── test_11
│   ├── device_report_2023-01-23-04-49-27.csv
│   └── device_report_2023-01-23-05-32-40.csv
└── test_13
├── device_report_2023-01-23-04-49-27.csv
└── device_report_2023-01-23-05-32-40.csv
2 directories, 4 files
Read the files
- community.general.read_csv:
path: "{{ item.src }}"
register: out
with_community.general.filetree: "{{ output_path }}"
when: item.state == 'file'
loop_control:
label: "{{ item.path }}"
Declare the below variables
output_path: "/tmp/reports"
reports_str: |
{% for result in out.results|selectattr('list', 'defined') %}
{% set _keys = result.item.path|split('/') %}
{% set host = _keys|first %}
{% set report = _keys|last|regex_replace('^device_report_(.*)\.csv', '\\1') %}
- {{ host }}:
{{ report }}: {{ result.list }}
{% endfor %}
reports: "{{ reports_str|from_yaml|combine(recursive=true) }}"
reports_lists: "{{ dict(reports|dict2items|json_query('[].[key, value.keys(#)]')) }}"
give
reports:
test_11:
2023-01-23-04-49-27:
- domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.61
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
snmp_server_1: snmp1.example.com
snmp_server_2: snmp2.example.com
snmp_server_version_1: SNMPv3
snmp_server_version_2: SNMPv3
system: N/A
version: N/A
vrf: N/A
2023-01-23-05-32-40:
- domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.61
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
snmp_server_1: snmp1.example.com
snmp_server_2: snmp2.example.com
snmp_server_version_1: SNMPv3
snmp_server_version_2: SNMPv3
system: N/A
version: N/A
vrf: N/A
test_13:
2023-01-23-04-49-27:
- domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.63
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
system: N/A
version: N/A
vrf: N/A
2023-01-23-05-32-40:
- domain_name: N/A
hostname: N/A
image: N/A
iostype: N/A
ip_address: 10.1.0.63
model: N/A
ntp_server_1: N/A
ntp_server_2: N/A
serialnum: N/A
system: N/A
version: N/A
vrf: N/A
reports_lists:
test_11:
- 2023-01-23-05-32-40
- 2023-01-23-04-49-27
test_13:
- 2023-01-23-05-32-40
- 2023-01-23-04-49-27
Example of a complete playbook to read the reports at the controller
shell> cat read_report.yml
- hosts: localhost
vars:
output_path: /tmp/reports
reports_str: |
{% for result in out.results|selectattr('list', 'defined') %}
{% set _keys = result.item.path|split('/') %}
{% set host = _keys|first %}
{% set report = _keys|last|regex_replace('^device_report_(.*)\.csv', '\\1') %}
- {{ host }}:
{{ report }}: {{ result.list }}
{% endfor %}
reports: "{{ reports_str|from_yaml|combine(recursive=true) }}"
reports_lists: "{{ dict(reports|dict2items|json_query('[].[key, value.keys(#)]')) }}"
tasks:
- debug:
msg: "{{ item.src }}"
with_community.general.filetree: "{{ output_path }}"
when:
- debug|d(false)|bool
- item.state == 'file'
loop_control:
label: "{{ item.path }}"
- community.general.read_csv:
path: "{{ item.src }}"
register: out
with_community.general.filetree: "{{ output_path }}"
when: item.state == 'file'
loop_control:
label: "{{ item.path }}"
- debug:
var: reports
- debug:
var: reports_lists

Related

How to pass data from text file to ansible playbook?

I want to have text file which contains name and password
name: "Peter", "Joe", "Mark"
password: "smith", "biden", "garyy"
And I have playbook like this
---
- hosts: myhosts
become: yes
remote_user: root1
become_user: root
vars_files:
- vars.yml
vars:
ansible_ssh_private_key_file: "{{key}}"
tasks:
- name: Create users
user: name="{{item.name}}" shell=/bin/bash home="/srv/{{item.name}}" groups=root generate_ssh_key=yes ssh_key_bits=2048
loop: "{{ lookup('file', 'userspasswd.txt', wantList=True)| list }}"
- name: Set password to users
shell: echo "{{item.name}}:{{item.password}}" | sudo chpasswd
no_log: True
loop: "{{ lookup('file', 'userspasswd.txt', wantList=True)| list }}"
I am getting error like this
fatal: [xxx.xxx.xxx.xxx]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'name'\n\nThe error appears to be in '/home/root1/Documents/ansiblekernel/main.yml': line 12, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Create users\n ^ here\n"}
Is there any correct way of doing this? Cause I am new to this?
Given the file
shell> cat userspasswd.txt
name: "Peter", "Joe", "Mark"
password: "smith", "biden", "garyy"
Neither wantList=True nor the filter list will help you to parse the file because it's not a valid YAML. If you can't change the structure of the file you'll have to parse it on your own.
Declare the variables
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
give
userspasswd_lines:
- 'name: "Peter", "Joe", "Mark"'
- 'password: "smith", "biden", "garyy"'
userspasswd_values:
- - Peter
- Joe
- Mark
- - smith
- biden
- garyy
userspasswd_dict:
Joe: biden
Mark: garyy
Peter: smith
Iterate the dictionary. Test it
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"
gives
TASK [Create users] **************************************************************************
ok: [test_11] => (item=Peter) =>
msg: |-
name: Peter
shell: /bin/bash
home: /srv/Peter
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
ok: [test_11] => (item=Joe) =>
msg: |-
name: Joe
shell: /bin/bash
home: /srv/Joe
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
ok: [test_11] => (item=Mark) =>
msg: |-
name: Mark
shell: /bin/bash
home: /srv/Mark
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
TASK [Set password to users] *****************************************************************
ok: [test_11] => (item={'key': 'Peter', 'value': 'smith'}) =>
msg: echo "Peter:smith" | sudo chpasswd
ok: [test_11] => (item={'key': 'Joe', 'value': 'biden'}) =>
msg: echo "Joe:biden" | sudo chpasswd
ok: [test_11] => (item={'key': 'Mark', 'value': 'garyy'}) =>
msg: echo "Mark:garyy" | sudo chpasswd
Example of a complete playbook for testing
- hosts: myhosts
vars:
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
tasks:
- block:
- debug:
var: userspasswd_lines
- debug:
var: userspasswd_values
- debug:
var: userspasswd_dict
run_once: true
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"- hosts: myhosts
vars:
userspasswd_lines: "{{ lookup('file', 'userspasswd.txt').splitlines() }}"
userspasswd_values: "{{ userspasswd_lines|
map('split', ':')|
map('last')|
map('regex_replace', '\"', '')|
map('split', ',')|
map('map', 'trim')|
list }}"
userspasswd_dict: "{{ dict(userspasswd_values.0|
zip(userspasswd_values.1)) }}"
tasks:
- block:
- debug:
var: userspasswd_lines
- debug:
var: userspasswd_values
- debug:
var: userspasswd_dict
run_once: true
- name: Create users
debug:
msg: |
name: {{ item }}
shell: /bin/bash
home: /srv/{{ item }}
groups: root
generate_ssh_key: yes
ssh_key_bits: 2048
loop: "{{ userspasswd_dict.keys()|list }}"
- name: Set password to users
debug:
msg: 'echo "{{ item.key }}:{{ item.value}}" | sudo chpasswd'
loop: "{{ userspasswd_dict|dict2items }}"

FAILED! => {"msg": "'item' is undefined"}

Trying to create a partition and mountpoint on the Azure Disks, which are getting attached to VM on creation as part of terraform. Disks should be created based on user input through Jenkins.
Each disk was passed with a LUN number and I am fetching the device name(sdc,sdd etc.) for each disk using that LUN number and grep. In my_tasks.yml this tasks to be looped with include_tasks in playbook.yml as below:
my_tasks.yml
---
- parted:
device: "{{ volumename.stdout }}"
number: 1
state: present
- filesystem:
fstype: xfs
dev: "{{ volumename.stdout }}"
- mount:
fstype: xfs
opts: noatime
src: "{{ volumename.stdout }}"
path: "{{ item.mountpoint }}"
state: mounted
- command: blkid -s UUID -o value {{ volumename.stdout }}
register: volumename_disk
- blockinfile:
path: /etc/fstab
state: present
block: |
UUID={{ volumename_disk.stdout }} {{ volumename.stdout }} xfs defaults,noatime,nofail 0 0
playbook.yml
---
- hosts: "{{ host }}"
become: true
become_method: sudo
become_user: root
vars:
mount: "{{ lookup('file', '/home/xyz/vars.txt') }}"
tasks:
- name: Generate the Lun_Name
shell: "tree /dev/disk/azure/scsi1 | grep -i lun | awk '{print $2}'"
register: lun
- set_fact:
lun_name: "{{ lun_name|default([]) + [ { 'name': lun.stdout } ] }}"
- debug:
msg: "LUN is: {{ lun_name }}"
- name: Generate the Volume_Name
shell: echo "$(ls -l /dev/disk/azure/scsi1 |grep lun |egrep -o "([^\/]+$)")"
register: volumename
- set_fact:
volumenames: "{{ volumenames|default([]) + [ { 'name': volumename.stdout } ] }}"
- debug:
msg: "VOLUMENAME is: {{ volumenames }}"
# - debug:
# msg: "the mountpoints are {{ mount }}"
- set_fact:
mountpoint: "{{ lookup('file', '/home/xyz/vars.txt').split(',') }}"
- debug:
msg: "the mountpoints are {{ mountpoint }}"
# loop: "{{ mountpoint }}"
- include_tasks: my_tasks.yml
loop: "{{ item.volumenames | list }} {{ item.mountpoint | list }}"
loop_control:
loop_var: "{{ item }}"
fatal: [10.102.26.74]: FAILED! => {"msg": "'item' is undefined"}
Issue seems to be with loop inside a include_tasks, I'm able to get the loop working for mountpoint which is running after set_fact in playbook.yml
How can I resolve this issue or improve the code?

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

how to traverse through each line of a text file in ansible

I have to traverse through package list file which contains list of packages with their architecture. How can I feed those input to my playbook file? I found a way to get the package names alone but architecture version is not coming. This is my package_list file
nginx | x86_64
telnet| x86_64
openssh | i386
This is my playbook
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- name: get contents of architecture from packages.txt
command: cat "/root/packages.txt" | awk '{print $3}'
register: _arch
- name: Filter
theforeman.foreman.content_view_filter:
username: "admin"
password: "mypass"
server_url: "myhost"
name: "myfilter"
organization: "COT"
content_view: "M_view"
filter_type: "rpm"
architecture: "{{ _arch }}"
package_name: "{{ item }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
loop: "{{ _arch.stdout_lines }}"
Any help would be appreciated
The required output is package name and architecture should be read from packages.txt file through ansible-playbook
try this playbook:
- name: Reproduce issue
hosts: localhost
gather_facts: no
tasks:
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- debug:
msg: "package: {{ line.0 }}, arch: {{ line.1 }}"
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
result:
ok: [localhost] => (item=nginx | x86_64) => {
"msg": "package: nginx , arch: x86_64 "
}
ok: [localhost] => (item=telnet| x86_64) => {
"msg": "package: telnet, arch: x86_64 "
}
ok: [localhost] => (item=openssh | i386) => {
"msg": "package: openssh , arch: i386 "
}
for your case:
- name: Filter
theforeman.foreman.content_view_filter:
:
:
architecture: "{{ line.1 }}"
package_name: "{{ line.0 }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
following the version of ansible you could write too line: "{{ item | split('|') | list }}"
You need to split up the line into the necessary values by filtering.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Gahter package list
shell:
cmd: cat package.txt
register: _packages
- name: Show packages
debug:
msg: "Name: {{ item.split('|')[0] }}, Arch: {{ item.split('|')[1] }}"
loop: "{{ _packages.stdout_lines }}"
Further Documentation
Playbook filters - Manipulating strings
Jinja Template Designer - Filters
Further Q&A
Split string into list in Jinja?

Ansible playbook loop with with_items

I have to update sudoers.d multiple user files with few lines/commands using ansible playbook
users.yml
user1:
- Line1111
- Line2222
- Line3333
user2:
- Line4444
- Line5555
- Line6666
main.yml
- hosts: "{{ host_group }}"
vars_files:
- ../users.yml
tasks:
- name: Add user "user1" to sudoers.d
lineinfile:
path: /etc/sudoers.d/user1
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ user1 }}"
The above one is working only for user1..
If I want to also include user2 --> How to change the file name : path: /etc/sudoers.d/user1
I tried below and its not working :
Passing below users as variable to main.yml while running
users:
- "user1"
- "user2"
- name: Add user "{{users}}" to sudoers.d
lineinfile:
path: /etc/sudoers.d/{{users}}
line: '{{ item }}'
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
So, basically I want to pass in users to a variable {{users}} as user1 and user2 and wanted to use the lines for each user from users.yml and add it to respective user files (/etc/sudoers.d/user1 and /etc/sudoers.d/user2).
So /etc/sudoers.d/user1 should look like
Line1111
Line2222
Line3333
and /etc/sudoers.d/user2 should look like
Line4444
Line5555
Line6666
Try to add quotes:
users:
- "user1"
- "user2"
- name: "Add user {{users}} to sudoers.d"
lineinfile:
path: "/etc/sudoers.d/{{users}}"
line: "{{ item }}"
state: present
mode: 0440
create: yes
validate: 'visudo -cf %s'
with_items:
- "{{ users }}"
As per Ansible Documentation on Using Variables:
YAML syntax requires that if you start a value with {{ foo }} you quote the whole line, since it wants to be sure you aren’t trying to start a YAML dictionary. This is covered on the YAML Syntax documentation.
This won’t work:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
Do it like this and you’ll be fine:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
cat users.yml
---
users:
- user1:
filename: user1sudoers
args:
- Line1111
- Line2222
- Line3333
- user2:
filename: user2sudoers
args:
- Line4444
- Line5555
- Line6666
I use template here, instead of lineinfile
---
cat sudoers.j2
{% if item.args is defined and item.args %}
{% for arg in item.args %}
{{ arg }}
{% endfor %}
{% endif %}
the task content
---
- hosts: localhost
vars_files: ./users.yml
tasks:
- name: sync sudoers.j2 to localhost
template:
src: sudoers.j2
dest: "/tmp/{{ item.filename }}"
loop: "{{ users_list }}"
when: "users_list is defined and users_list"
after run the task.yml, generate two files under /tmp directory.
cat /tmp/user1sudoers
Line1111
Line2222
Line3333
cat /tmp/user2sudoers
Line4444
Line5555
Line6666

Resources