From 514df137d011047319d38c7e3ecbd5990dc81db1 Mon Sep 17 00:00:00 2001 From: Teodoro Cook Date: Mon, 20 Jan 2025 19:01:50 -0600 Subject: [PATCH] Add composite inventory plugin (#14) --- .github/workflows/test.yml | 1 + .pylintrc | 6 +- galaxy.yml | 2 +- molecule/composite/inventory.yml | 7 + molecule/composite/molecule.yml | 25 ++ molecule/composite/primary/group_vars/all.yml | 2 + .../primary/group_vars/first_level/vars.yml | 2 + .../composite/primary/group_vars/level_a.yml | 2 + .../composite/primary/group_vars/level_b.yml | 2 + .../primary/group_vars/second_level/vars.yml | 3 + .../primary/group_vars/third_level.yml | 3 + .../primary/host_vars/pri_second_level_a.yml | 2 + .../primary/host_vars/pri_third_level_a.yml | 2 + molecule/composite/primary/hosts.yml | 51 ++++ molecule/composite/primary/zzconstructed.yml | 6 + molecule/composite/secondary.yml | 39 +++ .../composite/secondary/group_vars/all.yml | 2 + .../secondary/group_vars/first_level/vars.yml | 3 + .../group_vars/second_level/vars.yml | 2 + .../secondary/group_vars/third_level.yml | 3 + molecule/composite/secondary/hosts.yml | 46 ++++ molecule/composite/verify.yml | 226 ++++++++++++++++++ plugins/inventory/composite.py | 152 ++++++++++++ 23 files changed, 585 insertions(+), 4 deletions(-) create mode 100644 molecule/composite/inventory.yml create mode 100644 molecule/composite/molecule.yml create mode 100644 molecule/composite/primary/group_vars/all.yml create mode 100644 molecule/composite/primary/group_vars/first_level/vars.yml create mode 100644 molecule/composite/primary/group_vars/level_a.yml create mode 100644 molecule/composite/primary/group_vars/level_b.yml create mode 100644 molecule/composite/primary/group_vars/second_level/vars.yml create mode 100644 molecule/composite/primary/group_vars/third_level.yml create mode 100644 molecule/composite/primary/host_vars/pri_second_level_a.yml create mode 100644 molecule/composite/primary/host_vars/pri_third_level_a.yml create mode 100644 molecule/composite/primary/hosts.yml create mode 100644 molecule/composite/primary/zzconstructed.yml create mode 100644 molecule/composite/secondary.yml create mode 100644 molecule/composite/secondary/group_vars/all.yml create mode 100644 molecule/composite/secondary/group_vars/first_level/vars.yml create mode 100644 molecule/composite/secondary/group_vars/second_level/vars.yml create mode 100644 molecule/composite/secondary/group_vars/third_level.yml create mode 100644 molecule/composite/secondary/hosts.yml create mode 100644 molecule/composite/verify.yml create mode 100644 plugins/inventory/composite.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index acafa78..ddb14aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: matrix: scenario: - filter + - composite steps: - name: Check out the codebase uses: actions/checkout@v4 diff --git a/.pylintrc b/.pylintrc index e760806..4acda35 100644 --- a/.pylintrc +++ b/.pylintrc @@ -294,10 +294,10 @@ max-attributes=7 max-bool-expr=5 # Maximum number of branch for function / method body. -max-branches=16 +max-branches=30 # Maximum number of locals for function / method body. -max-locals=15 +max-locals=30 # Maximum number of parents for a class (see R0901). max-parents=7 @@ -309,7 +309,7 @@ max-public-methods=20 max-returns=6 # Maximum number of statements in function / method body. -max-statements=50 +max-statements=100 # Minimum number of public methods for a class (see R0903). min-public-methods=1 diff --git a/galaxy.yml b/galaxy.yml index d72906c..3e2ede5 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: nephelaiio name: plugins -version: 0.0.11 +version: 0.1.0 readme: README.md authors: - Ted Cook diff --git a/molecule/composite/inventory.yml b/molecule/composite/inventory.yml new file mode 100644 index 0000000..309c9a2 --- /dev/null +++ b/molecule/composite/inventory.yml @@ -0,0 +1,7 @@ +--- +plugin: nephelaiio.plugins.composite +inventories: + - file: primary + prefix: primary + - file: secondary + prefix: secondary diff --git a/molecule/composite/molecule.yml b/molecule/composite/molecule.yml new file mode 100644 index 0000000..79cc65c --- /dev/null +++ b/molecule/composite/molecule.yml @@ -0,0 +1,25 @@ +--- +dependency: + name: galaxy + options: + role-file: requirements.yml + requirements-file: requirements.yml +driver: + name: default +platforms: + - name: localhost +provisioner: + name: ansible + config_options: + inventory: + enable_plugins: nephelaiio.plugins.composite, ansible.builtin.yaml, ansible.builtin.auto, ansible.builtin.constructed + unparsed_is_failed: true + defaults: + callbacks_enabled: ansible.posix.profile_tasks + inventory: + links: + hosts: inventory.yml + playbooks: + verify: ./verify.yml +verifier: + name: ansible diff --git a/molecule/composite/primary/group_vars/all.yml b/molecule/composite/primary/group_vars/all.yml new file mode 100644 index 0000000..304cc58 --- /dev/null +++ b/molecule/composite/primary/group_vars/all.yml @@ -0,0 +1,2 @@ +--- +inventory_name: "primary" diff --git a/molecule/composite/primary/group_vars/first_level/vars.yml b/molecule/composite/primary/group_vars/first_level/vars.yml new file mode 100644 index 0000000..a0c0538 --- /dev/null +++ b/molecule/composite/primary/group_vars/first_level/vars.yml @@ -0,0 +1,2 @@ +--- +group_name: "first_level" diff --git a/molecule/composite/primary/group_vars/level_a.yml b/molecule/composite/primary/group_vars/level_a.yml new file mode 100644 index 0000000..c833645 --- /dev/null +++ b/molecule/composite/primary/group_vars/level_a.yml @@ -0,0 +1,2 @@ +--- +construct_id: a diff --git a/molecule/composite/primary/group_vars/level_b.yml b/molecule/composite/primary/group_vars/level_b.yml new file mode 100644 index 0000000..e626e2e --- /dev/null +++ b/molecule/composite/primary/group_vars/level_b.yml @@ -0,0 +1,2 @@ +--- +construct_id: b diff --git a/molecule/composite/primary/group_vars/second_level/vars.yml b/molecule/composite/primary/group_vars/second_level/vars.yml new file mode 100644 index 0000000..0d5aba8 --- /dev/null +++ b/molecule/composite/primary/group_vars/second_level/vars.yml @@ -0,0 +1,3 @@ +--- +group_name: second_level +private_id: primary_second_level diff --git a/molecule/composite/primary/group_vars/third_level.yml b/molecule/composite/primary/group_vars/third_level.yml new file mode 100644 index 0000000..0241851 --- /dev/null +++ b/molecule/composite/primary/group_vars/third_level.yml @@ -0,0 +1,3 @@ +--- +group_name: third_level +private_id: primary_third_level diff --git a/molecule/composite/primary/host_vars/pri_second_level_a.yml b/molecule/composite/primary/host_vars/pri_second_level_a.yml new file mode 100644 index 0000000..ff6cf07 --- /dev/null +++ b/molecule/composite/primary/host_vars/pri_second_level_a.yml @@ -0,0 +1,2 @@ +--- +host_id: pri_second_level_a diff --git a/molecule/composite/primary/host_vars/pri_third_level_a.yml b/molecule/composite/primary/host_vars/pri_third_level_a.yml new file mode 100644 index 0000000..774dbd5 --- /dev/null +++ b/molecule/composite/primary/host_vars/pri_third_level_a.yml @@ -0,0 +1,2 @@ +--- +host_id: overridden diff --git a/molecule/composite/primary/hosts.yml b/molecule/composite/primary/hosts.yml new file mode 100644 index 0000000..3ee5d4c --- /dev/null +++ b/molecule/composite/primary/hosts.yml @@ -0,0 +1,51 @@ +--- +all: + vars: + inventory_global_id: molecule + inventory_id: primary + hosts: + pri_first_level_a: + ansible_host: 10.0.0.1 + alias: first_a + pri_first_level_b: + ansible_host: 10.0.0.2 + alias: first_b + pri_second_level_a: + ansible_host: 10.0.1.1 + alias: second_a + pri_second_level_b: + ansible_host: 10.0.1.2 + alias: second_b + pri_third_level_a: + ansible_host: 10.0.2.1 + host_id: pri_third_level_a + pri_third_level_b: + ansible_host: 10.0.2.2 + host_id: pri_third_level_b + +select_levels: + vars: + group_id: primary_select_levels + children: + first_level: + second_level: + +first_level: + hosts: + pri_first_level_a: + pri_first_level_b: + +second_level: + hosts: + pri_second_level_a: + pri_second_level_b: + +third_level: + hosts: + pri_third_level_a: + pri_third_level_b: + +private_level: + children: + first_level: + third_level: diff --git a/molecule/composite/primary/zzconstructed.yml b/molecule/composite/primary/zzconstructed.yml new file mode 100644 index 0000000..f92f3e9 --- /dev/null +++ b/molecule/composite/primary/zzconstructed.yml @@ -0,0 +1,6 @@ +--- +plugin: constructed +strict: true +groups: + level_a: inventory_hostname is regex('.*level_a$') + level_b: inventory_hostname is regex('.*level_b$') diff --git a/molecule/composite/secondary.yml b/molecule/composite/secondary.yml new file mode 100644 index 0000000..6191478 --- /dev/null +++ b/molecule/composite/secondary.yml @@ -0,0 +1,39 @@ +--- +all: + hosts: + snd_first_level_a: + ansible_host: 10.0.0.1 + alias: first_a + snd_first_level_b: + ansible_host: 10.0.0.2 + alias: first_b + snd_second_level_a: + ansible_host: 10.0.1.1 + alias: second_a + snd_second_level_b: + ansible_host: 10.0.1.2 + alias: second_b + snd_third_level_a: + ansible_host: 10.0.2.1 + snd_third_level_b: + ansible_host: 10.0.2.2 + +select_levels: + children: + first_level: + second_level: + +first_level: + hosts: + snd_first_level_a: + snd_first_level_b: + +second_level: + hosts: + snd_second_level_a: + snd_second_level_b: + +third_level: + hosts: + snd_third_level_a: + snd_third_level_b: diff --git a/molecule/composite/secondary/group_vars/all.yml b/molecule/composite/secondary/group_vars/all.yml new file mode 100644 index 0000000..20244eb --- /dev/null +++ b/molecule/composite/secondary/group_vars/all.yml @@ -0,0 +1,2 @@ +--- +inventory_name: secondary diff --git a/molecule/composite/secondary/group_vars/first_level/vars.yml b/molecule/composite/secondary/group_vars/first_level/vars.yml new file mode 100644 index 0000000..cbf5e6c --- /dev/null +++ b/molecule/composite/secondary/group_vars/first_level/vars.yml @@ -0,0 +1,3 @@ +--- +group_name: first_level +host_id: default diff --git a/molecule/composite/secondary/group_vars/second_level/vars.yml b/molecule/composite/secondary/group_vars/second_level/vars.yml new file mode 100644 index 0000000..12b03f3 --- /dev/null +++ b/molecule/composite/secondary/group_vars/second_level/vars.yml @@ -0,0 +1,2 @@ +--- +group_name: second_level diff --git a/molecule/composite/secondary/group_vars/third_level.yml b/molecule/composite/secondary/group_vars/third_level.yml new file mode 100644 index 0000000..452e345 --- /dev/null +++ b/molecule/composite/secondary/group_vars/third_level.yml @@ -0,0 +1,3 @@ +--- +group_name: third_level +private_id: secondary_third_level diff --git a/molecule/composite/secondary/hosts.yml b/molecule/composite/secondary/hosts.yml new file mode 100644 index 0000000..4fbda8c --- /dev/null +++ b/molecule/composite/secondary/hosts.yml @@ -0,0 +1,46 @@ +--- +all: + vars: + inventory_global_id: molecule + inventory_id: secondary + hosts: + snd_first_level_a: + ansible_host: 10.0.0.1 + alias: first_a + host_id: snd_first_level_a + snd_first_level_b: + ansible_host: 10.0.0.2 + alias: first_b + host_id: snd_first_level_b + snd_second_level_a: + ansible_host: 10.0.1.1 + alias: second_a + snd_second_level_b: + ansible_host: 10.0.1.2 + alias: second_b + snd_third_level_a: + ansible_host: 10.0.2.1 + snd_third_level_b: + ansible_host: 10.0.2.2 + +select_levels: + vars: + group_id: secondary_select_levels + children: + first_level: + second_level: + +first_level: + hosts: + snd_first_level_a: + snd_first_level_b: + +second_level: + hosts: + snd_second_level_a: + snd_second_level_b: + +third_level: + hosts: + snd_third_level_a: + snd_third_level_b: diff --git a/molecule/composite/verify.yml b/molecule/composite/verify.yml new file mode 100644 index 0000000..37d3777 --- /dev/null +++ b/molecule/composite/verify.yml @@ -0,0 +1,226 @@ +--- +- name: Verify inventory hosts + hosts: localhost + connection: local + gather_facts: false + vars: + _primary_first_level: ["pri_first_level_a", "pri_first_level_b"] + _primary_second_level: ["pri_second_level_a", "pri_second_level_b"] + _primary_third_level: ["pri_third_level_a", "pri_third_level_b"] + _secondary_first_level: ["snd_first_level_a", "snd_first_level_b"] + _secondary_second_level: ["snd_second_level_a", "snd_second_level_b"] + _secondary_third_level: ["snd_third_level_a", "snd_third_level_b"] + _primary_level_a: + ["pri_first_level_a", "pri_second_level_a", "pri_third_level_a"] + _primary_level_b: + ["pri_first_level_b", "pri_second_level_b", "pri_third_level_b"] + _localhost: ["localhost"] + _all: "{{ _primary + _secondary + _localhost }}" + _first_level: "{{ _primary_first_level + _secondary_first_level }}" + _second_level: "{{ _primary_second_level + _secondary_second_level }}" + _third_level: "{{ _primary_third_level + _secondary_third_level }}" + _primary: "{{ _primary_first_level + _primary_second_level + _primary_third_level }}" + _secondary: "{{ _secondary_first_level + _secondary_second_level + _secondary_third_level }}" + _private_level: "{{ _primary_first_level + _primary_third_level }}" + tasks: + - name: Test all hostgroup + ansible.builtin.assert: + that: + - _hosts | symmetric_difference(_all) | length == 0 + fail_msg: | + Difference: {{ _hosts | ansible.builtin.symmetric_difference(_all) | join(', ') }} + vars: + _hosts: "{{ groups['all'] }}" + + - name: Test primary hostgroups + ansible.builtin.assert: + that: + - groups['primary'] | symmetric_difference(_primary) | length == 0 + - groups['primary_select_levels'] | symmetric_difference(_primary_first_level + _primary_second_level) | length == 0 + - groups['primary_first_level'] | symmetric_difference(_primary_first_level) | length == 0 + - groups['primary_second_level'] | difference(_primary_second_level) | length == 0 + - groups['primary_third_level'] | difference(_primary_third_level) | length == 0 + - groups['primary_private_level'] | difference(_private_level) | length == 0 + + - name: Test secondary hostgroups + ansible.builtin.assert: + that: + - groups['secondary'] | difference(_secondary) | length == 0 + - groups['secondary_select_levels'] | difference(_secondary_first_level + _secondary_second_level) | length == 0 + - groups['secondary_first_level'] | difference(_secondary_first_level) | length == 0 + - groups['secondary_second_level'] | difference(_secondary_second_level) | length == 0 + - groups['secondary_third_level'] | difference(_secondary_third_level) | length == 0 + + - name: Test global hostgroups + ansible.builtin.assert: + that: + - groups['all'] | difference(_all) | length == 0 + - groups['first_level'] | difference(_first_level) | length == 0 + - groups['second_level'] | difference(_second_level) | length == 0 + - groups['third_level'] | difference(_third_level) | length == 0 + - groups['private_level'] | difference(_private_level) | length == 0 + + - name: Test constructed hostgroups + ansible.builtin.assert: + that: + - groups['primary_level_a'] | difference(_primary_level_a) | length == 0 + - groups['primary_level_b'] | difference(_primary_level_b) | length == 0 + +- name: Verify global group vars + hosts: all:!localhost + gather_facts: false + tasks: + - name: Test global group var from inventory file + ansible.builtin.assert: + that: inventory_global_id == "molecule" + +- name: Verify prefix group vars + hosts: primary + gather_facts: false + tasks: + - name: Test group var from inventory file + ansible.builtin.assert: + that: + - inventory_name is defined + - inventory_name == "primary" + fail_msg: "Inventory name is not defined or does not match expected value, got '{{ inventory_name }}'" + + - name: Test group var from group_vars + ansible.builtin.assert: + that: + - inventory_id is defined + - inventory_id == "primary" + fail_msg: "inventory_id is not defined or does not match expected value, got '{{ inventory_id }}'" + +- name: Verify prefix group vars + hosts: secondary + gather_facts: false + tasks: + - name: Test group var from inventory file + ansible.builtin.assert: + that: + - inventory_name is defined + - inventory_name == "secondary" + fail_msg: "Inventory name is not defined or does not match expected value, got '{{ inventory_name }}'" + + - name: Test group var from group_vars + ansible.builtin.assert: + that: + - inventory_id is defined + - inventory_id == "secondary" + fail_msg: "inventory_id is not defined or does not match expected value, got '{{ inventory_id }}'" + +- name: Verify group vars + hosts: first_level + gather_facts: false + tasks: + - name: Test first_level group vars + ansible.builtin.assert: + that: group_name == "first_level" + +- name: Verify group vars + hosts: second_level + gather_facts: false + tasks: + - name: Test second_level group vars + ansible.builtin.assert: + that: group_name == "second_level" + +- name: Verify group vars + hosts: third_level + gather_facts: false + tasks: + - name: Test third_level group vars + ansible.builtin.assert: + that: group_name == "third_level" + +- name: Verify primary_select_levels group vars + hosts: primary_select_levels + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: group_id == "primary_select_levels" + +- name: Verify secondary_select_levels group vars + hosts: secondary_select_levels + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: group_id == "secondary_select_levels" + +- name: Verify primary_second_level group vars + hosts: primary_second_level + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: private_id == "primary_second_level" + +- name: Verify secondary_second_level group vars + hosts: secodary_second_level + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: private_id is undefined + +- name: Verify primary_third_level group vars + hosts: primary_third_level + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: private_id == "primary_third_level" + +- name: Verify secondary_third_level group vars + hosts: secondary_third_level + gather_facts: false + tasks: + - name: Test select_levels group vars + ansible.builtin.assert: + that: private_id == "secondary_third_level" + +- name: Verify primary_second_level_a host vars + hosts: primary_second_level_a + gather_facts: false + tasks: + - name: Test host_vars + ansible.builtin.assert: + that: host_id == inventory_hostname + fail_msg: "hostid = {{ host_id }}, expected {{ inventory_hostname }}" + +- name: Verify primary_third_level host vars + hosts: primary_third_level + gather_facts: false + tasks: + - name: Test host_vars + ansible.builtin.assert: + that: host_id == inventory_hostname + fail_msg: "hostid = {{ host_id }}, expected {{ inventory_hostname }}" + +- name: Verify secondary_first_level host vars + hosts: secondary_first_level + gather_facts: false + tasks: + - name: Test host_vars + ansible.builtin.assert: + that: host_id == inventory_hostname + fail_msg: "hostid = {{ host_id }}, expected {{ inventory_hostname }}" + +- name: Verify constructed group vars + hosts: primary_level_a + gather_facts: false + tasks: + - name: Test constructed group vars + ansible.builtin.assert: + that: construct_id == "a" + +- name: Verify constructed group vars + hosts: primary_level_b + gather_facts: false + tasks: + - name: Test constructed group vars + ansible.builtin.assert: + that: construct_id == "b" diff --git a/plugins/inventory/composite.py b/plugins/inventory/composite.py new file mode 100644 index 0000000..f9cc7cc --- /dev/null +++ b/plugins/inventory/composite.py @@ -0,0 +1,152 @@ +""" +Custom composite inventory plugin +""" + +import os +from collections.abc import MutableMapping + +from ansible.errors import AnsibleParserError +from ansible.inventory.manager import InventoryManager +from ansible.module_utils.common.text.converters import to_text +from ansible.plugins.inventory import BaseFileInventoryPlugin +from ansible.utils.path import basedir +from ansible.vars.plugins import get_vars_from_path + +DOCUMENTATION = """ + name: composite_inventory + plugin_type: inventory + short_description: A composition plugin for multiple yaml invetories + description: Build a composite inventory from the union of multiple yaml inventories + options: + plugin: + description: Name of the plugin + required: true + choices: ['nephelaiio.plugins.composite'] +""" + +NoneType = type(None) + +GROUP_VARS = "group_vars" +HOST_VARS = "host_vars" +DICT_TYPES = (MutableMapping, NoneType) +CANONICAL_PATHS: dict[str, str] = {} +FOUND: dict[str, list[str]] = {} +NAK: set[str] = set() +PATH_CACHE: dict[tuple[str, str], str] = {} + + +class InventoryModule(BaseFileInventoryPlugin): + """Build a composite inventory from the union of multiple yaml inventories""" + + NAME = "composite" + + def _prefixed_group_name(self, group_name, prefix): + if not prefix: + return group_name + return f"{prefix}_{group_name}" + + def verify_file(self, path): + valid = False + if super().verify_file(path): + _, file_ext = os.path.splitext(path) + if file_ext in ["", ".yml", ".yaml"]: + valid = True + return valid + + def parse(self, inventory, loader, path, cache=True): + super().parse(inventory, loader, path, cache) + self._read_config_data(path) + + try: + data = self.loader.load_from_file(path) + except Exception as e: + raise AnsibleParserError(e) from e + + path_dir = basedir(path) + group_vars_dir = os.path.join(path_dir, GROUP_VARS) + host_vars_dir = os.path.join(path_dir, HOST_VARS) + + if not data: + msg = f'Parsed empty YAML file "{to_text(path)}"' + raise AnsibleParserError(msg) + if not data.get("inventories"): + msg = f'Parsed file "{to_text(path)}" does not contain "inventories" key' + raise AnsibleParserError(msg) + if os.path.exists(group_vars_dir): + msg = f"Directory {group_vars_dir} exists, group_vars should be defined in child inventories" + raise AnsibleParserError(msg) + if os.path.exists(host_vars_dir): + msg = f"Directory {host_vars_dir} exists, host_vars should be defined in child inventories" + raise AnsibleParserError(msg) + + inventories = data.get("inventories") + if not inventories or len(inventories) == 0: + msg = f'Parsed file "{to_text(path)}" does not contain any inventories' + raise AnsibleParserError(msg) + for subinventory in inventories: + _subinventory = to_text(subinventory) + if not isinstance(data, MutableMapping): + msg = f"YAML inventory has invalid structure, it should be a dictionary, got: {type(subinventory)}" + raise AnsibleParserError(msg) + if not subinventory.get("file"): + msg = f'Inventory "{_subinventory}" does not contain "file" key' + raise AnsibleParserError(msg) + if not subinventory.get("prefix"): + msg = f'Inventory "{_subinventory}" does not contain "prefix" key' + raise AnsibleParserError(msg) + source = os.path.realpath(subinventory.get("file")) + if not os.path.exists(source): + msg = f'File "{source}" does not exist' + raise AnsibleParserError(msg) + prefix = subinventory.get("prefix") + manager = InventoryManager(loader=loader, sources=[source]) + manager.parse_sources() + groups = manager.get_groups_dict() + for group_name in groups: + if group_name == "ungrouped": + continue + if group_name == prefix: + msg = f"Group name {group_name} conflicts with prefix {prefix}" + raise AnsibleParserError(msg) + if group_name == "all": + prefix_group = prefix + else: + prefix_group = self._prefixed_group_name(group_name, prefix) + self.inventory.add_group(group_name) + self.inventory.add_group(prefix_group) + self.inventory.add_child(group_name, prefix_group) + group = manager.groups[group_name] + # load group_vars from group_vars directory + group_vars = get_vars_from_path(loader, source, group, "task") + for var_name in group_vars: + var_value = group_vars[var_name] + msg = f"Registered var {var_name} for group {prefix_group}" + self.display.warning(msg) + self.inventory.set_variable(prefix_group, var_name, var_value) + # load group_vars from inventory sources + group_vars = group.vars + for var_name in group_vars: + var_value = group_vars[var_name] + msg = f"Registered var {var_name} for group {prefix_group}" + self.display.warning(msg) + self.inventory.set_variable(prefix_group, var_name, var_value) + # register host + for host_name in groups[group_name]: + host = manager.get_host(host_name) + self.inventory.add_host(host_name, prefix_group) + msg = f"Registered host {host_name} for group {group_name}" + self.display.warning(msg) + # load host_vars from host_vars directory + host_vars = get_vars_from_path(loader, source, host, "task") + for var_name in host_vars: + msg = f"Registered var {var_name} for host {host_name}" + self.display.warning(msg) + var_value = host_vars[var_name] + self.inventory.set_variable(host_name, var_name, var_value) + # load host_vars from inventory sources + host_vars = host.vars + for var_name in host_vars: + msg = f"Registered var {var_name} for host {host_name}" + self.display.warning(msg) + var_value = host.vars[var_name] + self.inventory.set_variable(host_name, var_name, var_value)