summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipapython/ipautil.py100
1 files changed, 99 insertions, 1 deletions
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 44580be8e..fc0010d6e 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