From 99496593a9dcfe83019549274ff74aac283723c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sun, 7 Aug 2022 22:38:48 +0200 Subject: [PATCH] Inventory plugin for empty group creation --- ansible.cfg | 2 +- inventory_plugins/group_creator.py | 107 +++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 inventory_plugins/group_creator.py diff --git a/ansible.cfg b/ansible.cfg index 566294d..6db5979 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -12,4 +12,4 @@ use_persistent_connections = true usetty = false [inventory] -enable_plugins = constructed, community.general.proxmox, yaml +enable_plugins = constructed, community.general.proxmox, group_creator, yaml diff --git a/inventory_plugins/group_creator.py b/inventory_plugins/group_creator.py new file mode 100644 index 0000000..8aacfa1 --- /dev/null +++ b/inventory_plugins/group_creator.py @@ -0,0 +1,107 @@ +import os + +from ansible import constants as C +from ansible.errors import AnsibleParserError +from ansible.module_utils._text import to_native +from ansible.plugins.inventory import BaseInventoryPlugin + + +DOCUMENTATION = """ + name: group_creator + short_description: Uses Jinja2 templates to construct empty groups. + description: + - Uses a YAML configuration file with a valid YAML extension to define + groups. The groups' names as well as the list of parent groups for a + given group may use Jinja2 templates based on the host variables. + options: + plugin: + description: + - Token that ensures this is a source file for the C(group_creator) + plugin. + required: True + choices: ['group_creator'] + groups: + description: + - The list of groups to create. + type: list + elements: dict + required: True + suboptions: + name: + description: + - The template to generate a group name from. If an empty + string is returned, the group will not be created. + type: str + required: true + parents: + description: + - A list of templates to generate the names of the group's + parents from. Empty strings will be ignored. + type: list + elements: str + default: [] + strict: + description: + - If C(yes) make invalid templates a fatal error, otherwise skip and + continue. + required: False + type: bool + default: no +""" + + +class InventoryModule(BaseInventoryPlugin): + """Constructs empty groups based on arbitrary variables.""" + + NAME = "group_creator" + + def __init__(self): + super(InventoryModule, self).__init__() + + def verify_file(self, path): + valid = False + if super(InventoryModule, self).verify_file(path): + file_name, ext = os.path.splitext(path) + if not ext or ext in ['.config'] + C.YAML_FILENAME_EXTENSIONS: + valid = True + return valid + + def _evaluate(self, template, variables, auto_template): + t = self.templar + if auto_template: + template = "%s%s%s" % ( + t.environment.variable_start_string, + template, + t.environment.variable_end_string, + ) + t.available_variables = variables + return t.template(template, disable_lookups=True) + + def _get_group_name(self, host, name, host_vars, strict): + try: + name = self._evaluate(name, host_vars, False) + return self._sanitize_group_name(name) + except Exception as e: + if strict: + raise AnsibleParserError( + "Could not generate group name for host %s from '%s': (%s) %s" + % (host, name, to_native(repr(type(e))), to_native(e)) + ) + return "" + + def parse(self, inventory, loader, path, cache=False): + super(InventoryModule, self).parse(inventory, loader, path, cache=cache) + self._read_config_data(path) + strict = self.get_option("strict") + for host in inventory.hosts: + host_vars = self.inventory.get_host(host).get_vars() + for group in self.get_option("groups"): + name = self._get_group_name(host, group['name'], host_vars, strict) + if not name: + continue + self.inventory.add_group(name) + for ptmpl in group.get("parents"): + parent = self._get_group_name(host, ptmpl, host_vars, strict) + if parent: + self.inventory.add_group(parent) + self.inventory.add_child(parent, name)