Merge "Allow default section in ini to be specified"

This commit is contained in:
Zuul 2018-06-11 16:29:20 +00:00 committed by Gerrit Code Review
commit c7b39fa597
6 changed files with 85 additions and 22 deletions

View File

@ -72,6 +72,7 @@ class MultiKeyDict(dict):
>>> print(z) >>> print(z)
... {'a': tuple(['1', '2']), 'c': {'a': 1}, 'b': ['a', 'b', 'c']} ... {'a': tuple(['1', '2']), 'c': {'a': 1}, 'b': ['a', 'b', 'c']}
""" """
def __setitem__(self, key, value): def __setitem__(self, key, value):
if key in self: if key in self:
if isinstance(self[key], tuple): if isinstance(self[key], tuple):
@ -134,9 +135,11 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
key = var3 key = var3
key = var2 key = var2
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._comments = {} self._comments = {}
self.ignore_none_type = bool(kwargs.pop('ignore_none_type', True)) self.ignore_none_type = bool(kwargs.pop('ignore_none_type', True))
self.default_section = str(kwargs.pop('default_section', 'DEFAULT'))
ConfigParser.RawConfigParser.__init__(self, *args, **kwargs) ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)
def _write(self, fp, section, key, item, entry): def _write(self, fp, section, key, item, entry):
@ -165,28 +168,33 @@ class ConfigTemplateParser(ConfigParser.RawConfigParser):
self._write(fp, section, key, value, entry) self._write(fp, section, key, value, entry)
def write(self, fp): def write(self, fp):
def _do_write(section_name, section, section_bool=False):
_write_comments(section_name)
fp.write("[%s]\n" % section_name)
for key, value in sorted(section.items()):
_write_comments(section_name, optname=key)
self._write_check(fp, key=key, value=value,
section=section_bool)
else:
fp.write("\n")
def _write_comments(section, optname=None): def _write_comments(section, optname=None):
comsect = self._comments.get(section, {}) comsect = self._comments.get(section, {})
if optname in comsect: if optname in comsect:
fp.write(''.join(comsect[optname])) fp.write(''.join(comsect[optname]))
if self.default_section != 'DEFAULT' and self._sections.get(
self.default_section, False):
_do_write(self.default_section,
self._sections[self.default_section],
section_bool=True)
self._sections.pop(self.default_section)
if self._defaults: if self._defaults:
_write_comments('DEFAULT') _do_write('DEFAULT', self._defaults)
fp.write("[%s]\n" % 'DEFAULT')
for key, value in sorted(self._defaults.items()):
_write_comments('DEFAULT', optname=key)
self._write_check(fp, key=key, value=value)
else:
fp.write("\n")
for section in sorted(self._sections): for section in sorted(self._sections):
_write_comments(section) _do_write(section, self._sections[section], section_bool=True)
fp.write("[%s]\n" % section)
for key, value in sorted(self._sections[section].items()):
_write_comments(section, optname=key)
self._write_check(fp, key=key, value=value, section=True)
else:
fp.write("\n")
def _read(self, fp, fpname): def _read(self, fp, fpname):
comments = [] comments = []
@ -293,7 +301,8 @@ class ActionModule(ActionBase):
config_overrides, config_overrides,
resultant, resultant,
list_extend=True, list_extend=True,
ignore_none_type=True): ignore_none_type=True,
default_section='DEFAULT'):
"""Returns string value from a modified config file. """Returns string value from a modified config file.
:param config_overrides: ``dict`` :param config_overrides: ``dict``
@ -307,7 +316,8 @@ class ActionModule(ActionBase):
config = ConfigTemplateParser( config = ConfigTemplateParser(
allow_no_value=True, allow_no_value=True,
dict_type=MultiKeyDict, dict_type=MultiKeyDict,
ignore_none_type=ignore_none_type ignore_none_type=ignore_none_type,
default_section=default_section
) )
config.optionxform = str config.optionxform = str
except Exception: except Exception:
@ -376,7 +386,8 @@ class ActionModule(ActionBase):
config_overrides, config_overrides,
resultant, resultant,
list_extend=True, list_extend=True,
ignore_none_type=True): ignore_none_type=True,
default_section='DEFAULT'):
"""Returns config json """Returns config json
Its important to note that file ordering will not be preserved as the Its important to note that file ordering will not be preserved as the
@ -390,7 +401,8 @@ class ActionModule(ActionBase):
merged_resultant = self._merge_dict( merged_resultant = self._merge_dict(
base_items=original_resultant, base_items=original_resultant,
new_items=config_overrides, new_items=config_overrides,
list_extend=list_extend list_extend=list_extend,
default_section=default_section
) )
return json.dumps( return json.dumps(
merged_resultant, merged_resultant,
@ -402,7 +414,8 @@ class ActionModule(ActionBase):
config_overrides, config_overrides,
resultant, resultant,
list_extend=True, list_extend=True,
ignore_none_type=True): ignore_none_type=True,
default_section='DEFAULT'):
"""Return config yaml. """Return config yaml.
:param config_overrides: ``dict`` :param config_overrides: ``dict``
@ -537,6 +550,8 @@ class ActionModule(ActionBase):
# name with out the '=' or ':' suffix. The default is true. # name with out the '=' or ':' suffix. The default is true.
ignore_none_type = self._task.args.get('ignore_none_type', True) ignore_none_type = self._task.args.get('ignore_none_type', True)
default_section = self._task.args.get('default_section', 'DEFAULT')
return True, dict( return True, dict(
source=source, source=source,
dest=user_dest, dest=user_dest,
@ -544,7 +559,8 @@ class ActionModule(ActionBase):
config_type=config_type, config_type=config_type,
searchpath=searchpath, searchpath=searchpath,
list_extend=list_extend, list_extend=list_extend,
ignore_none_type=ignore_none_type ignore_none_type=ignore_none_type,
default_section=default_section
) )
def run(self, tmp=None, task_vars=None): def run(self, tmp=None, task_vars=None):
@ -618,7 +634,8 @@ class ActionModule(ActionBase):
config_overrides=_vars['config_overrides'], config_overrides=_vars['config_overrides'],
resultant=resultant, 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) ignore_none_type=_vars.get('ignore_none_type', True),
default_section=_vars.get('default_section', 'DEFAULT')
) )
# Re-template the resultant object as it may have new data within it # Re-template the resultant object as it may have new data within it
@ -651,6 +668,7 @@ class ActionModule(ActionBase):
new_module_args.pop('config_type', None) new_module_args.pop('config_type', None)
new_module_args.pop('list_extend', None) new_module_args.pop('list_extend', None)
new_module_args.pop('ignore_none_type', None) new_module_args.pop('ignore_none_type', None)
new_module_args.pop('default_section', None)
# Content from config_template is converted to src # Content from config_template is converted to src
new_module_args.pop('content', None) new_module_args.pop('content', None)
@ -663,7 +681,7 @@ class ActionModule(ActionBase):
if self._play_context.diff: if self._play_context.diff:
rc['diff'] = [] rc['diff'] = []
rc['diff'].append(self._get_diff_data(_vars['dest'], rc['diff'].append(self._get_diff_data(_vars['dest'],
transferred_data, task_vars)) transferred_data, task_vars))
if self._task.args.get('content'): if self._task.args.get('content'):
os.remove(_vars['source']) os.remove(_vars['source'])
return rc return rc

