1# (c) 2015, Yannig Perre <yannig.perre(at)gmail.com> 2# (c) 2017 Ansible Project 3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 4from __future__ import (absolute_import, division, print_function) 5__metaclass__ = type 6 7DOCUMENTATION = """ 8 lookup: ini 9 author: Yannig Perre (!UNKNOWN) <yannig.perre(at)gmail.com> 10 version_added: "2.0" 11 short_description: read data from a ini file 12 description: 13 - "The ini lookup reads the contents of a file in INI format C(key1=value1). 14 This plugin retrieves the value on the right side after the equal sign C('=') of a given section C([section])." 15 - "You can also read a property file which - in this case - does not contain section." 16 options: 17 _terms: 18 description: The key(s) to look up 19 required: True 20 type: 21 description: Type of the file. 'properties' refers to the Java properties files. 22 default: 'ini' 23 choices: ['ini', 'properties'] 24 file: 25 description: Name of the file to load. 26 default: ansible.ini 27 section: 28 default: global 29 description: Section where to lookup the key. 30 re: 31 default: False 32 type: boolean 33 description: Flag to indicate if the key supplied is a regexp. 34 encoding: 35 default: utf-8 36 description: Text encoding to use. 37 default: 38 description: Return value if the key is not in the ini file. 39 default: '' 40""" 41 42EXAMPLES = """ 43- debug: msg="User in integration is {{ lookup('ini', 'user section=integration file=users.ini') }}" 44 45- debug: msg="User in production is {{ lookup('ini', 'user section=production file=users.ini') }}" 46 47- debug: msg="user.name is {{ lookup('ini', 'user.name type=properties file=user.properties') }}" 48 49- debug: 50 msg: "{{ item }}" 51 with_ini: 52 - '.* section=section1 file=test.ini re=True' 53""" 54 55RETURN = """ 56_raw: 57 description: 58 - value(s) of the key(s) in the ini file 59 type: list 60 elements: str 61""" 62import os 63import re 64from io import StringIO 65 66from ansible.errors import AnsibleError, AnsibleAssertionError 67from ansible.module_utils.six.moves import configparser 68from ansible.module_utils._text import to_bytes, to_text 69from ansible.module_utils.common._collections_compat import MutableSequence 70from ansible.plugins.lookup import LookupBase 71 72 73def _parse_params(term): 74 '''Safely split parameter term to preserve spaces''' 75 76 keys = ['key', 'type', 'section', 'file', 're', 'default', 'encoding'] 77 params = {} 78 for k in keys: 79 params[k] = '' 80 81 thiskey = 'key' 82 for idp, phrase in enumerate(term.split()): 83 for k in keys: 84 if ('%s=' % k) in phrase: 85 thiskey = k 86 if idp == 0 or not params[thiskey]: 87 params[thiskey] = phrase 88 else: 89 params[thiskey] += ' ' + phrase 90 91 rparams = [params[x] for x in keys if params[x]] 92 return rparams 93 94 95class LookupModule(LookupBase): 96 97 def get_value(self, key, section, dflt, is_regexp): 98 # Retrieve all values from a section using a regexp 99 if is_regexp: 100 return [v for k, v in self.cp.items(section) if re.match(key, k)] 101 value = None 102 # Retrieve a single value 103 try: 104 value = self.cp.get(section, key) 105 except configparser.NoOptionError: 106 return dflt 107 return value 108 109 def run(self, terms, variables=None, **kwargs): 110 111 self.cp = configparser.ConfigParser() 112 113 ret = [] 114 for term in terms: 115 params = _parse_params(term) 116 key = params[0] 117 118 paramvals = { 119 'file': 'ansible.ini', 120 're': False, 121 'default': None, 122 'section': "global", 123 'type': "ini", 124 'encoding': 'utf-8', 125 } 126 127 # parameters specified? 128 try: 129 for param in params[1:]: 130 name, value = param.split('=') 131 if name not in paramvals: 132 raise AnsibleAssertionError('%s not in paramvals' % 133 name) 134 paramvals[name] = value 135 except (ValueError, AssertionError) as e: 136 raise AnsibleError(e) 137 138 # Retrieve file path 139 path = self.find_file_in_search_path(variables, 'files', 140 paramvals['file']) 141 142 # Create StringIO later used to parse ini 143 config = StringIO() 144 # Special case for java properties 145 if paramvals['type'] == "properties": 146 config.write(u'[java_properties]\n') 147 paramvals['section'] = 'java_properties' 148 149 # Open file using encoding 150 contents, show_data = self._loader._get_file_contents(path) 151 contents = to_text(contents, errors='surrogate_or_strict', 152 encoding=paramvals['encoding']) 153 config.write(contents) 154 config.seek(0, os.SEEK_SET) 155 156 self.cp.readfp(config) 157 var = self.get_value(key, paramvals['section'], 158 paramvals['default'], paramvals['re']) 159 if var is not None: 160 if isinstance(var, MutableSequence): 161 for v in var: 162 ret.append(v) 163 else: 164 ret.append(var) 165 return ret 166