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

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?

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

Print json output in Ansible and store in a list?

Ansible playbook:
---
- task:-
- code goes here
- name: Trying to get instance Private IP from ASG
ec2_instance_facts:
instance_ids:
- "{{ item }}"
with_items: "{{ INSTANCE_IDS_FROM_ASG }}"
register: instance_ids_result
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') }} "
- debug: var=msg
I have the output as follows:
ok: [localhost] => {
"msg": [
[
"172.31.144.74"
],
[
"172.31.147.69"
]
]
}
But, I would need the output in a list as ["172.31.144.74", "172.31.147.69"] or "172.31.147.69" "172.31.147.69".
What is the best way to print it that way?
You could flatten your list using the filter
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') | flatten }} "

how to create Ansible playbook to obtain OS versions of the remote hosts?

I'm new to ansible. I have a requirement that requires me to pull OS version for of more than 450 linux severs hosted in AWS. AWS does not provide this feature - it rather suggests us to get it from puppet or chef.
I created few simple playbooks which does not run
---
- hosts: testmachine
user: ec2-user
sudo: yes
tasks:
- name: Update all packages to latest
yum: name=* state=latest
task:
- name: obtain OS version
shell: Redhat-release
playbook should output a text file with hostname and OS version. Any insight on this will be highly appreciated.
Use one of the following Jinja2 expressions:
{{ hostvars[inventory_hostname].ansible_distribution }}
{{ hostvars[inventory_hostname].ansible_distribution_major_version }}
{{ hostvars[inventory_hostname].ansible_distribution_version }}
where:
hostvars and ansible_... are built-in and automatically collected by Ansible
ansible_distribution is the host being processed by Ansible
For example, assuming you are running the Ansible role test_role against the host host.example.com running a CentOS 7 distribution:
---
- debug:
msg: "{{ hostvars[inventory_hostname].ansible_distribution }}"
- debug:
msg: "{{ hostvars[inventory_hostname].ansible_distribution_major_version }}"
- debug:
msg: "{{ hostvars[inventory_hostname].ansible_distribution_version }}"
will give you:
TASK [test_role : debug] *******************************************************
ok: [host.example.com] => {
"msg": "CentOS"
}
TASK [test_role : debug] *******************************************************
ok: [host.example.com] => {
"msg": "7"
}
TASK [test_role : debug] *******************************************************
ok: [host.example.com] => {
"msg": "7.5.1804"
}
In a structured way:
- hosts: all
become: no
vars:
output_file: os.csv
tasks:
- block:
# For permisison setup.
- name: get current user
command: whoami
register: whoami
run_once: yes
- name: clean file
copy:
dest: "{{ output_file }}"
content: 'hostname,distribution,version,release'
owner: "{{ whoami.stdout }}"
run_once: yes
- name: fill os information
lineinfile:
path: "{{ output_file }}"
line: "{{ ansible_hostname }},\
{{ ansible_distribution }},\
{{ ansible_distribution_version }},\
{{ ansible_distribution_release }}"
# Tries to prevent concurrent writes.
throttle: 1
delegate_to: localhost
Creates a comma separated file named os.csv in execution folder. You can use any variables you want editing line:.
Ansible already provides a lot of information about the remote host in the "hostvars" variable that is automatically available.
To see information of your host named "my_remote_box_name", e.g. do
- debug: var=hostvars['my_remote_box_name']
Some OS information is in
hostvars['my_remote_box_name']['ansible_lsb']
Which, for one of my ubuntu hosts would be along:
{
"hostvars['my_remote_box_name']['ansible_lsb']": {
"codename": "xenial",
"description": "Ubuntu 16.04.1 LTS",
"id": "Ubuntu",
"major_release": "16",
"release": "16.04"
}
You can just use those variables in your playbooks and templates, using the "{{ variable_name }}" notation.
- debug: msg="My release is {{ansible_lsb.release}}"
output:
"msg": "My release is 16.04"
For a couple of windows instances:
- debug:
msg:
- "ansible_distribution {{ hostvars[inventory_hostname].ansible_distribution }}"
- "major version {{ hostvars[inventory_hostname].ansible_distribution_major_version }}"
- "version {{ hostvars[inventory_hostname].ansible_distribution_version }}"
gives:
ok: [server1] => {
"msg": [
"ansible_distribution Microsoft Windows Server 2008 R2 Standard ",
"major version 6",
"version 6.1.7601.65536"
]
}
ok: [server2] => {
"msg": [
"ansible_distribution Microsoft Windows Server 2016 Standard",
"major version 10",
"version 10.0.14393.0"
]
}
"AWS does not provide this feature " - you can check file /etc/os-release to get details of aws instance.
For example
[ec2-user#ip-xx-xx-xx ~]$ cat /etc/os-release
NAME="Amazon Linux AMI"
VERSION="2016.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2016.03"
PRETTY_NAME="Amazon Linux AMI 2016.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2016.03:ga"
HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
- name: obtain OS version
shell: Redhat-release
register: result
- name: print OS version
debug: var=result.stdout

Resources