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 <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""" 60import os 61import re 62from io import StringIO 63 64from ansible.errors import AnsibleError, AnsibleAssertionError 65from ansible.module_utils.six.moves import configparser 66from ansible.module_utils._text import to_bytes, to_text 67from ansible.module_utils.common._collections_compat import MutableSequence 68from ansible.plugins.lookup import LookupBase 69 70 71def _parse_params(term): 72 '''Safely split parameter term to preserve spaces''' 73 74 keys = ['key', 'type', 'section', 'file', 're', 'default', 'encoding'] 75 params = {} 76 for k in keys: 77 params[k] = '' 78 79 thiskey = 'key' 80 for idp, phrase in enumerate(term.split()): 81 for k in keys: 82 if ('%s=' % k) in phrase: 83 thiskey = k 84 if idp == 0 or not params[thiskey]: 85 params[thiskey] = phrase 86 else: 87 params[thiskey] += ' ' + phrase 88 89 rparams = [params[x] for x in keys if params[x]] 90 return rparams 91 92 93class LookupModule(LookupBase): 94 95 def get_value(self, key, section, dflt, is_regexp): 96 # Retrieve all values from a section using a regexp 97 if is_regexp: 98 return [v for k, v in self.cp.items(section) if re.match(key, k)] 99 value = None 100 # Retrieve a single value 101 try: 102 value = self.cp.get(section, key) 103 except configparser.NoOptionError: 104 return dflt 105 return value 106 107 def run(self, terms, variables=None, **kwargs): 108 109 self.cp = configparser.ConfigParser() 110 111 ret = [] 112 for term in terms: 113 params = _parse_params(term) 114 key = params[0] 115 116 paramvals = { 117 'file': 'ansible.ini', 118 're': False, 119 'default': None, 120 'section': "global", 121 'type': "ini", 122 'encoding': 'utf-8', 123 } 124 125 # parameters specified? 126 try: 127 for param in params[1:]: 128 name, value = param.split('=') 129 if name not in paramvals: 130 raise AnsibleAssertionError('%s not in paramvals' % 131 name) 132 paramvals[name] = value 133 except (ValueError, AssertionError) as e: 134 raise AnsibleError(e) 135 136 # Retrieve file path 137 path = self.find_file_in_search_path(variables, 'files', 138 paramvals['file']) 139 140 # Create StringIO later used to parse ini 141 config = StringIO() 142 # Special case for java properties 143 if paramvals['type'] == "properties": 144 config.write(u'[java_properties]\n') 145 paramvals['section'] = 'java_properties' 146 147 # Open file using encoding 148 contents, show_data = self._loader._get_file_contents(path) 149 contents = to_text(contents, errors='surrogate_or_strict', 150 encoding=paramvals['encoding']) 151 config.write(contents) 152 config.seek(0, os.SEEK_SET) 153 154 self.cp.readfp(config) 155 var = self.get_value(key, paramvals['section'], 156 paramvals['default'], paramvals['re']) 157 if var is not None: 158 if isinstance(var, MutableSequence): 159 for v in var: 160 ret.append(v) 161 else: 162 ret.append(var) 163 return ret 164