File: //opt/saltstack/salt/lib/python3.10/site-packages/salt/states/ini_manage.py
"""
Manage ini files
================
:maintainer: <akilesh1597@gmail.com>
:maturity: new
:depends: re
:platform: all
"""
from collections import OrderedDict
__virtualname__ = "ini"
def __virtual__():
"""
Only load if the ini module is available
"""
return __virtualname__ if "ini.set_option" in __salt__ else False
def options_present(
name, sections=None, separator="=", strict=False, encoding=None, no_spaces=False
):
"""
Set or create a key/value pair in an ``ini`` file. Options present in the
ini file and not specified in the sections dict will be untouched, unless
the ``strict: True`` flag is used.
Sections that do not exist will be created.
Args:
name (str):
The path to the ini file
sections (dict):
A dictionary of sections and key/value pairs that will be used to
update the ini file. Other sections and key/value pairs in the ini
file will be untouched unless ``strict: True`` is passed.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
strict (bool):
A boolean value that specifies that the ``sections`` dictionary
contains all settings in the ini file. ``True`` will create an ini
file with only the values specified in ``sections``. ``False`` will
append or update values in an existing ini file and leave the rest
untouched.
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.10
no_spaces (bool):
A bool value that specifies that the key/value separator will be
wrapped with spaces. This parameter was added to have the ability to
not wrap the separator with spaces. Default is ``False``, which
maintains backwards compatibility.
.. warning::
This will affect all key/value pairs in the ini file, not just
the specific value being set.
.. versionadded:: 3006.10
Returns:
dict: A dictionary containing list of changes made
Example:
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.options_present:
- separator: '='
- strict: True
- sections:
test:
testkey: 'testval'
secondoption: 'secondvalue'
test1:
testkey1: 'testval121'
"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": "No anomaly detected",
}
if __opts__["test"]:
ret["comment"] = ""
# pylint: disable=too-many-nested-blocks
try:
changes = {}
if sections:
options = {}
for sname, sbody in sections.items():
if not isinstance(sbody, (dict, OrderedDict)):
options.update({sname: sbody})
cur_ini = __salt__["ini.get_ini"](
file_name=name, separator=separator, encoding=encoding
)
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 str(original_top_level_opts[option]) == str(options[option]):
ret["comment"] += f"Unchanged key {option}.\n"
else:
ret["comment"] += f"Changed key {option}.\n"
ret["result"] = None
else:
ret["comment"] += f"Changed key {option}.\n"
ret["result"] = None
else:
options_updated = __salt__["ini.set_option"](
file_name=name,
sections=options,
separator=separator,
encoding=encoding,
no_spaces=no_spaces,
)
changes.update(options_updated)
if strict:
for opt_to_remove in set(original_top_level_opts).difference(options):
if __opts__["test"]:
ret["comment"] += f"Removed key {opt_to_remove}.\n"
ret["result"] = None
else:
__salt__["ini.remove_option"](
file_name=name,
section=None,
option=opt_to_remove,
separator=separator,
encoding=encoding,
)
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 = cur_ini.get(section_name, {})
for key_to_remove in set(original.keys()).difference(
section_body.keys()
):
orig_value = original_sections.get(section_name, {}).get(
key_to_remove, "#-#-"
)
if __opts__["test"]:
ret["comment"] += "Deleted key {}{}.\n".format(
key_to_remove, section_descr
)
ret["result"] = None
else:
__salt__["ini.remove_option"](
file_name=name,
section=section_name,
option=key_to_remove,
separator=separator,
encoding=encoding,
)
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 str(section_body[option]) == str(
original_sections.get(section_name, {}).get(option, "#-#-")
):
ret["comment"] += "Unchanged key {}{}.\n".format(
option, section_descr
)
else:
ret["comment"] += "Changed key {}{}.\n".format(
option, section_descr
)
ret["result"] = None
else:
options_updated = __salt__["ini.set_option"](
file_name=name,
sections={section_name: section_body},
separator=separator,
encoding=encoding,
no_spaces=no_spaces,
)
if options_updated:
changes[section_name].update(options_updated[section_name])
if not changes[section_name]:
del changes[section_name]
else:
if not __opts__["test"]:
changes = __salt__["ini.set_option"](
file_name=name,
sections=sections,
separator=separator,
encoding=encoding,
no_spaces=no_spaces,
)
except (OSError, KeyError) as err:
ret["comment"] = f"{err}"
ret["result"] = False
return ret
if "error" in changes:
ret["result"] = False
ret["comment"] = "Errors encountered. {}".format(changes["error"])
ret["changes"] = {}
else:
for ciname, body in changes.items():
if body:
ret["comment"] = "Changes take effect"
ret["changes"].update({ciname: changes[ciname]})
return ret
def options_absent(name, sections=None, separator="=", encoding=None):
"""
Remove a key/value pair from an ini file. Key/value pairs present in the ini
file and not specified in sections dict will be untouched.
Args:
name (str):
The path to the ini file
sections (dict):
A dictionary of sections and key/value pairs that will be removed
from the ini file. Other key/value pairs in the ini file will be
untouched.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.10
Returns:
dict: A dictionary containing list of changes made
Example:
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.options_absent:
- separator: '='
- sections:
test:
- testkey
- secondoption
test1:
- testkey1
"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": "No anomaly detected",
}
if __opts__["test"]:
ret["result"] = True
ret["comment"] = ""
for section in sections or {}:
section_name = " in section " + section if section else ""
try:
cur_section = __salt__["ini.get_section"](
file_name=name,
section=section,
separator=separator,
encoding=encoding,
)
except OSError as err:
ret["comment"] = f"{err}"
ret["result"] = False
return ret
except AttributeError:
cur_section = section
if isinstance(sections[section], list):
for key in sections[section]:
cur_value = cur_section.get(key)
if not cur_value:
ret["comment"] += "Key {}{} does not exist.\n".format(
key, section_name
)
continue
ret["comment"] += f"Deleted key {key}{section_name}.\n"
ret["result"] = None
else:
option = section
if not __salt__["ini.get_option"](
file_name=name,
section=None,
option=option,
separator=separator,
encoding=encoding,
):
ret["comment"] += f"Key {option} does not exist.\n"
continue
ret["comment"] += f"Deleted key {option}.\n"
ret["result"] = None
if ret["comment"] == "":
ret["comment"] = "No changes detected."
return ret
sections = sections or {}
for section, keys in sections.items():
for key in keys:
try:
current_value = __salt__["ini.remove_option"](
file_name=name,
section=section,
option=key,
separator=separator,
encoding=encoding,
)
except OSError as err:
ret["comment"] = f"{err}"
ret["result"] = False
return ret
if not current_value:
continue
if section not in ret["changes"]:
ret["changes"].update({section: {}})
ret["changes"][section].update({key: current_value})
if not isinstance(sections[section], list):
ret["changes"].update({section: current_value})
# break
ret["comment"] = "Changes take effect"
return ret
def sections_present(name, sections=None, separator="=", encoding=None):
"""
Add sections to an ini file. This will only create empty sections. To also
create key/value pairs, use options_present state.
Args:
name (str):
The path to the ini file
sections (dict):
A dictionary of sections and key/value pairs that will be used to
update the ini file. Only the sections portion is used, key/value
pairs are ignored. To also set key/value pairs, use the
options_present state.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.10
Returns:
dict: A dictionary containing list of changes made
Example:
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.sections_present:
- separator: '='
- sections:
- section_one
- section_two
"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": "No anomaly detected",
}
if __opts__["test"]:
ret["result"] = True
ret["comment"] = ""
try:
cur_ini = __salt__["ini.get_ini"](
file_name=name, separator=separator, encoding=encoding
)
except OSError as err:
ret["result"] = False
ret["comment"] = f"{err}"
return ret
for section in sections or {}:
if section in cur_ini:
ret["comment"] += f"Section unchanged {section}.\n"
continue
else:
ret["comment"] += f"Created new section {section}.\n"
ret["result"] = None
if ret["comment"] == "":
ret["comment"] = "No changes detected."
return ret
section_to_update = {}
for section_name in sections or []:
section_to_update.update({section_name: {}})
try:
changes = __salt__["ini.set_option"](
file_name=name,
section=section_to_update,
separator=separator,
encoding=encoding,
)
except OSError as err:
ret["result"] = False
ret["comment"] = f"{err}"
return ret
if "error" in changes:
ret["result"] = False
ret["changes"] = "Errors encountered {}".format(changes["error"])
return ret
ret["changes"] = changes
ret["comment"] = "Changes take effect"
return ret
def sections_absent(name, sections=None, separator="=", encoding=None):
"""
Remove sections from the ini file. All key/value pairs in the section will
also be removed.
Args:
name (str):
The path to the ini file
sections (dict):
A dictionary of sections and key/value pairs that will be used to
update the ini file. Other sections and key/value pairs in the ini
file will be untouched unless ``strict: True`` is passed.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
dict: A dictionary containing list of changes made
Example:
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.sections_absent:
- separator: '='
- sections:
- test
- test1
"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": "No anomaly detected",
}
if __opts__["test"]:
ret["result"] = True
ret["comment"] = ""
try:
cur_ini = __salt__["ini.get_ini"](
file_name=name, separator=separator, encoding=encoding
)
except OSError as err:
ret["result"] = False
ret["comment"] = f"{err}"
return ret
for section in sections or []:
if section not in cur_ini:
ret["comment"] += f"Section {section} does not exist.\n"
continue
ret["comment"] += f"Deleted section {section}.\n"
ret["result"] = None
if ret["comment"] == "":
ret["comment"] = "No changes detected."
return ret
for section in sections or []:
try:
cur_section = __salt__["ini.remove_section"](
file_name=name, section=section, separator=separator, encoding=encoding
)
except OSError as err:
ret["result"] = False
ret["comment"] = f"{err}"
return ret
if not cur_section:
continue
ret["changes"][section] = cur_section
ret["comment"] = "Changes take effect"
return ret