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:
Brian Coca 2017-08-21 16:06:15 -04:00 committed by GitHub
parent ec11cd2696
commit a897193bce
5 changed files with 60 additions and 26 deletions

View file

@ -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

View file

@ -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

View file

@ -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"""

View file

@ -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
View 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 = ''