Ansible nested loop cross list reference? - loops

I'm trying to solve this and frankly I'm beggining to think it doesn't work that way.
- name: Create directories
file:
path: "{{ item[0] }}"
state: directory
owner: some_user
group: some_group
mode: some_mode
with_nested:
- [ '/var/lib/{{ item[1] }}', '/var/lib/{{ item[1] }}/conf' ]
- [ 'app1', 'app2' ]
Apparently there's a scope issue here, I'm just not getting it.
If it's not clear enough, I want to create the app dirs first and then conf dirs inside each of them.
Thanks in advance

To create directories you need just one loop:
- name: Create directories
file:
path: "/var/lib/{{ item }}/conf"
state: directory
with_items:
- app1
- app2
From docs:
If state=directory, all immediate subdirectories will be created if they do not exist, since 1.7 they will be created with the supplied permissions
Update: in case of multiple subfolders (conf, logs, etc):
- name: Create directories
file:
path: "/var/lib/{{ item[1] }}/{{ item[0] }}"
state: directory
with_nested:
- [ 'conf', 'logs' ]
- [ 'app1', 'app2' ]

Related

Arrays in Ansible

I have a json like below
{
"nodes":[
{
"node_values":[
"[test1]",
"10.33.11.189",
"10.33.11.185"
]
},
{
"node_values":[
"[test2]",
"10.33.11.189",
"10.33.11.185"
]
}
]
}
I am trying to read only the node values and put it in the text files. I am using below ansible code
hosts: localhost
vars:
tmpdata1: "{{ lookup('file','test.json')|from_json }}"
tasks:
- name: Add mappings to /etc/hosts
blockinfile:
path: /home/s57232/Ansible-Install/Install_Inventory.txt
content: item
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.node_values[0] }}"
loop: "{{ tmpdata1 |json_query('nodes[*].node_values[*]') }}"
i am getting below error
**TASK [Add mappings to /etc/hosts] **********************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'node_values'\n\nThe error appears to have been in '/home/s57232/Ansible-Install/prepare_inventory.yml': line 14, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Add mappings to /etc/hosts\n ^ here\n"}**
When i am trying to read with with items and file format with out the blockinfile, if the same IP is there in multiple places, it is not writing, because it is looking for unique values. I am not able to proceed further. Can anyone please help me?
when i am using
- name: Add mappings to /etc/hosts
blockinfile:
path: /home/s57232/Ansible-Install/Install_Inventory.txt
content: "{{ item.node_values }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.node_values[0] }}"
loop: "{{ tmpdata1 |json_query('nodes[*]') }}"
I am getting
# BEGIN ANSIBLE MANAGED BLOCK [test1]
['[test1]', '10.33.11.189', '10.33.11.185']
# END ANSIBLE MANAGED BLOCK [test1]
# BEGIN ANSIBLE MANAGED BLOCK [test2]
['[test2]', '10.33.11.189', '10.33.11.185']
# END ANSIBLE MANAGED BLOCK [test2]
my expectation is
# BEGIN ANSIBLE MANAGED BLOCK [test1]
[test1]
10.33.11.189
10.33.11.185
# END ANSIBLE MANAGED BLOCK [test1]
# BEGIN ANSIBLE MANAGED BLOCK [test2]
[test2]
10.33.11.189
10.33.11.185
# END ANSIBLE MANAGED BLOCK [test2]
Here you are:
- name: Add mappings to /etc/hosts
blockinfile:
path: /home/s57232/Ansible-Install/Install_Inventory.txt
content: "{{ item.node_values | join('\n') }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.node_values.0 }}"
loop: "{{ tmpdata1.nodes }}"
You don't need to use JMESPath unless you wanted to filter some values. Other than that, you have two lists: one to loop over, the other to join the elements with a newline character.

Ansible - Issue with iterating through items in path

