From 2a92120ffaf21d853eca3913ea6fe1997080ac0b Mon Sep 17 00:00:00 2001 From: Pilou Date: Thu, 13 Jul 2017 21:04:20 +0200 Subject: [PATCH] INI inventory plugin: add documentation about variable types (#25798) * INI inventory: check variable types * INI inventory: add doc about variable types Fixes #25784 --- docs/docsite/rst/intro_inventory.rst | 4 ++++ lib/ansible/plugins/inventory/ini.py | 3 +++ test/units/inventory/test_inventory.py | 20 +++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/docsite/rst/intro_inventory.rst b/docs/docsite/rst/intro_inventory.rst index 9b0cbe99a3..d08d24c2af 100644 --- a/docs/docsite/rst/intro_inventory.rst +++ b/docs/docsite/rst/intro_inventory.rst @@ -60,6 +60,10 @@ Suppose you have just static IPs and want to set up some aliases that live in yo In the above example, trying to ansible against the host alias "jumper" (which may not even be a real hostname) will contact 192.0.2.50 on port 5555. Note that this is using a feature of the inventory file to define some special variables. Generally speaking this is not the best way to define variables that describe your system policy, but we'll share suggestions on doing this later. We're just getting started. +.. note:: Values passed in using the ``key=value`` syntax are interpreted as Python literal structure (strings, numbers, tuples, lists, dicts, + booleans, None), alternatively as string. For example ``var=FALSE`` would create a string equal to 'FALSE'. Do not rely on types set + during definition, always make sure you specify type with a filter when needed when consuming the variable. + Adding a lot of hosts? If you have a lot of hosts following similar patterns you can do this rather than listing each hostname: .. code-block:: ini diff --git a/lib/ansible/plugins/inventory/ini.py b/lib/ansible/plugins/inventory/ini.py index dd251036af..9351159adc 100644 --- a/lib/ansible/plugins/inventory/ini.py +++ b/lib/ansible/plugins/inventory/ini.py @@ -27,6 +27,9 @@ DOCUMENTATION: - The C(children) modifier indicates that the section contains groups. - The C(vars) modifier indicates that the section contains variables assigned to members of the group. - Anything found outside a section is considered an 'ungrouped' host. + - Values passed in using the C(key=value) syntax are interpreted as Python literal structure (strings, numbers, tuples, lists, dicts, + booleans, None), alternatively as string. For example C(var=FALSE) would create a string equal to 'FALSE'. Do not rely on types set + during definition, always make sure you specify type with a filter when needed when consuming the variable. notes: - It takes the place of the previously hardcoded INI inventory. - To function it requires being whitelisted in configuration. diff --git a/test/units/inventory/test_inventory.py b/test/units/inventory/test_inventory.py index be40ac03bf..d06865fb69 100644 --- a/test/units/inventory/test_inventory.py +++ b/test/units/inventory/test_inventory.py @@ -22,10 +22,10 @@ __metaclass__ = type import string from ansible.compat.tests import unittest -from ansible.compat.tests.mock import patch +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_text from ansible.inventory.manager import InventoryManager, split_host_pattern -from ansible.vars.manager import VariableManager from units.mock.loader import DictDataLoader @@ -109,7 +109,7 @@ class TestInventory(unittest.TestCase): ) -class InventoryDefaultGroup(unittest.TestCase): +class IniInventory(unittest.TestCase): def test_empty_inventory(self): inventory = self._get_inventory('') @@ -142,6 +142,20 @@ class InventoryDefaultGroup(unittest.TestCase): host5 """) + def test_ini_variables_stringify(self): + values = ['string', 'no', 'No', 'false', 'FALSE', [], False, 0] + + inventory_content = "host1 " + inventory_content += ' '.join(['var%s=%s' % (i, to_text(x)) for i, x in enumerate(values)]) + inventory = self._get_inventory(inventory_content) + + variables = inventory.get_host('host1').vars + for i in range(len(values)): + if isinstance(values[i], string_types): + self.assertIsInstance(variables['var%s' % i], string_types) + else: + self.assertIsInstance(variables['var%s' % i], type(values[i])) + def _get_inventory(self, inventory_content): fake_loader = DictDataLoader({__file__: inventory_content})