ansible 1.6 > using with_first_found in a with_items loop? - ansible

Is it possible to use with_first_found in a with_items loop such as:
- template:
dest=/foo/{{ item.name }}-{{ item.branch | default('master') }}
src={{ item }}
with_first_found:
- {{ item.name }}-{{ item.branch | default('master') }}
- {{ item.name }}.j2
- apache_site.j2
with_items: apache_sites
Can't seem to make it work using with_nested.

Combining loops is unsupported, but you can use them as lookups:
vars:
site_locations:
- {{ item.name }}-{{ item.branch | default('master') }}
- {{ item.name }}.j2
- apache_site.j2
tasks:
- template:
dest=/foo/{{ item.name }}-{{ item.branch | default('master') }}
src={{ lookup('first_found', site_locations }}
with_items: apache_sites

I had a similar need for tc Server (tomcat). This is what I did:
I put the site-specific configuration in a separate tasks file (configure-sites.yml):
- template:
src: "{{ item }}"
dest: /foo/{{ apache_site.name }}-{{ apache_site.branch | default('master') }}
with_first_found:
- "{{ apache_site.name }}-{{ apache_site.branch | default('master') }}"
- "{{ apache_site.name }}.j2"
- apache_site.j2
From a separate tasks file I included that tasks file, passing it each site:
- include: configure-sites.yml
with_items: "{{ apache_sites }}"
loop_control:
loop_var: apache_site
This makes use of loop_control which requires Ansible 2.1+: http://docs.ansible.com/ansible/playbooks_loops.html#loop-control
In case it helps, you can see exactly what I did here:
https://github.com/bmaupin/ansible-role-tcserver/blob/master/tasks/main.yml
https://github.com/bmaupin/ansible-role-tcserver/blob/master/tasks/configure-instances.yml

Related

Ansible: How to replace value of keys in a file?

I Have a file /tmp/srv_info.txt as below:
INVENTORY_HOSTNAME: hostname
BIOS_VERSION: biosversion
TOTAL_MEMORY: memory
VDA_SIZE: vdasize
VDB_SIZE: vdbsize
XVDA_SIZE: xvdasize
XVDC_SIZE: xvdcsize
I have to fill it with correct values per host, I already set the variables for each value:
- name: Save results
set_fact:
HN: "{{ ansible_hostname | default('NONE', true) }}"
BV: "{{ ansible_bios_version | default('NONE', true) }}"
MEM: "{{ ansible_memtotal_mb | default('NONE', true) }} MB"
XVDA: "{{ ansible_devices.xvda.size.split(',') | first | default('NONE', true) }}"
XVDC: "{{ ansible_devices.xvdc.size.split(',') | first | default('NONE', true) }}"
VDA: "{{ ansible_devices.vda.size | default('NONE', true) }}"
VDB: "{{ ansible_devices.vdb.size | default('NONE', true) }}"
How can I change /tmp/srv_info.txt with appropriate values, for example:
HOSTNAME: host_one.lab.infra
BIOS_VERSION: 4.13
TOTAL_MEMORY: 3900 MB
VDA_SIZE: 25 GB
VDB_SIZE: 10 GB
XVDA_SIZE: NONE
XVDC_SIZE: NONE
I Can use module replace, or lineinfile for each value, but then need to call 7 times the module.
I would like to loop the variable list, find the corresponding script, and replace the value in the file.
But could not achieve yet.
Can you help?
If you know exactly how the file should look like on the target machine, use a template. That's what they are for.
Template:
HOSTNAME: {{HN}}
BIOS_VERSION: {{BV}}
TOTAL_MEMORY: {{MEM}}
VDA_SIZE: {{VDA}}
VDB_SIZE: {{VDB}}
XVDA_SIZE: {{XVDA}}
XVDC_SIZE: {{XVDB}}
Playbook:
- template:
src: src.j2
dest: /tmp/srv_info.txt
[owner, group, mode...]
For example, use copy
- copy:
content: |
HOSTNAME: {{ HN }}
BIOS_VERSION: {{ BV }}
TOTAL_MEMORY: {{ MEM }}
VDA_SIZE: {{ VDA }}
VDB_SIZE: {{ VDB }}
XVDA_SIZE: {{ XVDA }}
XVDC_SIZE: {{ XVDC }}
dest: /tmp/srv_info.txt
The next option is to put the content into a file and use template.
If you want to change only some of the values read the hashes from the file into a dictionary and use this dictionary to set the defaults. For example
- include_vars:
file: /tmp/srv_info.txt
name: srv_info
- set_fact:
HN: "{{ ansible_hostname | default(srv_info.INVENTORY_HOSTNAME) }}"
BV: "{{ ansible_bios_version | default(srv_info.BIOS_VERSION) }}"
MEM: "{{ ansible_memtotal_mb | default(srv_info.TOTAL_MEMORY) }} MB"
XVDA: "{{ ansible_devices.xvda.size.split(',') | first | default(srv_info.XVDA_SIZE) }}"
XVDC: "{{ ansible_devices.xvdc.size.split(',') | first | default(srv_info.XVDC_SIZE) }}"
VDA: "{{ ansible_devices.vda.size | default(srv_info.VDA_SIZE) }}"
VDB: "{{ ansible_devices.vdb.size | default(srv_info.VDB_SIZE) }}"

Conditional to add_host module

I have an inventory that i need to add a conditional to.
My code:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
group: target_hosts
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
But I want something like:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
{% if item.0.value.OS_Choice[:3] == 'win' %}
group:
- target_hosts
- win
{% else %}
group:
- target_hosts
- linux
{% endif %}
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
With this configuration, Ansible errors:
The offending line appears to be:
{% if item.0.value.OS_Choice[:3] == 'win' %}
^ here
Any ideas on how I can implement this conditional?
You're mixing up Jinja2 with YAML. Here you go:
- name: Create memory inventory when win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] == 'win'
vars:
group:
- target_hosts
- win
- name: Create memory inventory when not win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] != 'win'
vars:
group:
- target_hosts
- linux
However, Ansible gather facts about the OS already. Perhaps you want to use those, instead of configuring something like this yourself.
Building up on Kevin's answer (and fixing some wrongly placed parameters)
You should definitely do this differently, like creating dynamic groups based on detected OS in facts. See:
the group_by module
the ansible_distribution* facts that you can explore as an example with
ansible localhost -m setup -a filter="ansible_distribution*"
Meanwhile, with your current logic, you can still do this in a single task:
- name: Create memory inventory
vars:
additional_group: >-
{{ (item.0.value.OS_Choice[:3] == 'win') | ternary('win', 'linux') }}
add_host:
name: "{{ item.0.key }}"
groups:
- target_hosts
- "{{ additional_group }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"

