From 025ae07850375cbf981ed335a4ddf702d54be6e3 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 14 Aug 2014 13:06:26 -0700 Subject: build: make GLUSTERD_WORKDIR rely on localstatedir Backport from master branch - http://review.gluster.org/#/c/8246/ - Break-way from '/var/lib/glusterd' hard-coded previously, instead rely on 'configure' value from 'localstatedir' - Provide 's/lib/db' as default working directory for gluster management daemon for BSD and Darwin based installations - loff_t is really off_t on Darwin - fix-off the warnings generated by clang on FreeBSD/Darwin - Now 'tests/*' use GLUSTERD_WORKDIR a common variable for all platforms. - Define proper environment for running tests, define correct PATH and LD_LIBRARY_PATH when running tests, so that the desired version of glusterfs is used, regardless where it is installed. (Thanks to manu@netbsd.org for this additional work) Change-Id: I06e684ac4c26d1e74c9daf76753403ad15f79276 BUG: 1130308 Signed-off-by: Harshavardhana Reviewed-on: http://review.gluster.org/8486 Tested-by: Gluster Build System --- geo-replication/src/Makefile.am | 4 +- geo-replication/src/peer_add_secret_pub.in | 2 +- geo-replication/src/peer_gsec_create.in | 16 +- geo-replication/src/set_geo_rep_pem_keys.sh | 4 +- geo-replication/syncdaemon/configinterface.py | 356 ----------------------- geo-replication/syncdaemon/configinterface.py.in | 352 ++++++++++++++++++++++ 6 files changed, 364 insertions(+), 370 deletions(-) delete mode 100644 geo-replication/syncdaemon/configinterface.py create mode 100644 geo-replication/syncdaemon/configinterface.py.in (limited to 'geo-replication') diff --git a/geo-replication/src/Makefile.am b/geo-replication/src/Makefile.am index 20b5b6bde6b..68b18c66e88 100644 --- a/geo-replication/src/Makefile.am +++ b/geo-replication/src/Makefile.am @@ -1,4 +1,3 @@ - gsyncddir = $(libexecdir)/glusterfs gsyncd_SCRIPTS = gverify.sh peer_add_secret_pub peer_gsec_create set_geo_rep_pem_keys.sh @@ -11,8 +10,7 @@ gsyncd_PROGRAMS = gsyncd gsyncd_SOURCES = gsyncd.c procdiggy.c -gsyncd_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \ - $(GF_GLUSTERFS_LIBS) +gsyncd_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la gsyncd_LDFLAGS = $(GF_LDFLAGS) diff --git a/geo-replication/src/peer_add_secret_pub.in b/geo-replication/src/peer_add_secret_pub.in index 122e577b446..97011f204d2 100644 --- a/geo-replication/src/peer_add_secret_pub.in +++ b/geo-replication/src/peer_add_secret_pub.in @@ -30,4 +30,4 @@ if [ ! -d $home_dir/.ssh/authorized_keys ]; then chown $user: $home_dir/.ssh/authorized_keys; fi -cat "$GLUSTERD_WORKING_DIR"/geo-replication/common_secret.pem.pub >> $home_dir/.ssh/authorized_keys; +cat "$GLUSTERD_WORKDIR"/geo-replication/common_secret.pem.pub >> $home_dir/.ssh/authorized_keys; diff --git a/geo-replication/src/peer_gsec_create.in b/geo-replication/src/peer_gsec_create.in index a39fdbfb5f7..9cadce56453 100755 --- a/geo-replication/src/peer_gsec_create.in +++ b/geo-replication/src/peer_gsec_create.in @@ -3,16 +3,16 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ -if [ ! -f "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem.pub ]; then - \rm -rf "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem* - ssh-keygen -N '' -f "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem > /dev/null +if [ ! -f "$GLUSTERD_WORKDIR"/geo-replication/secret.pem.pub ]; then + \rm -rf "$GLUSTERD_WORKDIR"/geo-replication/secret.pem* + ssh-keygen -N '' -f "$GLUSTERD_WORKDIR"/geo-replication/secret.pem > /dev/null fi -if [ ! -f "$GLUSTERD_WORKING_DIR"/geo-replication/tar_ssh.pem.pub ]; then - \rm -rf "$GLUSTERD_WORKING_DIR"/geo-replication/tar_ssh.pem* - ssh-keygen -N '' -f "$GLUSTERD_WORKING_DIR"/geo-replication/tar_ssh.pem > /dev/null +if [ ! -f "$GLUSTERD_WORKDIR"/geo-replication/tar_ssh.pem.pub ]; then + \rm -rf "$GLUSTERD_WORKDIR"/geo-replication/tar_ssh.pem* + ssh-keygen -N '' -f "$GLUSTERD_WORKDIR"/geo-replication/tar_ssh.pem > /dev/null fi -output1=`echo command=\"${exec_prefix}/libexec/glusterfs/gsyncd\" " "``cat "$GLUSTERD_WORKING_DIR"/geo-replication/secret.pem.pub` -output2=`echo command=\"tar \$\{SSH_ORIGINAL_COMMAND#* \}\" " "``cat "$GLUSTERD_WORKING_DIR"/geo-replication/tar_ssh.pem.pub` +output1=`echo command=\"${exec_prefix}/libexec/glusterfs/gsyncd\" " "``cat "$GLUSTERD_WORKDIR"/geo-replication/secret.pem.pub` +output2=`echo command=\"tar \$\{SSH_ORIGINAL_COMMAND#* \}\" " "``cat "$GLUSTERD_WORKDIR"/geo-replication/tar_ssh.pem.pub` echo -e "$output1\n$output2" diff --git a/geo-replication/src/set_geo_rep_pem_keys.sh b/geo-replication/src/set_geo_rep_pem_keys.sh index 7a7bcad25c2..7b825693fad 100755 --- a/geo-replication/src/set_geo_rep_pem_keys.sh +++ b/geo-replication/src/set_geo_rep_pem_keys.sh @@ -1,7 +1,7 @@ #!/bin/bash # Script to copy the pem keys from the user's home directory -# to $GLUSTERD_WORKING_DIR/geo-replication/ and then copy +# to $GLUSTERD_WORKDIR/geo-replication and then copy # the keys to other nodes in the cluster and add them to the # respective authorized keys. The script takes as argument the # user name and assumes that the user will be present in all @@ -28,7 +28,7 @@ function main() fi if [ -f $home_dir/common_secret.pem.pub ]; then - cp $home_dir/common_secret.pem.pub /var/lib/glusterd/geo-replication/ + cp $home_dir/common_secret.pem.pub ${GLUSTERD_WORKDIR}/geo-replication/ gluster system:: copy file /geo-replication/common_secret.pem.pub gluster system:: execute add_secret_pub $user else diff --git a/geo-replication/syncdaemon/configinterface.py b/geo-replication/syncdaemon/configinterface.py deleted file mode 100644 index a94e07f0d4b..00000000000 --- a/geo-replication/syncdaemon/configinterface.py +++ /dev/null @@ -1,356 +0,0 @@ -# -# Copyright (c) 2011-2014 Red Hat, Inc. -# This file is part of GlusterFS. - -# This file is licensed to you under your choice of the GNU Lesser -# General Public License, version 3 or any later version (LGPLv3 or -# later), or the GNU General Public License, version 2 (GPLv2), in all -# cases as published by the Free Software Foundation. -# - -try: - import ConfigParser -except ImportError: - # py 3 - import configparser as ConfigParser -import re -from string import Template -import os -import errno -import sys -from stat import ST_DEV, ST_INO, ST_MTIME -import tempfile -import shutil - -from syncdutils import escape, unescape, norm, update_file, GsyncdError - -SECT_ORD = '__section_order__' -SECT_META = '__meta__' -config_version = 2.0 - -re_type = type(re.compile('')) - - -# (SECTION, OPTION, OLD VALUE, NEW VALUE) -CONFIGS = ( - ("peersrx . .", - "georep_session_working_dir", - "", - "/var/lib/glusterd/geo-replication/${mastervol}_${remotehost}_" - "${slavevol}/"), - ("peersrx .", - "gluster_params", - "aux-gfid-mount xlator-option=\*-dht.assert-no-child-down=true", - "aux-gfid-mount"), - ("peersrx . .", - "ssh_command_tar", - "", - "ssh -oPasswordAuthentication=no -oStrictHostKeyChecking=no " - "-i /var/lib/glusterd/geo-replication/tar_ssh.pem"), - ("peersrx . .", - "changelog_log_file", - "", - "${iprefix}/log/glusterfs/geo-replication/${mastervol}" - "/${eSlave}${local_id}-changes.log"), - ("peersrx . .", - "working_dir", - "/var/run/gluster/${mastervol}/${eSlave}", - "${iprefix}/lib/misc/glusterfsd/${mastervol}/${eSlave}"), - ("peersrx . .", - "working_dir", - "/usr/local/var/run/gluster/${mastervol}/${eSlave}", - "${iprefix}/lib/misc/glusterfsd/${mastervol}/${eSlave}"), -) - - -def upgrade_config_file(path): - config_change = False - config = ConfigParser.RawConfigParser() - config.read(path) - - for sec, opt, oldval, newval in CONFIGS: - try: - val = config.get(sec, opt) - except ConfigParser.NoOptionError: - # if new config opt not exists - config_change = True - config.set(sec, opt, newval) - continue - except ConfigParser.Error: - """ - When gsyncd invoked at the time of create, config file - will not be their. Ignore any ConfigParser errors - """ - continue - - if val == newval: - # value is same as new val - continue - - if val == oldval: - # config value needs update - config_change = True - config.set(sec, opt, newval) - - if config_change: - tempConfigFile = tempfile.NamedTemporaryFile(mode="wb", delete=False) - with open(tempConfigFile.name, 'wb') as configFile: - config.write(configFile) - - # If src and dst are two different file system, then os.rename - # fails, In this case if temp file created in /tmp and if /tmp is - # seperate fs then os.rename gives following error, so use shutil - # OSError: [Errno 18] Invalid cross-device link - # mail.python.org/pipermail/python-list/2005-February/342893.html - shutil.move(tempConfigFile.name, path) - - -class MultiDict(object): - - """a virtual dict-like class which functions as the union - of underlying dicts""" - - def __init__(self, *dd): - self.dicts = dd - - def __getitem__(self, key): - val = None - for d in self.dicts: - if d.get(key) is not None: - val = d[key] - if val is None: - raise KeyError(key) - return val - - -class GConffile(object): - - """A high-level interface to ConfigParser which flattens the two-tiered - config layout by implenting automatic section dispatch based on initial - parameters. - - Also ensure section ordering in terms of their time of addition -- a compat - hack for Python < 2.7. - """ - - def _normconfig(self): - """normalize config keys by s/-/_/g""" - for n, s in self.config._sections.items(): - if n.find('__') == 0: - continue - s2 = type(s)() - for k, v in s.items(): - if k.find('__') != 0: - k = norm(k) - s2[k] = v - self.config._sections[n] = s2 - - def __init__(self, path, peers, *dd): - """ - - .path: location of config file - - .config: underlying ConfigParser instance - - .peers: on behalf of whom we flatten .config - (master, or master-slave url pair) - - .auxdicts: template subtituents - """ - self.peers = peers - self.path = path - self.auxdicts = dd - self.config = ConfigParser.RawConfigParser() - self.config.read(path) - self.dev, self.ino, self.mtime = -1, -1, -1 - self._normconfig() - - def _load(self): - try: - sres = os.stat(self.path) - self.dev = sres[ST_DEV] - self.ino = sres[ST_INO] - self.mtime = sres[ST_MTIME] - except (OSError, IOError): - if sys.exc_info()[1].errno == errno.ENOENT: - sres = None - - self.config = ConfigParser.RawConfigParser() - self.config.read(self.path) - self._normconfig() - - def get_realtime(self, opt): - try: - sres = os.stat(self.path) - except (OSError, IOError): - if sys.exc_info()[1].errno == errno.ENOENT: - sres = None - else: - raise - - # compare file system stat with that of our stream file handle - if not sres or sres[ST_DEV] != self.dev or \ - sres[ST_INO] != self.ino or self.mtime != sres[ST_MTIME]: - self._load() - - return self.get(opt, printValue=False) - - def section(self, rx=False): - """get the section name of the section representing .peers - in .config""" - peers = self.peers - if not peers: - peers = ['.', '.'] - rx = True - if rx: - st = 'peersrx' - else: - st = 'peers' - return ' '.join([st] + [escape(u) for u in peers]) - - @staticmethod - def parse_section(section): - """retrieve peers sequence encoded by section name - (as urls or regexen, depending on section type) - """ - sl = section.split() - st = sl.pop(0) - sl = [unescape(u) for u in sl] - if st == 'peersrx': - sl = [re.compile(u) for u in sl] - return sl - - def ord_sections(self): - """Return an ordered list of sections. - - Ordering happens based on the auxiliary - SECT_ORD section storing indices for each - section added through the config API. - - To not to go corrupt in case of manually - written config files, we take care to append - also those sections which are not registered - in SECT_ORD. - - Needed for python 2.{4,5,6} where ConfigParser - cannot yet order sections/options internally. - """ - so = {} - if self.config.has_section(SECT_ORD): - so = self.config._sections[SECT_ORD] - so2 = {} - for k, v in so.items(): - if k != '__name__': - so2[k] = int(v) - tv = 0 - if so2: - tv = max(so2.values()) + 1 - ss = [s for s in self.config.sections() if s.find('__') != 0] - for s in ss: - if s in so.keys(): - continue - so2[s] = tv - tv += 1 - - def scmp(x, y): - return cmp(*(so2[s] for s in (x, y))) - ss.sort(scmp) - return ss - - def update_to(self, dct, allow_unresolved=False): - """update @dct from key/values of ours. - - key/values are collected from .config by filtering the regexp sections - according to match, and from .section. The values are treated as - templates, which are substituted from .auxdicts and (in case of regexp - sections) match groups. - """ - if not self.peers: - raise GsyncdError('no peers given, cannot select matching options') - - def update_from_sect(sect, mud): - for k, v in self.config._sections[sect].items(): - if k == '__name__': - continue - if allow_unresolved: - dct[k] = Template(v).safe_substitute(mud) - else: - dct[k] = Template(v).substitute(mud) - for sect in self.ord_sections(): - sp = self.parse_section(sect) - if isinstance(sp[0], re_type) and len(sp) == len(self.peers): - match = True - mad = {} - for i in range(len(sp)): - m = sp[i].search(self.peers[i]) - if not m: - match = False - break - for j in range(len(m.groups())): - mad['match%d_%d' % (i + 1, j + 1)] = m.groups()[j] - if match: - update_from_sect(sect, MultiDict(dct, mad, *self.auxdicts)) - if self.config.has_section(self.section()): - update_from_sect(self.section(), MultiDict(dct, *self.auxdicts)) - - def get(self, opt=None, printValue=True): - """print the matching key/value pairs from .config, - or if @opt given, the value for @opt (according to the - logic described in .update_to) - """ - d = {} - self.update_to(d, allow_unresolved=True) - if opt: - opt = norm(opt) - v = d.get(opt) - if v: - if printValue: - print(v) - else: - return v - else: - for k, v in d.iteritems(): - if k == '__name__': - continue - print("%s: %s" % (k, v)) - - def write(self, trfn, opt, *a, **kw): - """update on-disk config transactionally - - @trfn is the transaction function - """ - def mergeconf(f): - self.config = ConfigParser.RawConfigParser() - self.config.readfp(f) - self._normconfig() - if not self.config.has_section(SECT_META): - self.config.add_section(SECT_META) - self.config.set(SECT_META, 'version', config_version) - return trfn(norm(opt), *a, **kw) - - def updateconf(f): - self.config.write(f) - update_file(self.path, updateconf, mergeconf) - - def _set(self, opt, val, rx=False): - """set @opt to @val in .section""" - sect = self.section(rx) - if not self.config.has_section(sect): - self.config.add_section(sect) - # regarding SECT_ORD, cf. ord_sections - if not self.config.has_section(SECT_ORD): - self.config.add_section(SECT_ORD) - self.config.set( - SECT_ORD, sect, len(self.config._sections[SECT_ORD])) - self.config.set(sect, opt, val) - return True - - def set(self, opt, *a, **kw): - """perform ._set transactionally""" - self.write(self._set, opt, *a, **kw) - - def _delete(self, opt, rx=False): - """delete @opt from .section""" - sect = self.section(rx) - if self.config.has_section(sect): - return self.config.remove_option(sect, opt) - - def delete(self, opt, *a, **kw): - """perform ._delete transactionally""" - self.write(self._delete, opt, *a, **kw) diff --git a/geo-replication/syncdaemon/configinterface.py.in b/geo-replication/syncdaemon/configinterface.py.in new file mode 100644 index 00000000000..acb51486a10 --- /dev/null +++ b/geo-replication/syncdaemon/configinterface.py.in @@ -0,0 +1,352 @@ +# +# Copyright (c) 2011-2014 Red Hat, Inc. +# This file is part of GlusterFS. + +# This file is licensed to you under your choice of the GNU Lesser +# General Public License, version 3 or any later version (LGPLv3 or +# later), or the GNU General Public License, version 2 (GPLv2), in all +# cases as published by the Free Software Foundation. +# + +try: + import ConfigParser +except ImportError: + # py 3 + import configparser as ConfigParser +import re +from string import Template +import os +import errno +import sys +from stat import ST_DEV, ST_INO, ST_MTIME +import tempfile +import shutil + +from syncdutils import escape, unescape, norm, update_file, GsyncdError + +SECT_ORD = '__section_order__' +SECT_META = '__meta__' +config_version = 2.0 + +re_type = type(re.compile('')) + + +# (SECTION, OPTION, OLD VALUE, NEW VALUE) +CONFIGS = ( + ("peersrx . .", + "georep_session_working_dir", + "", + "@GLUSTERD_WORKDIR@/geo-replication/${mastervol}_${remotehost}_" + "${slavevol}/"), + ("peersrx .", + "gluster_params", + "aux-gfid-mount xlator-option=\*-dht.assert-no-child-down=true", + "aux-gfid-mount"), + ("peersrx . .", + "ssh_command_tar", + "", + "ssh -oPasswordAuthentication=no -oStrictHostKeyChecking=no " + "-i @GLUSTERD_WORKDIR@/geo-replication/tar_ssh.pem"), + ("peersrx . .", + "changelog_log_file", + "", + "${iprefix}/log/glusterfs/geo-replication/${mastervol}" + "/${eSlave}${local_id}-changes.log"), + ("peersrx . .", + "working_dir", + "@localstatedir@/run/gluster/${mastervol}/${eSlave}", + "${iprefix}/lib/misc/glusterfsd/${mastervol}/${eSlave}"), +) + + +def upgrade_config_file(path): + config_change = False + config = ConfigParser.RawConfigParser() + config.read(path) + + for sec, opt, oldval, newval in CONFIGS: + try: + val = config.get(sec, opt) + except ConfigParser.NoOptionError: + # if new config opt not exists + config_change = True + config.set(sec, opt, newval) + continue + except ConfigParser.Error: + """ + When gsyncd invoked at the time of create, config file + will not be their. Ignore any ConfigParser errors + """ + continue + + if val == newval: + # value is same as new val + continue + + if val == oldval: + # config value needs update + config_change = True + config.set(sec, opt, newval) + + if config_change: + tempConfigFile = tempfile.NamedTemporaryFile(mode="wb", delete=False) + with open(tempConfigFile.name, 'wb') as configFile: + config.write(configFile) + + # If src and dst are two different file system, then os.rename + # fails, In this case if temp file created in /tmp and if /tmp is + # seperate fs then os.rename gives following error, so use shutil + # OSError: [Errno 18] Invalid cross-device link + # mail.python.org/pipermail/python-list/2005-February/342893.html + shutil.move(tempConfigFile.name, path) + + +class MultiDict(object): + + """a virtual dict-like class which functions as the union + of underlying dicts""" + + def __init__(self, *dd): + self.dicts = dd + + def __getitem__(self, key): + val = None + for d in self.dicts: + if d.get(key) is not None: + val = d[key] + if val is None: + raise KeyError(key) + return val + + +class GConffile(object): + + """A high-level interface to ConfigParser which flattens the two-tiered + config layout by implenting automatic section dispatch based on initial + parameters. + + Also ensure section ordering in terms of their time of addition -- a compat + hack for Python < 2.7. + """ + + def _normconfig(self): + """normalize config keys by s/-/_/g""" + for n, s in self.config._sections.items(): + if n.find('__') == 0: + continue + s2 = type(s)() + for k, v in s.items(): + if k.find('__') != 0: + k = norm(k) + s2[k] = v + self.config._sections[n] = s2 + + def __init__(self, path, peers, *dd): + """ + - .path: location of config file + - .config: underlying ConfigParser instance + - .peers: on behalf of whom we flatten .config + (master, or master-slave url pair) + - .auxdicts: template subtituents + """ + self.peers = peers + self.path = path + self.auxdicts = dd + self.config = ConfigParser.RawConfigParser() + self.config.read(path) + self.dev, self.ino, self.mtime = -1, -1, -1 + self._normconfig() + + def _load(self): + try: + sres = os.stat(self.path) + self.dev = sres[ST_DEV] + self.ino = sres[ST_INO] + self.mtime = sres[ST_MTIME] + except (OSError, IOError): + if sys.exc_info()[1].errno == errno.ENOENT: + sres = None + + self.config = ConfigParser.RawConfigParser() + self.config.read(self.path) + self._normconfig() + + def get_realtime(self, opt): + try: + sres = os.stat(self.path) + except (OSError, IOError): + if sys.exc_info()[1].errno == errno.ENOENT: + sres = None + else: + raise + + # compare file system stat with that of our stream file handle + if not sres or sres[ST_DEV] != self.dev or \ + sres[ST_INO] != self.ino or self.mtime != sres[ST_MTIME]: + self._load() + + return self.get(opt, printValue=False) + + def section(self, rx=False): + """get the section name of the section representing .peers + in .config""" + peers = self.peers + if not peers: + peers = ['.', '.'] + rx = True + if rx: + st = 'peersrx' + else: + st = 'peers' + return ' '.join([st] + [escape(u) for u in peers]) + + @staticmethod + def parse_section(section): + """retrieve peers sequence encoded by section name + (as urls or regexen, depending on section type) + """ + sl = section.split() + st = sl.pop(0) + sl = [unescape(u) for u in sl] + if st == 'peersrx': + sl = [re.compile(u) for u in sl] + return sl + + def ord_sections(self): + """Return an ordered list of sections. + + Ordering happens based on the auxiliary + SECT_ORD section storing indices for each + section added through the config API. + + To not to go corrupt in case of manually + written config files, we take care to append + also those sections which are not registered + in SECT_ORD. + + Needed for python 2.{4,5,6} where ConfigParser + cannot yet order sections/options internally. + """ + so = {} + if self.config.has_section(SECT_ORD): + so = self.config._sections[SECT_ORD] + so2 = {} + for k, v in so.items(): + if k != '__name__': + so2[k] = int(v) + tv = 0 + if so2: + tv = max(so2.values()) + 1 + ss = [s for s in self.config.sections() if s.find('__') != 0] + for s in ss: + if s in so.keys(): + continue + so2[s] = tv + tv += 1 + + def scmp(x, y): + return cmp(*(so2[s] for s in (x, y))) + ss.sort(scmp) + return ss + + def update_to(self, dct, allow_unresolved=False): + """update @dct from key/values of ours. + + key/values are collected from .config by filtering the regexp sections + according to match, and from .section. The values are treated as + templates, which are substituted from .auxdicts and (in case of regexp + sections) match groups. + """ + if not self.peers: + raise GsyncdError('no peers given, cannot select matching options') + + def update_from_sect(sect, mud): + for k, v in self.config._sections[sect].items(): + if k == '__name__': + continue + if allow_unresolved: + dct[k] = Template(v).safe_substitute(mud) + else: + dct[k] = Template(v).substitute(mud) + for sect in self.ord_sections(): + sp = self.parse_section(sect) + if isinstance(sp[0], re_type) and len(sp) == len(self.peers): + match = True + mad = {} + for i in range(len(sp)): + m = sp[i].search(self.peers[i]) + if not m: + match = False + break + for j in range(len(m.groups())): + mad['match%d_%d' % (i + 1, j + 1)] = m.groups()[j] + if match: + update_from_sect(sect, MultiDict(dct, mad, *self.auxdicts)) + if self.config.has_section(self.section()): + update_from_sect(self.section(), MultiDict(dct, *self.auxdicts)) + + def get(self, opt=None, printValue=True): + """print the matching key/value pairs from .config, + or if @opt given, the value for @opt (according to the + logic described in .update_to) + """ + d = {} + self.update_to(d, allow_unresolved=True) + if opt: + opt = norm(opt) + v = d.get(opt) + if v: + if printValue: + print(v) + else: + return v + else: + for k, v in d.iteritems(): + if k == '__name__': + continue + print("%s: %s" % (k, v)) + + def write(self, trfn, opt, *a, **kw): + """update on-disk config transactionally + + @trfn is the transaction function + """ + def mergeconf(f): + self.config = ConfigParser.RawConfigParser() + self.config.readfp(f) + self._normconfig() + if not self.config.has_section(SECT_META): + self.config.add_section(SECT_META) + self.config.set(SECT_META, 'version', config_version) + return trfn(norm(opt), *a, **kw) + + def updateconf(f): + self.config.write(f) + update_file(self.path, updateconf, mergeconf) + + def _set(self, opt, val, rx=False): + """set @opt to @val in .section""" + sect = self.section(rx) + if not self.config.has_section(sect): + self.config.add_section(sect) + # regarding SECT_ORD, cf. ord_sections + if not self.config.has_section(SECT_ORD): + self.config.add_section(SECT_ORD) + self.config.set( + SECT_ORD, sect, len(self.config._sections[SECT_ORD])) + self.config.set(sect, opt, val) + return True + + def set(self, opt, *a, **kw): + """perform ._set transactionally""" + self.write(self._set, opt, *a, **kw) + + def _delete(self, opt, rx=False): + """delete @opt from .section""" + sect = self.section(rx) + if self.config.has_section(sect): + return self.config.remove_option(sect, opt) + + def delete(self, opt, *a, **kw): + """perform ._delete transactionally""" + self.write(self._delete, opt, *a, **kw) -- cgit