mooli tayer has uploaded a new change for review. Change subject: configfile utility for common config file editing operations. ......................................................................
configfile utility for common config file editing operations. Add utility to edit config files that is backward compatible with libvirt_configure.sh. Support for common operations used by vdsm tool. Context managed and commits file upon __exit__ by replacing original file with a temporary file. Change-Id: If8c7ae2b562650e403fc39024f3531d44b2b9c4f Signed-off-by: Mooli Tayer <[email protected]> --- A lib/vdsm/tool/configfile.py M tests/toolTests.py 2 files changed, 199 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/29/27129/1 diff --git a/lib/vdsm/tool/configfile.py b/lib/vdsm/tool/configfile.py new file mode 100644 index 0000000..c008def --- /dev/null +++ b/lib/vdsm/tool/configfile.py @@ -0,0 +1,132 @@ +# Copyright 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# + +import os +import tempfile +import re +import selinux + + +class ConfigFile(object): + + def __init__(self, filename, confstart=None, confend=None): + self.filename = filename + self.context = OUT + if confstart is None or confend is None: + raise RuntimeError("argument missing") + self.confstart = confstart + self.confend = confend + + def __enter__(self): + if self.context == IN: + raise RuntimeError("Internal error") + self.entries = {} + self.context = IN + self.prefix = None + self.section = None + self.oldmod = os.stat(self.filename).st_mode + self.remove = None + self.rmstate = BEFORE + return self + + def getOldContent(self): + confpat = re.compile(r'^\s*([^=\s]*)\s*=') + oldlines = [] + oldentries = set() + with open(self.filename, 'r') as f: + for line in f: + if self.remove: + if self.rmstate == BEFORE and\ + line.startswith(self.confstart): + self.rmstate = WITHIN + elif self.rmstate == WITHIN and\ + line.startswith(self.confend): + self.rmstate = AFTER + continue + if not self.remove or self.rmstate != WITHIN: + m = confpat.match(line.rstrip()) + if m: + oldentries.add(m.group(1)) + if self.prefix: + line = self.prefix + line + oldlines.append(line) + return oldlines, oldentries + + def __exit__(self, exec_ty, exec_val, tb): + + self.context = OUT + if exec_ty is None: + fd, tname = tempfile.mkstemp(dir=os.path.dirname(self.filename)) + oldlines, oldentries = self.getOldContent() + with os.fdopen(fd, 'w') as f: + if self.section: + f.write(self.confstart) + f.write(self.section) + f.write(self.confend) + f.writelines(oldlines) + if self.entries: + f.write(self.confstart) + for key, val in self.entries.iteritems(): + if key not in oldentries: + f.write("{k}={v}\n".format(k=key, v=val)) + f.write(self.confend) + os.rename(tname, self.filename) + if self.oldmod != os.stat(self.filename).st_mode: + os.chmod(self.filename, self.oldmod) + if selinux.is_selinux_enabled: + try: + selinux.restorecon(self.filename) + except OSError: + pass # No default label for file + + def addEntry(self, key, val): + """ + add key=value unless key is already in the file. + """ + if self.context == IN: + self.entries[key] = val + else: + raise RuntimeError("Internal error") + + def prependSection(self, section): + """ + add 'section' in the beginning of the file. + section is wrapped with confstart and confend. + Only one section is currently allowed. + """ + if self.context == IN: + self.section = section + else: + raise RuntimeError("Internal error") + + def prefixLines(self, prefix): + """ + prefix each line originaly included in the file (not including + 'prependedSection' lines) with 'prefix'. + """ + if self.context == IN: + self.prefix = prefix + else: + raise RuntimeError("Internal error") + + def removeConf(self): + if self.context == IN: + self.remove = True + else: + raise RuntimeError("Internal error") diff --git a/tests/toolTests.py b/tests/toolTests.py index 2fc8c61..1ce5a27 100644 --- a/tests/toolTests.py +++ b/tests/toolTests.py @@ -18,6 +18,7 @@ # Refer to the README and COPYING files for full details of the license # from vdsm.tool import configurator +from vdsm.tool.configfile import ConfigFile from vdsm import utils import monkeypatch from unittest import TestCase @@ -174,3 +175,69 @@ self.assertFalse(libvirtConfigure.isconfigured()) libvirtConfigure.configure() self.assertTrue(libvirtConfigure.isconfigured()) + + +class ConfigFileTests(TestCase): + def setUp(self): + fd, self.tname = tempfile.mkstemp() + + def tearDown(self): + os.remove(self.tname) + + # helper function + def writeConf(self, text): + with open(self.tname, 'w') as f: + f.write(text) + + def testAddExistingConf(self): + self.writeConf("key1=val1\n" + " key2 =val2\n") + conf_file = ConfigFile(self.tname, + confstart="# start conf-3.4.4\n", + confend="# end conf-3.4.4\n") + with conf_file as conf: + conf.addEntry("key3", "val3") + conf.addEntry("key2", "val3") + with open(self.tname, 'r') as f: + + self.assertEqual(f.read(), "key1=val1\n" + " key2 =val2\n" + "# start conf-3.4.4\n" + "key3=val3\n" + "# end conf-3.4.4\n") + + def testPrefixAndPrepend(self): + self.writeConf("/var/log/libvirt/libvirtd.log {\n" + " weekly\n" + "}\n") + conf_file = ConfigFile(self.tname, + confstart='# start conf-3.4.4\n', + confend="# end conf-3.4.4\n") + with conf_file as conf: + conf.prefixLines("# comment ") + conf.prependSection("Some text to\n" + "add at the top\n") + with open(self.tname, 'r') as f: + self.assertEqual(f.read(), + "# start conf-3.4.4\n" + "Some text to\n" + "add at the top\n" + "# end conf-3.4.4\n" + "# comment /var/log/libvirt/libvirtd.log {\n" + "# comment weekly\n" + "# comment }\n") + + def testRemoveConfSection(self): + self.writeConf("key=val\n" + "kay=val\n" + "# start conf-text-here don't matter\n" + "all you sections are belong to us\n" + "# end conf-text-here don't matter\n") + conf_file = ConfigFile(self.tname, + confstart="# start conf", + confend="# end conf") + with conf_file as conf: + conf.removeConf() + with open(self.tname, 'r') as f: + self.assertEqual(f.read(), "key=val\n" + "kay=val\n") -- To view, visit http://gerrit.ovirt.org/27129 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If8c7ae2b562650e403fc39024f3531d44b2b9c4f Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: mooli tayer <[email protected]> _______________________________________________ vdsm-patches mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches
