From 911938cc36fcbbc9f163e99e4898f7f5e6f1e03e Mon Sep 17 00:00:00 2001 From: Robin Pierre Cordier Date: Fri, 11 Feb 2022 16:38:42 -0500 Subject: [PATCH] Cleanup: Code and refactoring --- plugins/README.md | 31 ++++++++++ plugins/inventory/kheops.py | 42 ++++++++------ plugins/lookup/kheops.py | 40 +++++-------- plugins/plugin_utils/common.py | 100 ++++++++++++++++++++++----------- 4 files changed, 139 insertions(+), 74 deletions(-) create mode 100644 plugins/README.md diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000..67044d2 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,31 @@ +# Collections Plugins Directory + +This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that +is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that +would contain module utils and modules respectively. + +Here is an example directory of the majority of plugins currently supported by Ansible: + +``` +└── plugins + ├── action + ├── become + ├── cache + ├── callback + ├── cliconf + ├── connection + ├── filter + ├── httpapi + ├── inventory + ├── lookup + ├── module_utils + ├── modules + ├── netconf + ├── shell + ├── strategy + ├── terminal + ├── test + └── vars +``` + +A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.11/plugins/plugins.html). diff --git a/plugins/inventory/kheops.py b/plugins/inventory/kheops.py index 31fdba4..12d5928 100644 --- a/plugins/inventory/kheops.py +++ b/plugins/inventory/kheops.py @@ -85,48 +85,58 @@ class InventoryModule(BaseInventoryPlugin, Cacheable, Constructable): config_data = self._read_config_data(path) #self._consume_options(config_data) + # Get options from inventory self.strict = self.get_option('strict') - self.compose = self.get_option('compose') self.groups = self.get_option('groups') self.keyed_groups = self.get_option('keyed_groups') + # self.process_scope = self.get_option('process_scope') + # self.process_results = self.get_option('process_results') + # Prepare Kheops instance self.config_file = self.get_option('config') - configs = [ self.config_file, path, ] kheops = AnsibleKheops(configs=configs, display=self.display) - # Loop over each keys + # Loop over each hosts for host_name in inventory.hosts: host = self.inventory.get_host(host_name) - scope = kheops.get_scope_from_host_inventory(host.get_vars(), scope=None) - - # Fetch the results - ret = kheops.lookup( + ret = kheops.super_lookup( keys=None, - scope=scope, + scope=None, + _templar=self.templar, + _variables=host.get_vars(), #trace=True, #explain=True, ) + # # Create the scope + # if self.process_scope == 'vars': + # scope = kheops.get_scope_from_host_inventory(host.get_vars(), scope=None) + # elif self.process_scope == 'jinja': + # scope = kheops.get_scope_from_jinja(host.get_vars(), self.templar, scope=None) + + # # Fetch the results + # ret = kheops.lookup( + # keys=None, + # scope=scope, + # #trace=True, + # #explain=True, + # ) + # Inject variables into host for key, value in ret.items(): - self.display.vv (f"Set {host_name} var: {key}={value}") - host.set_variable(key, value) + + self.display.vv (f"Define variable for {host_name}: {key}={value}") + host.set_variable(key, value) # Call constructed inventory plugin methods - #hostvars = self.inventory.get_host(host_name).get_vars() hostvars = host.get_vars() - - #tutu = hostvars.get('tiger_profiles', "MISSSSINGGGG") - #print ("YOOOOO", tutu, self.keyed_groups, host_name, self.strict) - - self._set_composite_vars(self.compose, hostvars, host_name, self.strict) self._add_host_to_composed_groups(self.groups, hostvars, host_name, self.strict) self._add_host_to_keyed_groups(self.keyed_groups, hostvars, host_name, self.strict) diff --git a/plugins/lookup/kheops.py b/plugins/lookup/kheops.py index 42e7829..415084b 100644 --- a/plugins/lookup/kheops.py +++ b/plugins/lookup/kheops.py @@ -109,30 +109,29 @@ from pprint import pprint class LookupModule(LookupBase): def run(self, terms, variables=None, scope=None, **kwargs): - self.set_options(direct=kwargs) - #self.config_file = self.get_option('config') + self.process_scope = self.get_option('process_scope') + self.process_results = self.get_option('process_results') + + # Prepare Kheops instance self.config_file = self.get_option('config') - configs = [ self.config_file, -# { -# "instance_log_level": 'DEBUG', -# } + kwargs, + #{ + # "instance_log_level": 'DEBUG', + # } ] kheops = AnsibleKheops(configs=configs, display=self._display) - - scope = kheops.get_scope_from_host_inventory(variables, scope=None) - scope = kheops.get_scope_from_jinja(variables, self._templar, scope=None) - - #assert False , f"OUTOUT: {scope2}" - - - #for key, value in self.get_option('scope').items(): - # scope[key] = variables[value] + # Create scope + if self.process_scope == 'vars': + scope = kheops.get_scope_from_host_inventory(variables, scope=None) + elif self.process_scope == 'jinja': + scope = kheops.get_scope_from_jinja(variables, self._templar, scope=None) + # Transform dict to list for lookup/queries ret = [] for term in terms: result = kheops.lookup( @@ -145,17 +144,6 @@ class LookupModule(LookupBase): - - - # if isinstance(terms, str): - # terms = terms.split(',') - - # import ansible_collections.barbu_it.ansible_kheops.plugins.plugin_utils.common as kheops_utils - # AnsibleKheops = kheops_utils.AnsibleKheops - # kheops = AnsibleKheops() - - # return None - # assert isinstance(terms, list), f"Expected a list, got: {terms}" # # Parse arguments diff --git a/plugins/plugin_utils/common.py b/plugins/plugin_utils/common.py index 39d44fe..6f1995d 100644 --- a/plugins/plugin_utils/common.py +++ b/plugins/plugin_utils/common.py @@ -123,6 +123,26 @@ DOCUMENTATION_OPTION_FRAGMENT = ''' - A list of keys to lookup default: Null + + # Behavior configuration + # ========================== + process_scope: + description: + - This setting defines how is parsed the `scope` configuration + - Set `vars` to enable simple variable interpolation + - Set `jinja` to enable jinja string interpolation + default: 'jinja' + choices: ['vars', 'jinja'] + + process_results: + description: + - This setting defines how is parsed the returned results. + - Set `none` to disable jinja interpolation from result. + - Set `jinja` to enable jinja result interpolation. + - Using jinja may pose some security issues, as you need to be sure that your source of data is properly secured. + default: 'none' + choices: ['none', 'jinja'] + # Uneeded # Misc # Uneeded version: @@ -163,6 +183,7 @@ from kheops.app import Kheops from ansible.errors import AnsibleError from ansible.module_utils.common.text.converters import to_native from ansible.template import generate_ansible_template_vars, AnsibleEnvironment, USE_JINJA2_NATIVE +from ansible.utils.display import Display from pprint import pprint @@ -190,14 +211,13 @@ class AnsibleKheops(): def __init__(self, configs=None, display=None): self.configs = configs or [] - self.display = display or print + self.display = display or Display() config = self.get_config() # print ("CURRENT CONFIG vv") # pprint (config) # print ("CURRENT CONFIG ^^") - self.init_templar() # Instanciate Kheops if config["mode"] == 'instance': @@ -224,7 +244,7 @@ class AnsibleKheops(): raise AnsibleError("Kheops client mode is not implemented") self.config = config - print ("Kheops instance is OK") + self.display.v(f"Kheops instance has been created") def get_config(self): @@ -278,7 +298,7 @@ class AnsibleKheops(): combined_config.update(env_config) combined_config.update(merged_configs) - + # Debug report # out = { # "0_default": default_config, # "1_env": env_config, @@ -290,7 +310,6 @@ class AnsibleKheops(): # pprint (combined_config) # print ('=' * 20) - return combined_config @@ -359,12 +378,9 @@ class AnsibleKheops(): return ret - def init_templar(self, enable_jinja=True, jinja2_native=False): - pass def get_scope_from_jinja(self, host_vars, templar, scope=None, jinja2_native=False): scope = scope or self.config['scope'] - ret = {} if USE_JINJA2_NATIVE and not jinja2_native: _templar = templar.copy_with_new_env(environment_class=AnsibleEnvironment) @@ -372,15 +388,25 @@ class AnsibleKheops(): _templar = templar _vars = deepcopy(host_vars) + ret = {} with _templar.set_temporary_context(available_variables=_vars): - res = _templar.template(scope, preserve_trailing_newlines=True, - convert_data=False, escape_backslashes=False) - if USE_JINJA2_NATIVE and not jinja2_native: - # jinja2_native is true globally but off for the lookup, we need this text - # not to be processed by literal_eval anywhere in Ansible - res = NativeJinjaText(res) - return res + for key, value in scope.items(): + res = value + try: + res = _templar.template(value, preserve_trailing_newlines=True, + convert_data=True, escape_backslashes=False) + if USE_JINJA2_NATIVE and not jinja2_native: + # jinja2_native is true globally but off for the lookup, we need this text + # not to be processed by literal_eval anywhere in Ansible + res = NativeJinjaText(res) + self.display.vvv(f"Transformed: {value} =====> {res}") + except Exception as err: + self.display.v(f"Got templating error for value: {value} => {err}") + + ret[key] = res + + return ret @@ -392,33 +418,43 @@ class AnsibleKheops(): keys_config = self.parse_keys(keys, namespace) keys = [ i.show() for i in keys_config ] - # self.base.display.v(f"Kheops keys: {keys}") - # self.base.display.vv(f"Kheops scope: {scope}") + self.display.v(f"Kheops keys: {keys}") + self.display.vv(f"Kheops scope: {scope}") - - print ("LOOKUP QUERY: ", keys, scope) - #try: ret = self.kheops.lookup( keys=keys, scope=scope, #trace=True, #explain=True, ) - #except Exception as err: - # assert False, f"{err.__traceback__}" - # assert False, f"{dir(err)}" - # raise AnsibleError('Something happened, this was original exception: %s' % to_native(err)) - # assert False, f"MY ERRRROR {err}" - # self.display.v(err) - - print ("RESULT: ", ret) # Remap output - #for key in keys_config: - # if key.remap != key.key: - # ret[key.remap] = ret[key.key] - # del ret[key.key] + for key in keys_config: + if key.remap != key.key: + ret[key.remap] = ret[key.key] + del ret[key.key] return ret or {} + def super_lookup(self, keys, namespace=None, scope=None, kwargs=None, + _templar=None, + _variables=None, + _process_scope=None, + _process_results=None, + ): + + _process_scope = _process_scope or self.config['process_scope'] + _process_results = _process_results or self.config['process_results'] + + if _process_scope == 'vars': + scope = self.get_scope_from_host_inventory(_variables, scope=scope) + elif _process_scope == 'jinja': + assert _templar, f"BUG: We expected a templar object here, got: {_templar}" + scope = self.get_scope_from_jinja(_variables, _templar, scope=scope) + + ret = self.lookup(keys, namespace=namespace, scope=scope) + + return ret + +