View File

@ -58,6 +58,12 @@ options:
choices: choices:
- True - True
- False - False
default_section:
description:
- Specify the default section for INI configuration files. This is the
section that will appear at the top of the configuration file. For
example 'global'.
default: 'DEFAULT'
author: Kevin Carter author: Kevin Carter
""" """
@ -68,6 +74,7 @@ EXAMPLES = """
src: templates/test.ini.j2 src: templates/test.ini.j2
dest: /tmp/test.ini dest: /tmp/test.ini
config_overrides: {} config_overrides: {}
top_ini_section: 'global'
config_type: ini config_type: ini
- name: run config template json - name: run config template json

View File

@ -0,0 +1,5 @@
---
features:
- Allow the default section in an ini file to be specified
using the ``default_section`` variable when calling a
``config_template`` task. This defaults to ``DEFAULT``.

View File

@ -0,0 +1,8 @@
[global]
test1 = 1
test2 = 2
[section1]
setting1 = 1
setting2 = 2

View File

@ -0,0 +1,5 @@
[section1]
setting1=1
[global]
test1=1

View File

@ -215,6 +215,21 @@
- [ 0, 1, 2 ] - [ 0, 1, 2 ]
- [ "{{ test_config_ini_overrides }}" ] - [ "{{ test_config_ini_overrides }}" ]
- name: Put down default_section_expected file
copy:
src: "{{ playbook_dir }}/files/test_default_section.ini.expected"
dest: "/tmp/test_default_section.ini"
- name: Template using default_section
config_template:
src: "{{ playbook_dir }}/templates/test_default_section.ini"
dest: "/tmp/test_default_section.ini"
config_type: "ini"
config_overrides: "{{ test_default_section_overrides }}"
default_section: "global"
register: template_changed
failed_when: template_changed | changed
vars: vars:
test_config_ini_overrides: test_config_ini_overrides:
DEFAULT: DEFAULT:
@ -259,3 +274,8 @@
- 4 - 4
test_config_yml_hostvars_overrides: test_config_yml_hostvars_overrides:
test_hostvar: "{{ ansible_default_ipv4.address }}" test_hostvar: "{{ ansible_default_ipv4.address }}"
test_default_section_overrides:
global:
test2: 2
section1:
setting2: 2