X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=hippotat.git;a=blobdiff_plain;f=hippotat%2F__init__.py;h=8a21966e279c175af286cd6855e7255339996d33;hp=8563834b5aca1ff16b48561d7bdba61f0034b9c1;hb=c7f134ce5802cb6a36371a5130d3a678518e9152;hpb=9e4456902798923b43e0e7748250ec87c0e0d3dd diff --git a/hippotat/__init__.py b/hippotat/__init__.py index 8563834..8a21966 100644 --- a/hippotat/__init__.py +++ b/hippotat/__init__.py @@ -86,6 +86,7 @@ class LogNotBoringTwisted: if event.get('log_level') != LogLevel.info: return yes dflag = event.get('dflag') + if dflag is False : return yes if dflag in debug_set: return yes if dflag is None and DBG.TWISTED in debug_set: return yes return no @@ -105,6 +106,8 @@ http_timeout_grace = 5 max_requests_outstanding = 6 max_batch_up = 4000 http_retry = 5 +port = 80 +vroutes = '' #[server] or [] overrides ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip %(rnets)s @@ -112,23 +115,22 @@ ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip %(rnets)s # relating to virtual network mtu = 1500 -[server] +[SERVER] +server = SERVER # addrs = 127.0.0.1 ::1 -port = 80 # url # relating to virtual network -routes = '' -vnetwork = 172.24.230.192 -# network = / -# server = -# relay = +vvnetwork = 172.24.230.192 +# vnetwork = / +# vadd r = +# vrelay = # [] # password = # used by both, must match -[limits] +[LIMIT] max_batch_down = 262144 max_queue_time = 121 http_timeout = 121 @@ -146,13 +148,11 @@ def mime_translate(s): return s.translate(_mimetrans) class ConfigResults: - def __init__(self, d = { }): - self.__dict__ = d + def __init__(self): + pass def __repr__(self): return 'ConfigResults('+repr(self.__dict__)+')' -c = ConfigResults() - def log_discard(packet, iface, saddr, daddr, why): log_debug(DBG.DROP, 'discarded packet [%s] %s -> %s: %s' % (iface, saddr, daddr, why), @@ -336,75 +336,165 @@ def crash_on_critical(event): #---------- config processing ---------- -def process_cfg_common_always(): - global mtu - c.mtu = cfg.get('virtual','mtu') +def _cfg_process_putatives(): + servers = { } + clients = { } + # maps from abstract object to canonical name for cs's -def process_cfg_ipif(section, varmap): - for d, s in varmap: - try: v = getattr(c, s) - except AttributeError: continue - setattr(c, d, v) + def putative(cmap, abstract, canoncs): + try: + current_canoncs = cmap[abstract] + except KeyError: + pass + else: + assert(current_canoncs == canoncs) + cmap[abstract] = canoncs + + server_pat = r'[-.0-9A-Za-z]+' + client_pat = r'[.:0-9a-f]+' + server_re = regexp.compile(server_pat) + serverclient_re = regexp.compile(server_pat + r' ' + client_pat) - #print(repr(c)) + for cs in cfg.sections(): + if cs == 'LIMIT': + # plan A "[LIMIT]" + continue - c.ipif_command = cfg.get(section,'ipif', vars=c.__dict__) + try: + # plan B "[]" part 1 + ci = ipaddr(cs) + except AddressValueError: + + if server_re.fullmatch(cs): + # plan C "[]" + putative(servers, cs, cs) + continue + + if serverclient_re.fullmatch(cs): + # plan D "[ ]" part 1 + (pss,pcs) = cs.split(' ') + + if pcs == 'LIMIT': + # plan E "[ LIMIT]" + continue + + try: + # plan D "[ ]" part 2 + ci = ipaddr(pc) + except AddressValueError: + # plan F "[]" + # well, we ignore this + print('warning: ignoring config section %s' % cs, file=sys.stderr) + continue + + else: # no AddressValueError + # plan D "[ " part 2 + putative(clients, ci, cs) + continue + + return (servers, clients) + +def cfg_process_common(ss): + c.mtu = cfg.getint(ss, 'mtu') + +def cfg_process_saddrs(c, ss): + class ServerAddr(): + def __init__(self, port, addrspec): + self.port = port + # also self.addr + try: + self.addr = ipaddress.IPv4Address(addrspec) + self._endpointfactory = twisted.internet.endpoints.TCP4ServerEndpoint + self._inurl = b'%s' + except AddressValueError: + self.addr = ipaddress.IPv6Address(addrspec) + self._endpointfactory = twisted.internet.endpoints.TCP6ServerEndpoint + self._inurl = b'[%s]' + def make_endpoint(self): + return self._endpointfactory(reactor, self.port, self.addr) + def url(self): + url = b'http://' + (self._inurl % str(self.addr).encode('ascii')) + if self.port != 80: url += b':%d' % self.port + url += b'/' + return url + + c.port = cfg.getint(ss,'port') + c.saddrs = [ ] + for addrspec in cfg.get(ss, 'addrs').split(): + sa = ServerAddr(c.port, addrspec) + c.saddrs.append(sa) -def process_cfg_network(): - c.network = ipnetwork(cfg.get('virtual','network')) - if c.network.num_addresses < 3 + 2: - raise ValueError('network needs at least 2^3 addresses') +def cfg_process_vnetwork(c, ss): + c.vnetwork = ipnetwork(cfg.get(ss,'vnetwork')) + if c.vnetwork.num_addresses < 3 + 2: + raise ValueError('vnetwork needs at least 2^3 addresses') -def process_cfg_server(): +def cfg_process_vaddr(c, ss): try: - c.server = cfg.get('virtual','server') + c.vaddr = cfg.get(ss,'vaddr') except NoOptionError: - process_cfg_network() - c.server = next(c.network.hosts()) - -class ServerAddr(): - def __init__(self, port, addrspec): - self.port = port - # also self.addr - try: - self.addr = ipaddress.IPv4Address(addrspec) - self._endpointfactory = twisted.internet.endpoints.TCP4ServerEndpoint - self._inurl = b'%s' - except AddressValueError: - self.addr = ipaddress.IPv6Address(addrspec) - self._endpointfactory = twisted.internet.endpoints.TCP6ServerEndpoint - self._inurl = b'[%s]' - def make_endpoint(self): - return self._endpointfactory(reactor, self.port, self.addr) - def url(self): - url = b'http://' + (self._inurl % str(self.addr).encode('ascii')) - if self.port != 80: url += b':%d' % self.port - url += b'/' - return url + cfg_process_vnetwork(c, ss) + c.vaddr = next(c.vnetwork.hosts()) + +def cfg_search_section(key,sections): + for section in sections: + if cfg.has_option(section, key): + return section + raise NoOptionError(key, repr(sections)) + +def cfg_search(getter,key,sections): + section = cfg_search_section(key,sections) + return getter(section, key) + +def cfg_process_client_limited(cc,ss,sections,key): + val = cfg_search(cfg.getint, key, sections) + lim = cfg_search(cfg.getint, key, ['%s LIMIT' % ss, 'LIMIT']) + cc.__dict__[key] = min(val,lim) + +def cfg_process_client_common(cc,ss,cs,ci): + # returns sections to search in, iff password is defined, otherwise None + cc.ci = ci + + sections = ['%s %s' % (ss,cs), + cs, + ss, + 'DEFAULT'] + + try: pwsection = cfg_search_section('password', sections) + except NoOptionError: return None -def process_cfg_saddrs(): - try: port = cfg.getint('server','port') - except NoOptionError: port = 80 + pw = cfg.get(pwsection, 'password') + cc.password = pw.encode('utf-8') - c.saddrs = [ ] - for addrspec in cfg.get('server','addrs').split(): - sa = ServerAddr(port, addrspec) - c.saddrs.append(sa) + cfg_process_client_limited(cc,ss,sections,'target_requests_outstanding') + cfg_process_client_limited(cc,ss,sections,'http_timeout') -def process_cfg_clients(constructor): - c.clients = [ ] - for cs in cfg.sections(): - if not (':' in cs or '.' in cs): continue - ci = ipaddr(cs) - pw = cfg.get(cs, 'password') - pw = pw.encode('utf-8') - constructor(ci,cs,pw) + return sections + +def cfg_process_ipif(c, sections, varmap): + for d, s in varmap: + try: v = getattr(c, s) + except AttributeError: continue + setattr(c, d, v) + + #print('CFGIPIF',repr((varmap, sections, c.__dict__)),file=sys.stderr) + + section = cfg_search_section('ipif', sections) + c.ipif_command = cfg.get(section,'ipif', vars=c.__dict__) #---------- startup ---------- def common_startup(process_cfg): + # calls process_cfg(putative_clients, putative_servers) + # ConfigParser hates #-comments after values - trailingcomments_re = regexp.compile('#.*') + trailingcomments_re = regexp.compile(r'#.*') cfg.read_string(trailingcomments_re.sub('', defcfg)) need_defcfg = True @@ -514,7 +604,9 @@ just `+': all DFLAGs. readconfig('/etc/hippotat/config', False) readconfig('/etc/hippotat/config.d', False) - try: process_cfg() + try: + (pss, pcs) = _cfg_process_putatives() + process_cfg(pss, pcs) except (configparser.Error, ValueError): traceback.print_exc(file=sys.stderr) print('\nInvalid configuration, giving up.', file=sys.stderr)