summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2012-02-01 14:20:53 +0200
committerRob Crittenden <rcritten@redhat.com>2012-02-08 20:10:59 -0500
commit9078efeb4034dac3dd6f27741d6c3adb43d526a5 (patch)
tree384d1d1c5fcba6d6a8c273608748bb91d0005833
parent4c04cfb39cd02951fa260d16926acd2c71fa1024 (diff)
downloadfreeipa.git-9078efeb4034dac3dd6f27741d6c3adb43d526a5.tar.gz
freeipa.git-9078efeb4034dac3dd6f27741d6c3adb43d526a5.tar.xz
freeipa.git-9078efeb4034dac3dd6f27741d6c3adb43d526a5.zip
Add management of inifiles to allow manipulation of systemd units
inifile_replace_variables() works similar to config_replace_variables() but allows to apply changes to specific section of an inifile. Inifiles are commonly used by freedesktop.org software and particularly used by systemd. When modifying inifile, all changes will be applied to specific section. Also fixes corner case in config_replace_variables() which would dublicate variables when adding them.
-rw-r--r--ipapython/ipautil.py100
1 files changed, 99 insertions, 1 deletions
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 44580be8..fc0010d6 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1259,7 +1259,7 @@ $)''', re.VERBOSE)
new_vars = replacevars.copy()
new_vars.update(appendvars)
newvars_view = set(new_vars.keys()) - set(old_values.keys())
- append_view = (set(appendvars.keys()) - set(replacevars.keys())) - set(old_values.keys())
+ append_view = (set(appendvars.keys()) - newvars_view)
for item in newvars_view:
new_config.write("%s=%s\n" % (item,new_vars[item]))
for item in append_view:
@@ -1276,6 +1276,104 @@ $)''', re.VERBOSE)
return old_values
+def inifile_replace_variables(filepath, section, replacevars=dict(), appendvars=dict()):
+ """
+ Take a section-structured key=value based configuration file, and write new version
+ with certain values replaced or appended within the section
+
+ All (key,value) pairs from replacevars and appendvars that were not found
+ in the configuration file, will be added there.
+
+ It is responsibility of a caller to ensure that replacevars and
+ appendvars do not overlap.
+
+ It is responsibility of a caller to back up file.
+
+ returns dictionary of affected keys and their previous values
+
+ One have to run restore_context(filepath) afterwards or
+ security context of the file will not be correct after modification
+ """
+ pattern = re.compile('''
+(^
+ \[
+ (?P<section> .+) \]
+ (\s+((\#|;).*)?)?
+$)|(^
+ \s*
+ (?P<option> [^\#;]+?)
+ (\s*=\s*)
+ (?P<value> .+?)?
+ (\s*((\#|;).*)?)?
+$)''', re.VERBOSE)
+ def add_options(config, replacevars, appendvars, oldvars):
+ # add all options from replacevars and appendvars that were not found in the file
+ new_vars = replacevars.copy()
+ new_vars.update(appendvars)
+ newvars_view = set(new_vars.keys()) - set(oldvars.keys())
+ append_view = (set(appendvars.keys()) - newvars_view)
+ for item in newvars_view:
+ config.write("%s=%s\n" % (item,new_vars[item]))
+ for item in append_view:
+ config.write("%s=%s\n" % (item,appendvars[item]))
+
+ orig_stat = os.stat(filepath)
+ old_values = dict()
+ temp_filename = None
+ with tempfile.NamedTemporaryFile(delete=False) as new_config:
+ temp_filename = new_config.name
+ with open(filepath, 'r') as f:
+ in_section = False
+ finished = False
+ line_idx = 1
+ for line in f:
+ line_idx = line_idx + 1
+ new_line = line
+ m = pattern.match(line)
+ if m:
+ sect, option, value = m.group('section', 'option', 'value')
+ if in_section and sect is not None:
+ # End of the searched section, add remaining options
+ add_options(new_config, replacevars, appendvars, old_values)
+ finished = True
+ if sect is not None:
+ # New section is found, check whether it is the one we are looking for
+ in_section = (str(sect).lower() == str(section).lower())
+ if option is not None and in_section:
+ # Great, this is an option from the section we are loking for
+ if replacevars and option in replacevars:
+ # replace value completely
+ new_line = u"%s=%s\n" % (option, replacevars[option])
+ old_values[option] = value
+ if appendvars and option in appendvars:
+ # append a new value unless it is already existing in the original one
+ if not value:
+ new_line = u"%s=%s\n" % (option, appendvars[option])
+ elif value.find(appendvars[option]) == -1:
+ new_line = u"%s=%s %s\n" % (option, value, appendvars[option])
+ old_values[option] = value
+ new_config.write(new_line)
+ # We have finished parsing the original file.
+ # There are two remaining cases:
+ # 1. Section we were looking for was not found, we need to add it.
+ if not (in_section or finished):
+ new_config.write("[%s]\n" % (section))
+ # 2. The section is the last one but some options were not found, add them.
+ if in_section or not finished:
+ add_options(new_config, replacevars, appendvars, old_values)
+
+ new_config.flush()
+ # Make sure the resulting file is readable by others before installing it
+ os.fchmod(new_config.fileno(), orig_stat.st_mode)
+ os.fchown(new_config.fileno(), orig_stat.st_uid, orig_stat.st_gid)
+
+ # At this point new_config is closed but not removed due to 'delete=False' above
+ # Now, install the temporary file as configuration and ensure old version is available as .orig
+ # While .orig file is not used during uninstall, it is left there for administrator.
+ install_file(temp_filename, filepath)
+
+ return old_values
+
def backup_config_and_replace_variables(fstore, filepath, replacevars=dict(), appendvars=dict()):
"""
Take a key=value based configuration file, back up it, and