diff --git a/docsite/rst/playbooks_lookups.rst b/docsite/rst/playbooks_lookups.rst index 386230873c7..237a43e254e 100644 --- a/docsite/rst/playbooks_lookups.rst +++ b/docsite/rst/playbooks_lookups.rst @@ -139,6 +139,65 @@ default empty string return value if the key is not in the csv file .. note:: The default delimiter is TAB, *not* comma. +.. _ini_lookup: + +The INI File Lookup +``````````````````` +.. versionadded:: 2.0 + +The ``ini`` lookup reads the contents of a file in INI format (key1=value1). +This plugin retrieve the value on the right side after the equal sign ('=') of +a given section ([section]). You can also read a property file which - in this +case - does not contain section. + +Here's a simple example of an INI file with user/password configuration:: + + [production] + # My production information + user=robert + pass=somerandompassword + + [integration] + # My integration information + user=gertrude + pass=anotherpassword + + +We can use the ``ini`` plugin to lookup user configuration:: + + - debug: msg="User in integration is {{ lookup('ini', 'user section=integration file=users.ini') }}" + - debug: msg="User in production is {{ lookup('ini', 'user section=production file=users.ini') }}" + +Another example for this plugin is for looking for a value on java properties. +Here's a simple properties we'll take as an example:: + + user.name=robert + user.pass=somerandompassword + +You can retrieve the ``user.name`` field with the following lookup:: + + - debug: msg="user.name is {{ lookup('ini', 'user.name type=property file=user.properties') }}" + +The ``ini`` lookup supports several arguments like the csv plugin. The format for passing +arguments is:: + + lookup('ini', 'key [type=] [section=section] [file=file.ini] [re=true] [default=]') + +The first value in the argument is the ``key``, which must be an entry that +appears exactly once on keys. All other arguments are optional. + + +========== ============ ========================================================================================= +Field Default Description +---------- ------------ ----------------------------------------------------------------------------------------- +type ini Type of the file. Can be ini or properties (for java properties). +file ansible.ini Name of the file to load +section global Default section where to lookup for key. +re False The key is a regexp. +default empty string return value if the key is not in the ini file +========== ============ ========================================================================================= + +.. note:: In java properties files, there's no need to specify a section. .. _credstash_lookup: diff --git a/docsite/rst/playbooks_loops.rst b/docsite/rst/playbooks_loops.rst index a76254a966c..5acc6ae6301 100644 --- a/docsite/rst/playbooks_loops.rst +++ b/docsite/rst/playbooks_loops.rst @@ -347,6 +347,54 @@ It's uncommonly used:: debug: msg="at array position {{ item.0 }} there is a value {{ item.1 }}" with_indexed_items: "{{some_list}}" +.. _using_ini_with_a_loop: + +Using ini file with a loop +`````````````````````````` +.. versionadded: 2.0 + +The ini plugin can use regexp to retrieve a set of keys. As a consequence, we can loop over this set. Here is the ini file we'll use:: + + [section1] + value1=section1/value1 + value2=section1/value2 + + [section2] + value1=section2/value1 + value2=section2/value2 + +Here is an example of using ``with_ini``:: + + - debug: msg="{{item}}" + with_ini: value[1-2] section=section1 file=lookup.ini re=true + +And here is the returned value:: + + { + "changed": false, + "msg": "All items completed", + "results": [ + { + "invocation": { + "module_args": "msg=\"section1/value1\"", + "module_name": "debug" + }, + "item": "section1/value1", + "msg": "section1/value1", + "verbose_always": true + }, + { + "invocation": { + "module_args": "msg=\"section1/value2\"", + "module_name": "debug" + }, + "item": "section1/value2", + "msg": "section1/value2", + "verbose_always": true + } + ] + } + .. _flattening_a_list: Flattening A List diff --git a/lib/ansible/plugins/lookup/ini.py b/lib/ansible/plugins/lookup/ini.py new file mode 100644 index 00000000000..0c04f06909d --- /dev/null +++ b/lib/ansible/plugins/lookup/ini.py @@ -0,0 +1,96 @@ +# (c) 2015, Yannig Perre +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import StringIO +import os +import codecs +import ConfigParser +import re + +from ansible.errors import * +from ansible.plugins.lookup import LookupBase + +class LookupModule(LookupBase): + + def read_properties(self, filename, key, dflt, is_regexp): + config = StringIO.StringIO() + config.write('[java_properties]\n' + open(filename).read()) + config.seek(0, os.SEEK_SET) + self.cp.readfp(config) + return self.get_value(key, 'java_properties', dflt, is_regexp) + + def read_ini(self, filename, key, section, dflt, is_regexp): + self.cp.readfp(open(filename)) + return self.get_value(key, section, dflt, is_regexp) + + 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, e: + return dflt + return value + + def run(self, terms, variables=None, **kwargs): + + if isinstance(terms, basestring): + terms = [ terms ] + + basedir = self.get_basedir(variables) + self.basedir = basedir + self.cp = ConfigParser.ConfigParser() + + ret = [] + for term in terms: + params = term.split() + key = params[0] + + paramvals = { + 'file' : 'ansible.ini', + 're' : False, + 'default' : None, + 'section' : "global", + 'type' : "ini", + } + + # parameters specified? + try: + for param in params[1:]: + name, value = param.split('=') + assert(name in paramvals) + paramvals[name] = value + except (ValueError, AssertionError), e: + raise errors.AnsibleError(e) + + path = self._loader.path_dwim_relative(basedir, 'files', paramvals['file']) + if paramvals['type'] == "properties": + var = self.read_properties(path, key, paramvals['default'], paramvals['re']) + else: + var = self.read_ini(path, key, paramvals['section'], paramvals['default'], paramvals['re']) + if var is not None: + if type(var) is list: + for v in var: + ret.append(v) + else: + ret.append(var) + return ret diff --git a/test/integration/lookup.ini b/test/integration/lookup.ini new file mode 100644 index 00000000000..16500fd8990 --- /dev/null +++ b/test/integration/lookup.ini @@ -0,0 +1,24 @@ +[global] +# A comment +value1=Text associated with value1 and global section +value2=Same for value2 and global section +value.dot=Properties with dot +field.with.space = another space + +[section1] +value1=section1/value1 +value2=section1/value2 + +[value_section] +value1=1 +value2=2 +value3=3 +other1=4 +other2=5 + +[other_section] +value1=1 +value2=2 +value3=3 +other1=4 +other2=5 diff --git a/test/integration/lookup.properties b/test/integration/lookup.properties new file mode 100644 index 00000000000..f388d8cfbf5 --- /dev/null +++ b/test/integration/lookup.properties @@ -0,0 +1,5 @@ +# A comment +value1=Text associated with value1 +value2=Same for value2 +value.dot=Properties with dot +field.with.space = another space diff --git a/test/integration/test_lookup_properties.yml b/test/integration/test_lookup_properties.yml new file mode 100644 index 00000000000..4d22ce642c4 --- /dev/null +++ b/test/integration/test_lookup_properties.yml @@ -0,0 +1,40 @@ +--- +- name: "Lookup test" + hosts: "localhost" +# connection: local + tasks: + - name: "read properties value" + set_fact: + test1: "{{lookup('ini', 'value1 type=properties file=lookup.properties')}}" + test2: "{{lookup('ini', 'value2 type=properties file=lookup.properties')}}" + test_dot: "{{lookup('ini', 'value.dot type=properties file=lookup.properties')}}" + field_with_space: "{{lookup('ini', 'field.with.space type=properties file=lookup.properties')}}" + - debug: var={{item}} + with_items: [ 'test1', 'test2', 'test_dot', 'field_with_space' ] + - name: "read ini value" + set_fact: + value1_global: "{{lookup('ini', 'value1 section=global file=lookup.ini')}}" + value2_global: "{{lookup('ini', 'value2 section=global file=lookup.ini')}}" + value1_section1: "{{lookup('ini', 'value1 section=section1 file=lookup.ini')}}" + - debug: var={{item}} + with_items: [ 'value1_global', 'value2_global', 'value1_section1' ] + - name: "read ini value with section and regexp" + set_fact: + value_section: "{{lookup('ini', 'value[1-2] section=value_section file=lookup.ini re=true')}}" + other_section: "{{lookup('ini', 'other[1-2] section=other_section file=lookup.ini re=true')}}" + - debug: var={{item}} + with_items: [ 'value_section', 'other_section' ] + - name: "Reading unknown value" + set_fact: + unknown: "{{lookup('ini', 'value2 default=unknown section=section1 file=lookup.ini')}}" + - debug: var=unknown + - name: "Looping over section section1" + debug: msg="{{item}}" + with_ini: value[1-2] section=section1 file=lookup.ini re=true + - name: "Looping over section value_section" + debug: msg="{{item}}" + with_ini: value[1-2] section=value_section file=lookup.ini re=true + - debug: msg="{{item}}" + with_ini: value[1-2] section=section1 file=lookup.ini re=true + register: _ + - debug: var=_