Allow default section in ini to be specified
The default section in an ini file will always be 'DEFAULT', but for some configuration files you may want to be able to set this to ensure another section is at the top. 'global' for example, when working with fio. This PR adds the ability to set the section to be at the top, but will default to 'DEFAULT' so no change will happen to existing tasks running config_template. Change-Id: Ifbc0b91aef46f3c2d98e73fdc9ab888244550bab
This commit is contained in:
parent
3be4203a87
commit
75cb8670fb
@ -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
|
||||||
|
@ -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
|
||||||
|
5
releasenotes/notes/top_ini_section-c28d7acadf5fe836.yaml
Normal file
5
releasenotes/notes/top_ini_section-c28d7acadf5fe836.yaml
Normal 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``.
|
8
tests/files/test_default_section.ini.expected
Normal file
8
tests/files/test_default_section.ini.expected
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[global]
|
||||||
|
test1 = 1
|
||||||
|
test2 = 2
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
setting1 = 1
|
||||||
|
setting2 = 2
|
||||||
|
|
5
tests/templates/test_default_section.ini
Normal file
5
tests/templates/test_default_section.ini
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[section1]
|
||||||
|
setting1=1
|
||||||
|
|
||||||
|
[global]
|
||||||
|
test1=1
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user