diff --git a/systemvm/patches/debian/config/opt/cloud/bin/line_edit.py b/systemvm/patches/debian/config/opt/cloud/bin/line_edit.py
new file mode 100755
index 00000000000..5918883ea96
--- /dev/null
+++ b/systemvm/patches/debian/config/opt/cloud/bin/line_edit.py
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import logging
+import re
+import shutil
+import os
+
+
+class LineEdit(object):
+ """Helper for LineEditingFile that keeps track of one edit."""
+ def __init__(self, search, sub, *sub_args, **kwargs):
+ if len(sub_args) > 0:
+ sub = sub % sub_args
+ flags = kwargs.get('flags', 0)
+ self.pattern = re.compile(search, flags=flags)
+ self.sub = sub
+ self.count = kwargs.get('count', 0) # max subs to make
+ self.subs = 0 # subs made so far
+
+
+class LineEditingFile(object):
+ """
+ Atomic, conservative, by-line editing of configuration files.
+
+ Will not touch the file if there are no changes to do.
+ Reasonably efficient for large files, though files with a long time
+ before their first match will use memory.
+
+
+ Given a vhosts file such as:
+ >>> with open('doctest-vhosts.conf', 'w') as f:
+ ... f.write('''
+ ... Listen foo:80
+ ...
+ ... DocRoot /var/www
+ ...
+ ...
+ ... Listen other:80
+ ...
+ ... DocRoot /var/www
+ ...
+ ... ''')
+ ...
+
+ To replace the hostname for the first virtualhost entry:
+ >>> new_hostname = 'fooooo'
+ >>> with LineEditingFile('doctest-vhosts.conf') as f:
+ ... f.replace(r'', '', new_hostname, count=1, flags=re.I)
+ ... f.replace(r'Listen .*?:80', 'Listen %s:80', new_hostname, count=1, flags=re.I)
+ ...
+
+ Be careful with the matches!
+ A second invocation of the same rule will edit the second vhost:
+ >>> new_hostname = 'fooooo'
+ >>> with LineEditingFile('doctest-vhosts.conf') as f:
+ ... f.replace(r'', '', new_hostname, count=1, flags=re.I)
+ ...
+
+ To move all hosts from port 80 to port 8080:
+ >>> with LineEditingFile('doctest-vhosts.conf') as f:
+ ... f.replace(r'', '', flags=re.I)
+ ... f.replace(r'Listen (.*?):80', 'Listen \\\\1:80', flags=re.I)
+ ...
+
+ (please note in this example there's a double escape of the backreference
+ \\\\1, to make the example work with doctest)
+
+ Since this example already matched all files, a second invocation does nothing:
+ >>> with LineEditingFile('doctest-vhosts.conf') as f:
+ ... f.replace(r'', '', flags=re.I)
+ ...
+
+ It's also acceptable to not make any edits at all:
+ >>> with LineEditingFile('doctest-vhosts.conf') as f:
+ ... pass
+ ...
+
+ You don't _have_ to use a with statement:
+ >>> f = LineEditingFile('doctest-vhosts.conf')
+ >>> f.replace(r'DocRoot /var/www', 'DocRoot /var/www/html', flags=re.I)
+ >>> changes = f.commit()
+ >>> print changes
+ 2
+ >>>
+
+ Cleanup of the example vhosts.conf:
+ >>> # noinspection PyBroadException
+ >>> try:
+ ... os.unlink('doctest-vhosts.conf')
+ ... os.unlink('doctest-vhosts.conf.bak')
+ ... os.unlink('doctest-vhosts.conf.new')
+ ... except:
+ ... pass
+ ...
+ """
+
+ def __init__(self, filename):
+ self.filename = filename
+ self.changed = False
+ self.edits = []
+
+ def __enter__(self):
+ return self
+
+ def replace(self, search, sub, *sub_args, **kwargs):
+ edit = LineEdit(search, sub, *sub_args, **kwargs)
+ self.edits.append(edit)
+
+ # noinspection PyUnusedLocal
+ def __exit__(self, exc, value, traceback):
+ if exc is not None:
+ return False # return false results in re-raise
+
+ self.commit()
+
+ def commit(self):
+ changes = 0
+ changed_file = None
+ changed_filename = self.filename + '.new'
+ try:
+ lines = []
+ backup_filename = self.filename + '.bak'
+ # noinspection PyUnusedLocal
+ stat = None
+ with open(self.filename, 'r') as orig:
+ stat = os.fstat(orig.fileno())
+ for line in orig:
+ changed_line = line
+ for edit in self.edits:
+ remaining_count = 0
+ if edit.count != 0:
+ remaining_count = edit.count - edit.subs
+ if remaining_count < 0:
+ raise Exception("Made too many edits")
+ elif remaining_count == 0:
+ continue
+ changed_line, subs = edit.pattern.subn(
+ edit.sub, line, remaining_count)
+ if changed_line != line:
+ if changed_file is None:
+ logging.debug("Editing file %s" % self.filename)
+ logging.debug(" - %s" % line[:-1])
+ logging.debug(" + %s" % changed_line[:-1])
+ changes += subs
+ edit.subs += subs
+ if changes == 0: # buffer until we find a change
+ lines.append(changed_line)
+ elif changed_file is None: # found first change, flush buffer
+ changed_file = open(changed_filename, 'w')
+ if hasattr(os, 'fchmod'):
+ os.fchmod(changed_file.fileno(), # can cause OSError which aborts
+ stat.st_mode)
+ if hasattr(os, 'fchown'):
+ os.fchown(changed_file.fileno(), # can cause OSError which aborts
+ stat.st_uid, stat.st_gid)
+ changed_file.writelines(lines)
+ changed_file.write(changed_line)
+ del lines # reclaim buffer memory
+ else: # already flushed, just write
+ changed_file.write(changed_line)
+
+ if changes == 0:
+ logging.info("No edits need for file %s" %
+ self.filename)
+ else:
+ changed_file.close()
+ changed_file = None
+ if os.path.exists(backup_filename): # back up the original
+ os.unlink(backup_filename)
+ shutil.copy(self.filename, backup_filename)
+ os.rename(changed_filename, self.filename) # the swap
+ logging.info("Edited file %s (%d changes)" %
+ (self.filename, changes))
+ finally:
+ if changed_file is not None: # failed, clean up
+ changed_file.close()
+ os.unlink(changed_filename)
+ return changes
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG)
+ import doctest
+ doctest.testmod()