I'm quite new to Ansible and I can't seem to be able to iterate through the items in a path using "with_items".
Below is the sample code which is meant to go through some files in a path and apply the configuration to a Juniper router.
---
- name: Get Juniper Device Facts
hosts: "junos_devices"
gather_facts: false
connection: local
tasks:
- name: Update prefix-lists
junos_config:
src: prefix-lists/{{item}}
with_items: "/home/python/prefix-lists/*"
The error I'm getting is this:
failed: [192.168.216.66] (item=/home/python/prefix-lists/*) => {"changed": false, "failed": true, "item": "/home/python/prefix-lists/*", "msg": "path specified in src not found"}
Does anyone have any idea why I'm unable to do so?
Why with_items? Use with_fileglob.
From examples:
# copy each file over that matches the given pattern
- name: Copy each file over that matches the given pattern
copy:
src: "{{ item }}"
dest: "/etc/fooapp/"
owner: "root"
mode: 0600
with_fileglob:
- "/playbooks/files/fooapp/*"

ansible to pick & iterate the location name from the file sequencly

I have written a playbook to install our custom collectd and that's working , where i'm changing the configuration with lineinfile module now i have another situation where i have multiple site names like one i have in my playbook ie richmod as follows:
Prefix "collectd.dns.NA.richmond.physical.
i have multiple site name viz richmond, carry, london, boston hence looking forward to put them into a varible and call that variable to assign the value.
lets suppose when it run the playbook on first Server it should pick up "richmon" for second it should pick up carry, for third it should london and so on.. looking for ideas..
---
- name: Playbook to Install CollectD
#hosts: all[1]
hosts: all
remote_user: root
become: true
tasks:
- name: Downloading collectd
get_url:
url="http://my-dc/collectd-5.7.2.tar.gz"
dest="/opt/"
- name: Extracting collectd archive
unarchive:
src="/opt/collectd-5.7.2.tar.gz"
dest="/opt/"
remote_src=True
- name: Creating soft link to collectd Dir
file:
src: "/opt/collectd-5.7.2"
dest: "/opt/collectd"
state: link
owner: root
group: root
#############################################################################################
# Dont Disable the gather_facts at all as {{ ansible_hostname }} absolutlty depends on facts #
# ###########################################################################################
- name: Committing changes to collectd configuration....
lineinfile:
dest: "{{ item.dest }}"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
backrefs: yes
with_items:
- { dest: '/opt/collectd/etc/collectd.conf', regexp: '#Hostname "collectServer"', line: 'Hostname "{{ ansible_hostname }}"' }
- { dest: '/opt/collectd/etc/collectd.conf', regexp: '# Prefix "collectd.unix."', line: ' Prefix "collectd.dns.NA.richmond.physical."' }
- name: Copy the collectd Daemon to init ..
copy:
src: "/opt/collectd/startup/collectd"
dest: "/etc/init.d/"
remote_src: True
mode: 0755
owner: root
group: root
- name: starting collectd Service
service:
name: collectd
state: started
enabled: yes

In Ansible, how can I fetch file from multiple nodes and store this in one file centralized?

All what I need is in title, for example I want to know how I can do somethings like that :
---
- hosts: ansible-clients
tasks:
- name: Fetch source list from clients
fetch: src=/etc/apt/sources.list
dest=/tmp/allnodes.sourcelist
OR in simply way
echo remote#/etc/apt/sources.list >> local#/tmp/allnodes.sourcelist
I can create and run script in local but the only condition I have is to do all actions in one playbook.
You can use this play:
---
- hosts: ansible-clients
tasks:
- name: Fetch source list from clients
fetch:
src: /etc/apt/sources.list
flat: yes
dest: "/tmp/{{ inventory_hostname }}.sourcelist"
- name: Merge files
run_once: yes
delegate_to: localhost
shell: "cat /tmp/{{ item }}.sourcelist >> /tmp/allnodes.sourcelist"
with_items: "{{ groups['ansible-clients'] }}"
First task is used to fetch all files from remotes and store them in /tmp. inventory_hostname is used in filename to be sure it is unique.
Second task is run once on any host, and append all files (get list of hosts linked to group ansible-clients) in final file

ansible loop over list and dictionary at the same time

I am writing a playbook that ensure nodes appear in /etc/fstab.
I am using loops to prevent code duplication.
The logic is first to check if the line appears using grep (with perl regex because it is a multi line) and store the results in a register.
Then I want to add only the lines that are not in fstab file. To achieve that I need to loop over list (the register with the grep return codes) and a dictionary (that contains the fstab entries).
I am having errors with the parallel loop. I tried to follow these steps.
One or more undefined variables: 'str object' has no attribute 'item'
tasks/fstab.yaml:
---
- name: Make dirs
sudo: yes
file: path={{ item.value }} state=directory
with_dict:
"{{ fstab.paths }}"
- name: Check whether declared in fstab
sudo: no
command: grep -Pzq '{{ item.value }}' /etc/fstab
register: is_declared
with_dict:
"{{ fstab.regexs }}"
- name: Add the missing entries
sudo: yes
lineinfile: dest=/etc/fstab line="{{ item.1.item.value }}"
when: item.0.rc == 1
with_together:
- "{{ is_declared.results }}"
- "{{ fstab.entries }}"
vars/main.yml:
---
fstab:
paths:
a: "/mnt/a"
b: "/mnt/b"
regexs:
a: '\n# \(a\)\nfoo1'
b: '\n# \(b\)\nfoo2'
entries:
a: "\n# (a)\nfoo1"
b: "\n# (b)\nfoo2"
I am not using template on purpose (I want to add entries to existing files and not to over write them).
UPDATE: I see ansible has module "mount" which deals with fstab. However I am still looking for a solution to this issue because I might be needed it again later on.
I have a couple ideas as to why your original approach was failing, but let's scratch that for a moment. It looks like you're overcomplicating things- why not use a complex list var to tie it all together, and use the regexp arg to the lineinfile module instead of a separate regex task? (though your sample data should work fine even without the regexp param) Something like:
---
- name: Make dirs
sudo: yes
file: path={{ item.path }} state=directory
with_items: fstab
- name: Add the missing entries
sudo: yes
lineinfile: dest=/etc/fstab line={{ item.entry }} regexp={{ item.regex }}
with_items: fstab
fstab:
- path: /mnt/a
regex: '\n# \(a\)\nfoo1'
entry: "\n# (a)\nfoo1"
- path: /mnt/b
regex: '\n# \(b\)\nfoo2'
entry: '\n# (b)\nfoo2'

Resources