diff options
Diffstat (limited to 'lib/ansible/plugins/lookup/ini.py')
-rw-r--r-- | lib/ansible/plugins/lookup/ini.py | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/lib/ansible/plugins/lookup/ini.py b/lib/ansible/plugins/lookup/ini.py new file mode 100644 index 0000000..eea8634 --- /dev/null +++ b/lib/ansible/plugins/lookup/ini.py @@ -0,0 +1,204 @@ +# (c) 2015, Yannig Perre <yannig.perre(at)gmail.com> +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = """ + name: ini + author: Yannig Perre (!UNKNOWN) <yannig.perre(at)gmail.com> + version_added: "2.0" + short_description: read data from an ini file + description: + - "The ini lookup reads the contents of a file in INI format C(key1=value1). + This plugin retrieves the value on the right side after the equal sign C('=') of a given section C([section])." + - "You can also read a property file which - in this case - does not contain section." + options: + _terms: + description: The key(s) to look up. + required: True + type: + description: Type of the file. 'properties' refers to the Java properties files. + default: 'ini' + choices: ['ini', 'properties'] + file: + description: Name of the file to load. + default: 'ansible.ini' + section: + default: global + description: Section where to lookup the key. + re: + default: False + type: boolean + description: Flag to indicate if the key supplied is a regexp. + encoding: + default: utf-8 + description: Text encoding to use. + default: + description: Return value if the key is not in the ini file. + default: '' + case_sensitive: + description: + Whether key names read from C(file) should be case sensitive. This prevents + duplicate key errors if keys only differ in case. + default: False + version_added: '2.12' + allow_no_value: + description: + - Read an ini file which contains key without value and without '=' symbol. + type: bool + default: False + aliases: ['allow_none'] + version_added: '2.12' +""" + +EXAMPLES = """ +- ansible.builtin.debug: msg="User in integration is {{ lookup('ansible.builtin.ini', 'user', section='integration', file='users.ini') }}" + +- ansible.builtin.debug: msg="User in production is {{ lookup('ansible.builtin.ini', 'user', section='production', file='users.ini') }}" + +- ansible.builtin.debug: msg="user.name is {{ lookup('ansible.builtin.ini', 'user.name', type='properties', file='user.properties') }}" + +- ansible.builtin.debug: + msg: "{{ item }}" + loop: "{{ q('ansible.builtin.ini', '.*', section='section1', file='test.ini', re=True) }}" + +- name: Read an ini file with allow_no_value + ansible.builtin.debug: + msg: "{{ lookup('ansible.builtin.ini', 'user', file='mysql.ini', section='mysqld', allow_no_value=True) }}" +""" + +RETURN = """ +_raw: + description: + - value(s) of the key(s) in the ini file + type: list + elements: str +""" + +import configparser +import os +import re + +from io import StringIO +from collections import defaultdict +from collections.abc import MutableSequence + +from ansible.errors import AnsibleLookupError, AnsibleOptionsError +from ansible.module_utils._text import to_text, to_native +from ansible.plugins.lookup import LookupBase + + +def _parse_params(term, paramvals): + '''Safely split parameter term to preserve spaces''' + + # TODO: deprecate this method + valid_keys = paramvals.keys() + params = defaultdict(lambda: '') + + # TODO: check kv_parser to see if it can handle spaces this same way + keys = [] + thiskey = 'key' # initialize for 'lookup item' + for idp, phrase in enumerate(term.split()): + + # update current key if used + if '=' in phrase: + for k in valid_keys: + if ('%s=' % k) in phrase: + thiskey = k + + # if first term or key does not exist + if idp == 0 or not params[thiskey]: + params[thiskey] = phrase + keys.append(thiskey) + else: + # append to existing key + params[thiskey] += ' ' + phrase + + # return list of values + return [params[x] for x in keys] + + +class LookupModule(LookupBase): + + def get_value(self, key, section, dflt, is_regexp): + # Retrieve all values from a section using a regexp + if is_regexp: + return [v for k, v in self.cp.items(section) if re.match(key, k)] + value = None + # Retrieve a single value + try: + value = self.cp.get(section, key) + except configparser.NoOptionError: + return dflt + return value + + def run(self, terms, variables=None, **kwargs): + + self.set_options(var_options=variables, direct=kwargs) + paramvals = self.get_options() + + self.cp = configparser.ConfigParser(allow_no_value=paramvals.get('allow_no_value', paramvals.get('allow_none'))) + if paramvals['case_sensitive']: + self.cp.optionxform = to_native + + ret = [] + for term in terms: + + key = term + # parameters specified? + if '=' in term or ' ' in term.strip(): + self._deprecate_inline_kv() + params = _parse_params(term, paramvals) + try: + updated_key = False + for param in params: + if '=' in param: + name, value = param.split('=') + if name not in paramvals: + raise AnsibleLookupError('%s is not a valid option.' % name) + paramvals[name] = value + elif key == term: + # only take first, this format never supported multiple keys inline + key = param + updated_key = True + except ValueError as e: + # bad params passed + raise AnsibleLookupError("Could not use '%s' from '%s': %s" % (param, params, to_native(e)), orig_exc=e) + if not updated_key: + raise AnsibleOptionsError("No key to lookup was provided as first term with in string inline options: %s" % term) + # only passed options in inline string + + # TODO: look to use cache to avoid redoing this for every term if they use same file + # Retrieve file path + path = self.find_file_in_search_path(variables, 'files', paramvals['file']) + + # Create StringIO later used to parse ini + config = StringIO() + # Special case for java properties + if paramvals['type'] == "properties": + config.write(u'[java_properties]\n') + paramvals['section'] = 'java_properties' + + # Open file using encoding + contents, show_data = self._loader._get_file_contents(path) + contents = to_text(contents, errors='surrogate_or_strict', encoding=paramvals['encoding']) + config.write(contents) + config.seek(0, os.SEEK_SET) + + try: + self.cp.readfp(config) + except configparser.DuplicateOptionError as doe: + raise AnsibleLookupError("Duplicate option in '{file}': {error}".format(file=paramvals['file'], error=to_native(doe))) + + try: + var = self.get_value(key, paramvals['section'], paramvals['default'], paramvals['re']) + except configparser.NoSectionError: + raise AnsibleLookupError("No section '{section}' in {file}".format(section=paramvals['section'], file=paramvals['file'])) + if var is not None: + if isinstance(var, MutableSequence): + for v in var: + ret.append(v) + else: + ret.append(var) + return ret |