Moar constructive (#28254)
* made composite vars and groups generic now you can do both in every plugin that chooses to suport it renamed constructed_groups as it now also constructs vars ... to constructed moved most of constructed_groups logic into base class to easily share * documented inventory_hostname * typo fix
This commit is contained in:
parent
ec11cd2696
commit
a897193bce
5 changed files with 60 additions and 26 deletions
|
@ -134,7 +134,7 @@ Ansible Changes By Release
|
||||||
|
|
||||||
#### New Inventory Plugins:
|
#### New Inventory Plugins:
|
||||||
- advanced_host_list
|
- advanced_host_list
|
||||||
- constructed_groups
|
- constructed
|
||||||
- host_list
|
- host_list
|
||||||
- ini
|
- ini
|
||||||
- script
|
- script
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
#callback_whitelist = timer, mail
|
#callback_whitelist = timer, mail
|
||||||
|
|
||||||
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
|
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
|
||||||
#inventory_enabled = host_list, aws, openstack, docker
|
#inventory_enabled = host_list, virtualbox, yaml, constructed
|
||||||
|
|
||||||
# Determine whether includes in tasks and handlers are "static" by
|
# Determine whether includes in tasks and handlers are "static" by
|
||||||
# default. As of 2.0, includes are dynamic by default. Setting these
|
# default. As of 2.0, includes are dynamic by default. Setting these
|
||||||
|
|
|
@ -50,6 +50,7 @@ class BaseInventoryPlugin(object):
|
||||||
|
|
||||||
self.loader = loader
|
self.loader = loader
|
||||||
self.inventory = inventory
|
self.inventory = inventory
|
||||||
|
self.templar = Templar(loader=loader)
|
||||||
|
|
||||||
def verify_file(self, path):
|
def verify_file(self, path):
|
||||||
''' Verify if file is usable by this plugin, base does minimal accessability check '''
|
''' Verify if file is usable by this plugin, base does minimal accessability check '''
|
||||||
|
@ -81,9 +82,31 @@ class BaseInventoryPlugin(object):
|
||||||
|
|
||||||
def _compose(self, template, variables):
|
def _compose(self, template, variables):
|
||||||
''' helper method for pluigns to compose variables for Ansible based on jinja2 expression and inventory vars'''
|
''' helper method for pluigns to compose variables for Ansible based on jinja2 expression and inventory vars'''
|
||||||
t = Templar(loader=self.loader, variables=variables)
|
t = self.templar
|
||||||
|
t.set_available_variables(variables)
|
||||||
return t.do_template('%s%s%s' % (t.environment.variable_start_string, template, t.environment.variable_end_string), disable_lookups=True)
|
return t.do_template('%s%s%s' % (t.environment.variable_start_string, template, t.environment.variable_end_string), disable_lookups=True)
|
||||||
|
|
||||||
|
def _set_composite_vars(self, compose, variables, host):
|
||||||
|
''' loops over compose entries to create vars for hosts '''
|
||||||
|
if compose and isinstance(compose, dict):
|
||||||
|
for varname in compose:
|
||||||
|
composite = self._compose(compose[varname], variables)
|
||||||
|
self.inventory.set_variable(host, varname, composite)
|
||||||
|
|
||||||
|
def _add_host_to_composed_groups(self, groups, variables, host):
|
||||||
|
''' helper to create complex groups for plugins based on jinaj2 conditionals, hosts that meet the conditional are added to group'''
|
||||||
|
# process each 'group entry'
|
||||||
|
if groups and isinstance(groups, dict):
|
||||||
|
self.templar.set_available_variables(variables)
|
||||||
|
for group_name in groups:
|
||||||
|
conditional = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % groups[group_name]
|
||||||
|
result = self.templar.template(conditional)
|
||||||
|
if result and bool(result):
|
||||||
|
# ensure group exists
|
||||||
|
self.inventory.add_group(group_name)
|
||||||
|
# add host to group
|
||||||
|
self.inventory.add_child(group_name, host)
|
||||||
|
|
||||||
|
|
||||||
class BaseFileInventoryPlugin(BaseInventoryPlugin):
|
class BaseFileInventoryPlugin(BaseInventoryPlugin):
|
||||||
""" Parses a File based Inventory Source"""
|
""" Parses a File based Inventory Source"""
|
||||||
|
|
|
@ -17,16 +17,28 @@
|
||||||
#############################################
|
#############################################
|
||||||
'''
|
'''
|
||||||
DOCUMENTATION:
|
DOCUMENTATION:
|
||||||
name: constructed_groups
|
name: constructed
|
||||||
plugin_type: inventory
|
plugin_type: inventory
|
||||||
version_added: "2.4"
|
version_added: "2.4"
|
||||||
short_description: Uses Jinja2 expressions to construct groups.
|
short_description: Uses Jinja2 to construct vars and groups based on existing inventory.
|
||||||
description:
|
description:
|
||||||
- Uses a YAML configuration file to identify group and the Jinja2 expressions that qualify a host for membership.
|
- Uses a YAML configuration file to define var expresisions and group conditionals
|
||||||
- Only variables already in inventory are available for expressions (no facts).
|
- The Jinja2 conditionals that qualify a host for membership.
|
||||||
|
- The JInja2 exprpessions are calculated and assigned to the variables
|
||||||
|
- Only variables already available from previous inventories can be used for templating.
|
||||||
- Failed expressions will be ignored (assumes vars were missing).
|
- Failed expressions will be ignored (assumes vars were missing).
|
||||||
|
compose:
|
||||||
|
description: create vars from jinja2 expressions
|
||||||
|
type: dictionary
|
||||||
|
default: {}
|
||||||
|
groups:
|
||||||
|
description: add hosts to group based on Jinja2 conditionals
|
||||||
|
type: dictionary
|
||||||
|
default: {}
|
||||||
EXAMPLES: | # inventory.config file in YAML format
|
EXAMPLES: | # inventory.config file in YAML format
|
||||||
plugin: constructed_groups
|
plugin: comstructed
|
||||||
|
compose:
|
||||||
|
var_sum: var1 + var2
|
||||||
groups:
|
groups:
|
||||||
# simple name matching
|
# simple name matching
|
||||||
webservers: inventory_hostname.startswith('web')
|
webservers: inventory_hostname.startswith('web')
|
||||||
|
@ -48,15 +60,14 @@ import os
|
||||||
|
|
||||||
from ansible.errors import AnsibleParserError
|
from ansible.errors import AnsibleParserError
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin
|
from ansible.plugins.inventory import BaseInventoryPlugin
|
||||||
from ansible.template import Templar
|
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native
|
||||||
from ansible.utils.vars import combine_vars
|
from ansible.utils.vars import combine_vars
|
||||||
|
|
||||||
|
|
||||||
class InventoryModule(BaseInventoryPlugin):
|
class InventoryModule(BaseInventoryPlugin):
|
||||||
""" constructs groups using Jinaj2 template expressions """
|
""" constructs groups and vars using Jinaj2 template expressions """
|
||||||
|
|
||||||
NAME = 'constructed_groups'
|
NAME = 'constructed'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
@ -87,8 +98,6 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
raise AnsibleParserError("%s is empty or not a constructed groups config file" % (to_native(path)))
|
raise AnsibleParserError("%s is empty or not a constructed groups config file" % (to_native(path)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
templar = Templar(loader=loader)
|
|
||||||
|
|
||||||
# Go over hosts (less var copies)
|
# Go over hosts (less var copies)
|
||||||
for host in inventory.hosts:
|
for host in inventory.hosts:
|
||||||
|
|
||||||
|
@ -96,16 +105,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
hostvars = inventory.hosts[host].get_vars()
|
hostvars = inventory.hosts[host].get_vars()
|
||||||
if host in inventory.cache: # adds facts if cache is active
|
if host in inventory.cache: # adds facts if cache is active
|
||||||
hostvars = combine_vars(hostvars, inventory.cache[host])
|
hostvars = combine_vars(hostvars, inventory.cache[host])
|
||||||
templar.set_available_variables(hostvars)
|
|
||||||
|
|
||||||
# process each 'group entry'
|
# create composite vars
|
||||||
for group_name in data.get('groups', {}):
|
self._set_composite_vars(data.get('compose'), hostvars, host)
|
||||||
conditional = u"{%% if %s %%} True {%% else %%} False {%% endif %%}" % data['groups'][group_name]
|
|
||||||
result = templar.template(conditional)
|
# constructed groups based on conditionals
|
||||||
if result and bool(result):
|
self._add_host_to_composed_groups(data.get('groups'), hostvars, host)
|
||||||
# ensure group exists
|
|
||||||
inventory.add_group(group_name)
|
|
||||||
# add host to group
|
|
||||||
inventory.add_child(group_name, host)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
|
raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
|
12
lib/ansible/plugins/inventory/virtualbox.py
Executable file → Normal file
12
lib/ansible/plugins/inventory/virtualbox.py
Executable file → Normal file
|
@ -24,6 +24,7 @@ DOCUMENTATION:
|
||||||
description:
|
description:
|
||||||
- Get inventory hosts from the local virtualbox installation.
|
- Get inventory hosts from the local virtualbox installation.
|
||||||
- Uses a <name>.vbox.yaml (or .vbox.yml) YAML configuration file.
|
- Uses a <name>.vbox.yaml (or .vbox.yml) YAML configuration file.
|
||||||
|
- The inventory_hostname is always the 'Name' of the virtualbox instance.
|
||||||
options:
|
options:
|
||||||
running_only:
|
running_only:
|
||||||
description: toggles showing all vms vs only those currently running
|
description: toggles showing all vms vs only those currently running
|
||||||
|
@ -42,6 +43,10 @@ DOCUMENTATION:
|
||||||
description: create vars from jinja2 expressions, these are created AFTER the query block
|
description: create vars from jinja2 expressions, these are created AFTER the query block
|
||||||
type: dictionary
|
type: dictionary
|
||||||
default: {}
|
default: {}
|
||||||
|
groups:
|
||||||
|
description: add hosts to group based on Jinja2 conditionals, these also run after query block
|
||||||
|
type: dictionary
|
||||||
|
default: {}
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
# file must be named vbox.yaml or vbox.yml
|
# file must be named vbox.yaml or vbox.yml
|
||||||
simple_config_file:
|
simple_config_file:
|
||||||
|
@ -94,14 +99,15 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
hostvars[host][varname] = self._query_vbox_data(host, data['query'][varname])
|
hostvars[host][varname] = self._query_vbox_data(host, data['query'][varname])
|
||||||
|
|
||||||
# create composite vars
|
# create composite vars
|
||||||
if data.get('compose') and isinstance(data['compose'], dict):
|
self._set_composite_vars(data.get('compose'), hostvars, host)
|
||||||
for varname in data['compose']:
|
|
||||||
hostvars[host][varname] = self._compose(data['compose'][varname], hostvars[host])
|
|
||||||
|
|
||||||
# actually update inventory
|
# actually update inventory
|
||||||
for key in hostvars[host]:
|
for key in hostvars[host]:
|
||||||
self.inventory.set_variable(host, key, hostvars[host][key])
|
self.inventory.set_variable(host, key, hostvars[host][key])
|
||||||
|
|
||||||
|
# constructed groups based on conditionals
|
||||||
|
self._add_host_to_composed_groups(data.get('groups'), hostvars, host)
|
||||||
|
|
||||||
def _populate_from_source(self, source_data, config_data):
|
def _populate_from_source(self, source_data, config_data):
|
||||||
hostvars = {}
|
hostvars = {}
|
||||||
prevkey = pref_k = ''
|
prevkey = pref_k = ''
|
||||||
|
|
Loading…
Reference in a new issue