Add ignore_none_type to config template

It is sometimes useful to tell ConfigTemplateParser to write
out options that are valueless and not suffixed with '=' or ':',
such as when overriding a my.cnf.

This commit adds the 'ignore_none_type' attribute to the config_template
module. If this attribute is set to false, then valueless options will be
written out to the resultant INI file as-is, without the '=' or ':' suffix.

Change-Id: I5c88b2019c01b44193a5d0df9299ecce6de52f01
Partial-Bug: #1693234
This commit is contained in:
Miguel Alex Cantu 2017-06-23 19:32:44 +00:00
parent 30af08208d
commit 76d5f02a32
5 changed files with 90 additions and 10 deletions

View File

@ -155,9 +155,17 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
key = var3
key = var2
"""
def _write(self, fp, section, item, entry):
def __init__(self, *args, **kwargs):
self.ignore_none_type = bool(kwargs.pop('ignore_none_type', True))
ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)
def _write(self, fp, section, key, item, entry):
if section:
if (item is not None) or (self._optcre == self.OPTCRE):
# If we are not ignoring a none type value, then print out
# the option name only if the value type is None.
if not self.ignore_none_type and item is None:
fp.write(key + '\n')
elif (item is not None) or (self._optcre == self.OPTCRE):
fp.write(entry)
else:
fp.write(entry)
@ -167,14 +175,14 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
for item in value:
item = str(item).replace('\n', '\n\t')
entry = "%s = %s\n" % (key, item)
self._write(fp, section, item, entry)
self._write(fp, section, key, item, entry)
else:
if isinstance(value, list):
_value = [str(i.replace('\n', '\n\t')) for i in value]
entry = '%s = %s\n' % (key, ','.join(_value))
else:
entry = '%s = %s\n' % (key, str(value).replace('\n', '\n\t'))
self._write(fp, section, value, entry)
self._write(fp, section, key, value, entry)
def write(self, fp):
if self._defaults:
@ -268,7 +276,11 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
class ActionModule(ActionBase):
TRANSFERS_FILES = True
def return_config_overrides_ini(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_ini(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Returns string value from a modified config file.
:param config_overrides: ``dict``
@ -281,7 +293,8 @@ class ActionModule(ActionBase):
try:
config = ConfigTemplateParser(
allow_no_value=True,
dict_type=MultiKeyDict
dict_type=MultiKeyDict,
ignore_none_type=ignore_none_type
)
config.optionxform = str
except Exception:
@ -344,7 +357,11 @@ class ActionModule(ActionBase):
else:
config.set(str(section), str(key), str(value))
def return_config_overrides_json(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_json(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Returns config json
Its important to note that file ordering will not be preserved as the
@ -366,7 +383,11 @@ class ActionModule(ActionBase):
sort_keys=True
)
def return_config_overrides_yaml(self, config_overrides, resultant, list_extend=True):
def return_config_overrides_yaml(self,
config_overrides,
resultant,
list_extend=True,
ignore_none_type=True):
"""Return config yaml.
:param config_overrides: ``dict``
@ -484,13 +505,23 @@ class ActionModule(ActionBase):
if user_dest.endswith(os.sep):
user_dest = os.path.join(user_dest, os.path.basename(source))
# Get ignore_none_type
# In some situations(i.e. my.cnf files), INI files can have valueless
# options that don't have a '=' or ':' suffix. In these cases,
# ConfigParser gives these options a "None" value. If ignore_none_type
# is set to true, these key/value options will be ignored, if it's set
# to false, then ConfigTemplateParser will write out only the option
# name with out the '=' or ':' suffix. The default is true.
ignore_none_type = self._task.args.get('ignore_none_type', True)
return True, dict(
source=source,
dest=user_dest,
config_overrides=self._task.args.get('config_overrides', dict()),
config_type=config_type,
searchpath=searchpath,
list_extend=list_extend
list_extend=list_extend,
ignore_none_type=ignore_none_type
)
def run(self, tmp=None, task_vars=None):
@ -563,7 +594,8 @@ class ActionModule(ActionBase):
resultant = type_merger(
config_overrides=_vars['config_overrides'],
resultant=resultant,
list_extend=_vars.get('list_extend', True)
list_extend=_vars.get('list_extend', True),
ignore_none_type=_vars.get('ignore_none_type', True)
)
# Re-template the resultant object as it may have new data within it
@ -595,6 +627,7 @@ class ActionModule(ActionBase):
new_module_args.pop('config_overrides', None)
new_module_args.pop('config_type', None)
new_module_args.pop('list_extend', None)
new_module_args.pop('ignore_none_type', None)
# Content from config_template is converted to src
new_module_args.pop('content', None)

View File

@ -49,6 +49,16 @@ options:
choices:
- True
- False
ignore_none_type:
description:
- Can be true or false. If ignore_none_type is set to true, then
valueless INI options will not be written out to the resultant file.
If it's set to false, then config_template will write out only
the option name without the '=' or ':' suffix. The default is true.
choices:
- True
- False
author: Kevin Carter
"""

View File

@ -0,0 +1,9 @@
---
features:
- |
The config_template template module now supports writing out valueless
INI options without suffixing them with '=' or ':'. This is done via the
'ignore_none_type' attribute. If ignore_none_type is set to true, these
key/value entries will be ignored, if it's set to false, then
ConfigTemplateParser will write out only the option name without the
'=' or ':' suffix. The default is true.

View File

@ -0,0 +1,8 @@
[alfa]
bravo = charlie
delta = echo
[foxtrot]
golf = hotel
india
juliett kilo
lima = mike

View File

@ -136,6 +136,26 @@
that:
- "(content_no_overrides_file.content | b64decode | from_json) == (content_no_overrides_file_expected.content | b64decode | from_json)"
# Test the ignore_none_type attribute when set to False
- name: Template test with ignore_none_type set to false
config_template:
src: "{{ playbook_dir }}/templates/test_ignore_none_type.ini"
dest: "/tmp/test_ignore_none_type.ini"
config_overrides: "{{ test_config_ini_overrides }}"
config_type: "ini"
ignore_none_type: False
- name: Read test_ignore_none_type.ini
slurp:
src: /tmp/test_ignore_none_type.ini
register: test_ignore_none_type
- debug:
msg: "test_ignore_none_type.ini - {{ test_ignore_none_type.content | b64decode }}"
- name: Validate output has valueless options printed out
assert:
that:
- "{{ test_ignore_none_type.content | b64decode | search('(?m)^india$') }}"
- "{{ test_ignore_none_type.content | b64decode | search('(?m)^juliett kilo$') }}"
vars:
test_config_ini_overrides:
DEFAULT: