Skip to content

Commit

Permalink
Merge pull request #48310 from mtorromeo/ini-manage-backports
Browse files Browse the repository at this point in the history
Backport ini_manage fixes to 2018.3
  • Loading branch information
Nicole Thomas committed Jul 5, 2018
2 parents 84fd3d2 + 88f80fd commit 432cbbb
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 97 deletions.
82 changes: 63 additions & 19 deletions salt/modules/ini_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def __virtual__():
return __virtualname__


ini_regx = re.compile(r'^\s*\[(.+?)\]\s*$', flags=re.M)
com_regx = re.compile(r'^\s*(#|;)\s*(.*)')
indented_regx = re.compile(r'(\s+)(.*)')
INI_REGX = re.compile(r'^\s*\[(.+?)\]\s*$', flags=re.M)
COM_REGX = re.compile(r'^\s*(#|;)\s*(.*)')
INDENTED_REGX = re.compile(r'(\s+)(.*)')


def set_option(file_name, sections=None, separator='='):
Expand Down Expand Up @@ -105,7 +105,13 @@ def get_option(file_name, section, option, separator='='):
salt '*' ini.get_option /path/to/ini section_name option_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
return inifile.get(section, {}).get(option, None)
if section:
try:
return inifile.get(section, {}).get(option, None)
except AttributeError:
return None
else:
return inifile.get(option, None)


def remove_option(file_name, section, option, separator='='):
Expand All @@ -129,7 +135,10 @@ def remove_option(file_name, section, option, separator='='):
salt '*' ini.remove_option /path/to/ini section_name option_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
value = inifile.get(section, {}).pop(option, None)
if isinstance(inifile.get(section), (dict, OrderedDict)):
value = inifile.get(section, {}).pop(option, None)
else:
value = inifile.pop(option, None)
inifile.flush()
return value

Expand Down Expand Up @@ -182,15 +191,53 @@ def remove_section(file_name, section, separator='='):
salt '*' ini.remove_section /path/to/ini section_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
if section in inifile:
section = inifile.pop(section)
inifile.flush()
ret = {}
for key, value in six.iteritems(section):
if key[0] != '#':
ret.update({key: value})
return ret


def get_ini(file_name, separator='='):
'''
Retrieve whole structure from an ini file and return it as dictionary.
API Example:
.. code-block:: python
import salt
sc = salt.client.get_local_client()
sc.cmd('target', 'ini.get_ini',
[path_to_ini_file])
CLI Example:
.. code-block:: bash
salt '*' ini.get_ini /path/to/ini
'''
def ini_odict2dict(odict):
'''
Transform OrderedDict to regular dict recursively
:param odict: OrderedDict
:return: regular dict
'''
ret = {}
for key, val in six.iteritems(odict):
if key[0] != '#':
if isinstance(val, (dict, OrderedDict)):
ret.update({key: ini_odict2dict(val)})
else:
ret.update({key: val})
return ret

inifile = _Ini.get_ini_file(file_name, separator=separator)
section = inifile.pop(section, {})
inifile.flush()
ret = {}
for key, value in six.iteritems(section):
if key[0] != '#':
ret.update({key: value})
return ret
return ini_odict2dict(inifile)


class _Section(OrderedDict):
Expand Down Expand Up @@ -221,15 +268,15 @@ def refresh(self, inicontents=None):
self.pop(opt)
for opt_str in inicontents.split(os.linesep):
# Match comments
com_match = com_regx.match(opt_str)
com_match = COM_REGX.match(opt_str)
if com_match:
name = '#comment{0}'.format(comment_count)
self.com = com_match.group(1)
comment_count += 1
self.update({name: opt_str})
continue
# Add indented lines to the value of the previous entry.
indented_match = indented_regx.match(opt_str)
indented_match = INDENTED_REGX.match(opt_str)
if indented_match:
indent = indented_match.group(1).replace('\t', ' ')
if indent > curr_indent:
Expand Down Expand Up @@ -318,7 +365,7 @@ def gen_ini(self):
sections_dict = OrderedDict()
for name, value in six.iteritems(self):
# Handle Comment Lines
if com_regx.match(name):
if COM_REGX.match(name):
yield '{0}{1}'.format(value, os.linesep)
# Handle Sections
elif isinstance(value, _Section):
Expand Down Expand Up @@ -363,9 +410,6 @@ def __ne__(self, item):


class _Ini(_Section):
def __init__(self, name, inicontents='', separator='=', commenter='#'):
super(_Ini, self).__init__(name, inicontents, separator, commenter)

def refresh(self, inicontents=None):
if inicontents is None:
try:
Expand All @@ -382,7 +426,7 @@ def refresh(self, inicontents=None):
# Remove anything left behind from a previous run.
self.clear()

inicontents = ini_regx.split(inicontents)
inicontents = INI_REGX.split(inicontents)
inicontents.reverse()
# Pop anything defined outside of a section (ie. at the top of
# the ini file).
Expand Down
158 changes: 102 additions & 56 deletions salt/states/ini_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# Import Salt libs
from salt.ext import six
from salt.utils.odict import OrderedDict

__virtualname__ = 'ini'

Expand Down Expand Up @@ -53,46 +54,79 @@ def options_present(name, sections=None, separator='=', strict=False):
'comment': 'No anomaly detected'
}
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
for section in sections or {}:
section_name = ' in section ' + section if section != 'DEFAULT_IMPLICIT' else ''
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
for key in sections[section]:
cur_value = cur_section.get(key)
if cur_value == six.text_type(sections[section][key]):
ret['comment'] += 'Key {0}{1} unchanged.\n'.format(key, section_name)
continue
ret['comment'] += 'Changed key {0}{1}.\n'.format(key, section_name)
ret['result'] = None
if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
# pylint: disable=too-many-nested-blocks
try:
changes = {}
if sections:
for section_name, section_body in sections.items():
options = {}
for sname, sbody in sections.items():
if not isinstance(sbody, (dict, OrderedDict)):
options.update({sname: sbody})
cur_ini = __salt__['ini.get_ini'](name, separator)
original_top_level_opts = {}
original_sections = {}
for key, val in cur_ini.items():
if isinstance(val, (dict, OrderedDict)):
original_sections.update({key: val})
else:
original_top_level_opts.update({key: val})
if __opts__['test']:
for option in options:
if option in original_top_level_opts:
if six.text_type(original_top_level_opts[option]) == six.text_type(options[option]):
ret['comment'] += 'Unchanged key {0}.\n'.format(option)
else:
ret['comment'] += 'Changed key {0}.\n'.format(option)
ret['result'] = None
else:
ret['comment'] += 'Changed key {0}.\n'.format(option)
ret['result'] = None
else:
options_updated = __salt__['ini.set_option'](name, options, separator)
changes.update(options_updated)
if strict:
for opt_to_remove in set(original_top_level_opts).difference(options):
if __opts__['test']:
ret['comment'] += 'Removed key {0}.\n'.format(opt_to_remove)
ret['result'] = None
else:
__salt__['ini.remove_option'](name, None, opt_to_remove, separator)
changes.update({opt_to_remove: {'before': original_top_level_opts[opt_to_remove],
'after': None}})
for section_name, section_body in [(sname, sbody) for sname, sbody in sections.items()
if isinstance(sbody, (dict, OrderedDict))]:
section_descr = ' in section ' + section_name if section_name else ''
changes[section_name] = {}
if strict:
original = __salt__['ini.get_section'](name, section_name, separator)
original = cur_ini.get(section_name, {})
for key_to_remove in set(original.keys()).difference(section_body.keys()):
orig_value = __salt__['ini.get_option'](name, section_name, key_to_remove, separator)
__salt__['ini.remove_option'](name, section_name, key_to_remove, separator)
changes[section_name].update({key_to_remove: ''})
changes[section_name].update({key_to_remove: {'before': orig_value,
'after': None}})
options_updated = __salt__['ini.set_option'](name, {section_name: section_body}, separator)
if options_updated:
changes[section_name].update(options_updated[section_name])
if not changes[section_name]:
del changes[section_name]
orig_value = original_sections.get(section_name, {}).get(key_to_remove, '#-#-')
if __opts__['test']:
ret['comment'] += 'Deleted key {0}{1}.\n'.format(key_to_remove, section_descr)
ret['result'] = None
else:
__salt__['ini.remove_option'](name, section_name, key_to_remove, separator)
changes[section_name].update({key_to_remove: ''})
changes[section_name].update({key_to_remove: {'before': orig_value,
'after': None}})
if __opts__['test']:
for option in section_body:
if six.text_type(section_body[option]) == \
six.text_type(original_sections.get(section_name, {}).get(option, '#-#-')):
ret['comment'] += 'Unchanged key {0}{1}.\n'.format(option, section_descr)
else:
ret['comment'] += 'Changed key {0}{1}.\n'.format(option, section_descr)
ret['result'] = None
else:
options_updated = __salt__['ini.set_option'](name, {section_name: section_body}, separator)
if options_updated:
changes[section_name].update(options_updated[section_name])
if not changes[section_name]:
del changes[section_name]
else:
changes = __salt__['ini.set_option'](name, sections, separator)
if not __opts__['test']:
changes = __salt__['ini.set_option'](name, sections, separator)
except (IOError, KeyError) as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
Expand All @@ -102,10 +136,10 @@ def options_present(name, sections=None, separator='=', strict=False):
ret['comment'] = 'Errors encountered. {0}'.format(changes['error'])
ret['changes'] = {}
else:
for name, body in changes.items():
for ciname, body in changes.items():
if body:
ret['comment'] = 'Changes take effect'
ret['changes'].update({name: changes[name]})
ret['changes'].update({ciname: changes[ciname]})
return ret


Expand Down Expand Up @@ -137,20 +171,31 @@ def options_absent(name, sections=None, separator='='):
ret['result'] = True
ret['comment'] = ''
for section in sections or {}:
section_name = ' in section ' + section if section != 'DEFAULT_IMPLICIT' else ''
section_name = ' in section ' + section if section else ''
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
for key in sections[section]:
cur_value = cur_section.get(key)
if not cur_value:
ret['comment'] += 'Key {0}{1} does not exist.\n'.format(key, section_name)
except AttributeError:
cur_section = section
if isinstance(sections[section], (dict, OrderedDict)):
for key in sections[section]:
cur_value = cur_section.get(key)
if not cur_value:
ret['comment'] += 'Key {0}{1} does not exist.\n'.format(key, section_name)
continue
ret['comment'] += 'Deleted key {0}{1}.\n'.format(key, section_name)
ret['result'] = None
else:
option = section
if not __salt__['ini.get_option'](name, None, option, separator):
ret['comment'] += 'Key {0} does not exist.\n'.format(option)
continue
ret['comment'] += 'Deleted key {0}{1}.\n'.format(key, section_name)
ret['comment'] += 'Deleted key {0}.\n'.format(option)
ret['result'] = None

if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
Expand All @@ -168,6 +213,9 @@ def options_absent(name, sections=None, separator='='):
if section not in ret['changes']:
ret['changes'].update({section: {}})
ret['changes'][section].update({key: current_value})
if not isinstance(sections[section], (dict, OrderedDict)):
ret['changes'].update({section: current_value})
# break
ret['comment'] = 'Changes take effect'
return ret

Expand Down Expand Up @@ -197,18 +245,16 @@ def sections_present(name, sections=None, separator='='):
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
try:
cur_ini = __salt__['ini.get_ini'](name, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
for section in sections or {}:
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if dict(sections[section]) == cur_section:
if section in cur_ini:
ret['comment'] += 'Section unchanged {0}.\n'.format(section)
continue
elif cur_section:
ret['comment'] += 'Changed existing section {0}.\n'.format(section)
else:
ret['comment'] += 'Created new section {0}.\n'.format(section)
ret['result'] = None
Expand Down Expand Up @@ -255,14 +301,14 @@ def sections_absent(name, sections=None, separator='='):
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
try:
cur_ini = __salt__['ini.get_ini'](name, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
for section in sections or []:
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if not cur_section:
if section not in cur_ini:
ret['comment'] += 'Section {0} does not exist.\n'.format(section)
continue
ret['comment'] += 'Deleted section {0}.\n'.format(section)
Expand Down
Loading

0 comments on commit 432cbbb

Please sign in to comment.