From f9a982f30922d812097a70b87628a590b2f51f15 Mon Sep 17 00:00:00 2001 From: Csaba Henk Date: Thu, 10 Mar 2011 00:39:27 +0000 Subject: syncdaemon: configinterface: add support for regexp based pattern-matching sections Signed-off-by: Kaushik BV Signed-off-by: Vijay Bellur BUG: 2427 (set a proper default for remote syncdaemon) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=2427 --- .../marker/utils/syncdaemon/configinterface.py | 121 +++++++++++++++++---- xlators/features/marker/utils/syncdaemon/gsyncd.py | 47 ++++---- 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/xlators/features/marker/utils/syncdaemon/configinterface.py b/xlators/features/marker/utils/syncdaemon/configinterface.py index 25a2a526818..f0efc1df861 100644 --- a/xlators/features/marker/utils/syncdaemon/configinterface.py +++ b/xlators/features/marker/utils/syncdaemon/configinterface.py @@ -3,29 +3,106 @@ try: except ImportError: # py 3 import configparser as ConfigParser +import re +try: + # py 3 + from urllib import parse as urllib +except ImportError: + import urllib +SECT_ORD = '__section_order__' -DEF_SECT = 'global' +re_type = type(re.compile('')) class GConffile(object): def __init__(self, path, peers): - if peers: - self.section = 'peers ' + ' '.join(peers) - else: - self.section = DEF_SECT + self.peers = peers self.path = path self.config = ConfigParser.RawConfigParser() self.config.read(path) + def section(self, rx=False): + peers = self.peers + if not peers: + peers = ['.', '.'] + rx = True + if rx: + st = 'peersrx' + else: + st = 'peers' + return ' '.join([st] + [urllib.quote_plus(u) for u in peers]) + + @staticmethod + def parse_section(section): + sl = section.split() + st = sl.pop(0) + sl = [urllib.unquote_plus(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} 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 = self.config.sections() + try: + ss.remove(SECT_ORD) + except ValueError: + pass + 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): - for sect in set([DEF_SECT, self.section]): - if self.config.has_section(sect): - for k, v in self.config._sections[sect].items(): - if k == '__name__': - continue - k = k.replace('-', '_') - dct[k] = v + if not self.peers: + raise RuntimeError('no peers given, cannot select matching options') + def update_from_sect(sect): + for k, v in self.config._sections[sect].items(): + if k == '__name__': + continue + k = k.replace('-', '_') + dct[k] = v + 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 + for i in range(len(sp)): + if not sp[i].search(self.peers[i]): + match = False + break + if match: + update_from_sect(sect) + if self.config.has_section(self.section()): + update_from_sect(self.section()) def get(self, opt=None): d = {} @@ -46,14 +123,20 @@ class GConffile(object): if f: f.close() - def set(self, opt, val): - if not self.config.has_section(self.section): - self.config.add_section(self.section) - self.config.set(self.section, opt, val) + def set(self, opt, val, rx=False): + 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) self.write() - def delete(self, opt): - if not self.config.has_section(self.section): + def delete(self, opt, rx=False): + sect = self.section(rx) + if not self.config.has_section(sect): return - if self.config.remove_option(self.section, opt): + if self.config.remove_option(sect, opt): self.write() diff --git a/xlators/features/marker/utils/syncdaemon/gsyncd.py b/xlators/features/marker/utils/syncdaemon/gsyncd.py index b8b92056b54..d8166baea53 100644 --- a/xlators/features/marker/utils/syncdaemon/gsyncd.py +++ b/xlators/features/marker/utils/syncdaemon/gsyncd.py @@ -199,9 +199,14 @@ def main_i(): a[-1].values.__dict__.update(log_level='DEBUG'))) op.add_option('--config-get', metavar='OPT', type=str, dest='config', action='callback', callback=store_local) op.add_option('--config-get-all', dest='config', action='callback', callback=store_local_curry(True)) - op.add_option('--config-set', metavar='OPT VAL', type=str, nargs=2, dest='config', action='callback', callback=store_local) + op.add_option('--config-set', metavar='OPT VAL', type=str, nargs=2, dest='config', action='callback', + callback=lambda o, oo, vx, p: store_local(o, oo, (vx[0], vx[1], False), p)) + op.add_option('--config-set-rx', metavar='OPT VAL', type=str, nargs=2, dest='config', action='callback', + callback=lambda o, oo, vx, p: store_local(o, oo, (vx[0], vx[1], True), p)) op.add_option('--config-del', metavar='OPT', type=str, dest='config', action='callback', callback=lambda o, oo, vx, p: - store_local(o, oo, (vx, False), p)) + store_local(o, oo, (vx, False, False), p)) + op.add_option('--config-del-rx', metavar='OPT', type=str, dest='config', action='callback', callback=lambda o, oo, vx, p: + store_local(o, oo, (vx, False, True), p)) # precedence for sources of values: 1) commandline, 2) cfg file, 3) defaults # -- for this to work out we need to tell apart defaults from explicitly set @@ -209,25 +214,30 @@ def main_i(): # values container. defaults = op.get_default_values() opts, args = op.parse_args(values=optparse.Values()) - if not (len(args) == 2 or (len(args) == 1 and rconf.get('listen')) or (len(args) <= 2 and rconf.get('config'))): + confdata = rconf.get('config') + if not (len(args) == 2 or (len(args) == 1 and rconf.get('listen')) or (len(args) <= 2 and confdata)): sys.stderr.write("error: incorrect number of arguments\n\n") sys.stderr.write(op.get_usage() + "\n") sys.exit(1) - local = remote = None - if args: - local = resource.parse_url(args[0]) - if len(args) > 1: - remote = resource.parse_url(args[1]) - if not local.can_connect_to(remote): - raise RuntimeError("%s cannot work with %s" % (local.path, remote and remote.path)) - pa = ([], []) - canon = [False, True] - for x in (local, remote): - if x: - for i in range(2): - pa[i].append(x.get_url(canonical=canon[i])) - peers, canon_peers = pa + if confdata and isinstance(confdata, tuple) and confdata[2]: + # peers are regexen, don't try to parse them + canon_peers = args + else: + local = remote = None + if args: + local = resource.parse_url(args[0]) + if len(args) > 1: + remote = resource.parse_url(args[1]) + if not local.can_connect_to(remote): + raise RuntimeError("%s cannot work with %s" % (local.path, remote and remote.path)) + pa = ([], []) + canon = [False, True] + for x in (local, remote): + if x: + for i in range(2): + pa[i].append(x.get_url(canonical=canon[i])) + peers, canon_peers = pa if not 'config_file' in rconf: rconf['config_file'] = os.path.join(os.path.dirname(sys.argv[0]), "conf/gsyncd.conf") confp = os.path.dirname(sys.argv[0]) + "conf/" @@ -241,13 +251,12 @@ def main_i(): raise gcnf = GConffile(rconf['config_file'], canon_peers) - confdata = rconf.get('config') if confdata: if isinstance(confdata, tuple): if confdata[1]: gcnf.set(*confdata) else: - gcnf.delete(confdata[0]) + gcnf.delete(confdata[0], confdata[1]) else: if confdata == True: confdata = None -- cgit