Not sure what you mean. Can you provide an example?
vars file:
LAX:
name: lax
vcnetuse: lax1051
datacenter: LAX
server: vclax1.example.com
cluster: vclax1-linux-cluster1
ds0: vclax1-linux-cluster1-datastore1
ds1: Unity2LAX_Datastore13
ds2: Unity2LAX_Datastore06
ds3: Unity2LAX_Datastore02
isofull: null
CLT:
name: clt
vcnetuse: clt1051
datacenter: VCCLT1
server: vcclt1.example.com
cluster: vcclt1-linux-cluster1
ds1: vblk4_vmfs3
ds2: null
ds3: null
isofull: null
Say all my included tasks are using server/datacenter/cluster var names.
Say i want to use the vars from CLT ā¦ if i want to sent the VM to CLTā¦
how can i control which group of vars is used
(i already ended up using lookup plugin) but i feel like theres something obvious im missing
Sounds like it should be handled in inventory and not in a vars file. Control the values by changing the group membership of the VM and then re-run the playbook. Not sure though without broader context.
---
# inventory.yml
all:
children:
lax:
children:
vclax1_linux_cluster1:
hosts:
vclax1.example.com:
clt:
children:
vcclt1_linux_cluster1:
hosts:
vcclt1.example.com:
---
# group_vars/lax.yml
datacenter:
vcnetuse: lax1051
name: lax
---
# group_vars/vclax1_linux_cluster1.yml
cluster:
ds0: vclax1-linux-cluster1-datastore1
ds1: Unity2LAX_Datastore13
ds2: Unity2LAX_Datastore06
ds3: Unity2LAX_Datastore02
isofull: null
etc.
Or something along those lines.
Bonus, if you need to take a variable from a datacenter other than the one the host is currently in:
# host in the clt group
- debug:
msg: "{{ hostvars[ groups['lax'][0] ]['datacenter']['name'] }}
Or you can set all of the datacenters and clusters in a dictionary in the same vars file that you already have (with the same inventory file from above):
---
# vars/main.yml
datacenters:
LAX:
name: lax
vcnetuse: lax1051
datacenter: LAX
server: vclax1.example.com
cluster: vclax1-linux-cluster1
ds0: vclax1-linux-cluster1-datastore1
ds1: Unity2LAX_Datastore13
ds2: Unity2LAX_Datastore06
ds3: Unity2LAX_Datastore02
isofull: null
CLT:
name: clt
vcnetuse: clt1051
datacenter: VCCLT1
server: vcclt1.example.com
cluster: vcclt1-linux-cluster1
ds1: vblk4_vmfs3
ds2: null
ds3: null
isofull: null
---
# group_vars/lax.yml
dc: LAX
---
# play.yml
...
- debug:
var: datacenters[dc]
Thank you for the suggestion(s)ā¦
i rewrote some stuff
and your post helped me a ton
thank you
One other note on this that I didnāt realize for a long time. Ansible will populate the hostvars
dictionary for all hosts in the inventory even if they are not in the current play.
@nx2l have you written any plugins for Ansible? I want to extend the first_found
lookup. I have written a role that does what I want, but I know it should really be a plugin instead.
I am familiar with Python but havenāt written much. Looking at the developer docs, I donāt understand what some of the boilerplate is doing and Iām not confident with error handlingā¦
https://docs.ansible.com/ansible/latest/dev_guide/developing_plugins.html#lookup-plugins
sadly no, i find it hard finding info on the built in pluginsā¦
fyi
the lookup plugin for fileā¦ you can use {{ var }} in the file nameā¦ couldnt find any examples show vars, but some trail and errorā¦ and it works.
I have a python book that i havent finished reading and I bet if i did, i could write a plugin if needed
fyi this one
No Starch Press is a good publisher. I havenāt read that one, but Iāve used other books from them before and theyāre often pretty good ones.
I also have it.
Yeah so I use a formulation of that where it looks for a tasks, vars or template file based on certain attributes of the host. For instance, on a Rocky 8.4 host, it will look for prefix_rocky_8.4.yml
, then prefix_rocky_8.yml
, then prefix_rocky.yml
, then prefix_redhat.yml
, then prefix_linux.yml
, then prefix_ssh.yml
, etc. And itās actually more complicated than that because it tracks selinux vs app armor, NetworkManager vs networkd.
Hereās the whole thing if youāre curious. I compressed it into one giant task because I use it so liberally it makes the Ansible output extremely noisy otherwise.
---
# vim: ts=2:sw=2:sts=2:et:ft=yaml.ansible
#
# Get the first found name of a tasks, variables or template file
#
########################################################################
# Condense into one task to be as quiet as possible
- name: >-
Look up first found {{ first_found['type'] }} in
{{ ff_candidates_var['paths'][0] }}
ansible.builtin.set_fact:
ff: "{{ lookup('first_found', ff_candidates_var) }}"
# Fail if the first_found variable isn't formatted properly.
failed_when: >-
first_found | type_debug != 'dict'
or first_found.keys()
| difference( [ 'type', 'prefix', 'suffix', 'default',
'allow_none' ] )
!= []
or first_found['type'] not in ['vars', 'tasks', 'templates']
or first_found['prefix'] is defined
and not first_found['prefix'] | string
or first_found['suffix'] is defined
and not first_found['suffix'] | string
or first_found['default'] is defined
and first_found['default'] | string
or first_found['allow_none'] | type_debug != 'bool'
vars:
# Parameters may be passed in using first_found, first_found_item
# or first_found_var, or the default (defined in defaults/main.yml)
# will be used if none of those exist. If more than one is present,
# precedence follows the order listed.
first_found: >-
{{ first_found_default
| combine( first_found_var
| default(first_found_item)
| default({}) ) }}
# Extensions are defined in default/main.yml
ext_var: "{{ exts[ first_found['type'] ] }}"
# Precedence follows most specific to least specific. Since
# ansible_connection is used, this role may be useful even before
# facts are gathered (if running against hosts with a variety of
# connection types).
ff_candidates_var:
files:
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_version | default(omit) | lower }}_\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8.5_nmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_version | default(omit) | lower }}_\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8.5_selinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_version | default(omit) | lower }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8.5SUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_major_version
| default(omit)
| lower }}_\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8_nmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_major_version
| default(omit)
| lower }}_\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8_selinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}-\
{{ ansible_distribution_major_version
| default(omit)
| lower }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky-8SUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}_\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky_nmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}_\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrocky_selinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_distribution | default(omit) | lower }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrockySUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_os_family | default(omit) | lower }}_\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXredhat_nmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_os_family | default(omit) | lower }}_\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXredhat_selinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_os_family | default(omit) | lower }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXredhatSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_system | default(omit) | lower }}_\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXlinux_nmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_system | default(omit) | lower }}_\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXlinux_selinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_system | default(omit) | lower }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXlinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ net_mgr | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXnmSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ mac | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXselinuxSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_network_os | default(omit) | split('.') | last }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXrouterosSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ ansible_connection | default(omit) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXsshSUFFIX.EXT
- "{{ first_found['prefix'] | default('') }}\
{{ first_found['default'] | default(omit, true) }}\
{{ first_found['suffix'] | default('') }}.\
{{ ext_var }}"
# Ex: PREFIXdefaultSUFFIX.EXT
- "{{ first_found['allow_none']
| ternary('/dev/null', omit) }}"
paths:
- "{{ ansible_parent_role_paths
| difference( [role_path] ) | first }}/\
{{ first_found['type'] }}/"
Usage looks like this:
- name: Use the first found role to get platform-specific file name
ansible.builtin.include_role:
name: o0_o.first_found
vars:
first_found_var:
type: tasks
prefix: cfg_iface_vlan_
allow_none: true
- name: Configure VLAN child interfaces
ansible.builtin.include_tasks: "{{ ff }}"
loop: "{{ iface['vlans'] | default([]) }}"
loop_control:
loop_var: vlan_subnet_item
I had an issue with a dict variable set in group_vars
. The dict needs to be updated by hosts during the play. The issue is keeping it in sync across all hosts in the group.
- set_fact:
group_foo: "{{ group_foo | combine( host_bar ) }}"
Assuming host_bar
is different for each host, this will result in a group_foo
that is no longer the same for the whole group.
I have solved this by running my tasks file in pseudo-serial with this loop/conditional:
when: inventory_hostname == host_item
loop: "{{ ansible_play_hosts }}"
loop_control:
loop_var: host_item
And then running this at the end of that tasks file:
- name: Propagate group_foo
set_fact:
group_foo: "{{ group_foo }}"
delegate_to: "{{ foo_host_item }}"
delegate_facts: true
loop: "{{ groups['foo']] }}"
loop_control:
loop_var: foo_host_item
Note that {{ group_foo }}
is taken from the current host, despite delegate_facts
. The delegation does affect the assignment though, so itās taking the current value of group_foo
and applying it to all other hosts in the group.
A quirk I discovered while working on this is that when looping a single task, hostvars
is not updated until the entire loop has executed. So if youāre drilling into the hostvars
dictionary in a set_fact
loop with delegation, hostvars
will not reflect changes made during the loop. Again, this only applies to looping on a single task. To work around it, put your task in a file and loop include_tasks
instead.
What is the difference between the iterable
and sequence
jinja2 tests? When would you use on over the other?
anyone know if its possible to import a playbook based on a group name?
something like this
- name: test
import_playbook: {{ item }}.yaml
when: {{ item }}.yaml exists
with_items: "{{ group_names }}"
Yeah it should be.
Use a lookup for this part.
do i need quotes?
trying this next.
waitā¦ wouldnt that load the contents of the file ā¦ not just check if it exists?
Yeah, file
gives the contents, but could ignore errors and set the condition to it not being an empty string.
when: "{{ lookup('file', item + '.yaml' , errors='ignore') != '' }}"
Might be easier to use pipe
though.
when: "{{ lookup('pipe', '-e ' + item + '.yaml') }}"
And yeah, your formatting isnāt right with the variable. No need to nest {{
}}
.
I didnāt test either of these but should give you the idea.
Now that I think about it, Iām not sure how pipe
handles exit codes, so maybe stick with file
.
I found fileglobā¦ looking for examples on it right now.
good news i figured out how to make the listā¦
the bad news it it doesnt work
ERROR! ālist_of_exist_group_playbksā is undefined
or
ERROR! ādelegate_factsā is not a valid attribute for a PlaybookInclude
or
ERROR! āloopā is not a valid attribute for a PlaybookInclude
guessing these arent any help
Template Designer Documentation ā Jinja Documentation (3.1.x)
Template Designer Documentation ā Jinja Documentation (3.1.x)
- set_fact:
list_of_exist_group_playbks: "{{ list_of_exist_group_playbks | default([]) + [ item ] }}"
You donāt need run_once
. Try wrapping wrapping the import in a tasks file and looping on the include_tasks
.
Use with_items: "{{ list_of_exist_group_playbks | intersect( lookup('fileglob', '*.yaml') ) }}"
Does fileglob
give you full or relative paths? Is it a list?
wellā¦ if im running big group of systems that have multiple groupsā¦ i dont want a playbook meant for a whole group to kick off 20 times b/c 20 systems in the same group ran before the import, right
yes
and yes
will try the task calling the import laterā¦
if task calling import doenst workā¦ iāll see about convert the playbook to a task that can be includedā¦