asible variable constructed of vars as a volume

Hello im working with ansible and in my docker container task im trying to set my volumes as a var.
Here is my volumes var
volumes_map: |
- "{{ airflow_script_location }}:{{ airflow_script_location_in_container }}:ro"
- "{{ airflow_data_input_location }}:{{ airflow_data_input_location_in_container }}:rw"
- "{{ airflow_data_output_success_location }}:{{ airflow_data_output_success_location_in_container }}:rw"
- "{{ airflow_data_output_failed_location }}:{{ airflow_data_output_failed_location_in_container }}:rw"
- "{{ airflow_root_location }}/requirements.txt:/requirements.txt"
- "{{ airflow_services_location }}/:{{ airflow_services_location_in_container }}:rw"
- "{{ airflow_root_location }}/airflow.cfg:{{ airflow_root_in_container }}/airflow.cfg"
- "{{ airflow_logs_location }}:{{ airflow_logs_location_in_container }}"
- "{{ airflow_conf_location}}:{{ airflow_conf_location_in_container }}"
and this is how im setting it in my volumes but this isnt working
volumes: "{{ volumes_map }}"
When i set the volumes directly without using the volumes_map it all works.
Try to Use template module
The "volumes_map" type is List,you can try
- debug: msg="{% for item in volumes_map %}{{item}}{% endfor %}"
Use Map it may help
- name: test
shell: echo "{{item}}"
with_items:
- "{{ airflow_script_location }}:{{ airflow_script_location_in_container }}:ro"
- "{{ airflow_root_location }}/airflow.cfg:{{ airflow_root_in_container }}/airflow.cfg"
- "{{ airflow_conf_location}}:{{ airflow_conf_location_in_container }}"
register: volumes_map
- debug: var=volumes_map.results|map(attribute='stdout')|list

Can we have 2 with_items in ansible in a single task

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.

Ansible: Save registered variables to file

How could I save a registered variables to a file using Ansible?
Goal:
I would like to gather detailed information about all PCI buses and devices in the system and save the result somewhere (Ex. using lspci. Ideally, I should have results of command in my local machine for further analysis).
Save results also somewhere with the given criterion.
My playbook looks like this:
tasks:
- name: lspci Debian
command: /usr/bin/lspci
when: ansible_os_family == "Debian"
register: lspcideb
- name: lspci RedHat
command: /usr/sbin/lspci
when: ansible_os_family == "RedHat"
register: lspciredhat
- name: copy content
local_action: copy content="{{ item }}" dest="/path/to/destination/file-{{ item }}-{{ ansible_date_time.date }}-{{ ansible_hostname }}.log"
with_items:
- lspcideb
- aptlist
- lspciredhat
But saves only item_name
Good Q&A with saving 1 variable there - Ansible - Save registered variable to file.
- local_action: copy content={{ foo_result }} dest=/path/to/destination/file
My question:
How can I save multiple variables and transfer stdout to my local machine?
- name: copy content
local_action: copy content="{{ vars[item] }}" dest="/path/to/destination/file-{{ item }}-{{ ansible_date_time.date }}-{{ ansible_hostname }}.log"
with_items:
- lspcideb
- aptlist
- lspciredhat
Explanation:
You must embed variable names in Jinja2 expressions to refer to their values, otherwise you are passing strings. So:
with_items:
- "{{ lspcideb }}"
- "{{ aptlist }}"
- "{{ lspciredhat }}"
It's a universal rule in Ansible. For the same reason you used {{ item }} not item, and {{ foo_result }} not foo_result.
But you use {{ item }} also for the file name and this will likely cause a mess.
So you can refer to the variable value with: {{ vars[item] }}.
Another method would be to define a dictionary:
- name: copy content
local_action: copy content="{{ item.value }}" dest="/path/to/destination/file-{{ item.variable }}-{{ ansible_date_time.date }}-{{ ansible_hostname }}.log"
with_items:
- variable: lspcideb
value: "{{ lspcideb }}"
- variable: aptlist
value: "{{ aptlist }}"
- variable: lspciredhat
value: "{{ lspciredhat }}"

Resources