chiark / gitweb /
process putatives: Add many log_debug_config calls
[hippotat.git] / hippotatlib / __init__.py
index feb7894430fe4b61898fca1c429ebbc0e9a04f5e..bd5d2c3cf58262989707c279c00691e5a969a29b 100644 (file)
@@ -100,27 +100,33 @@ def log_debug(dflag, msg, idof=None, d=None):
     msg += ' ' + d + trunc
   log.info('{dflag} {msgcore}', dflag=dflag, msgcore=msg)
 
+def logevent_is_boringtwisted(event):
+  try:
+    if event.get('log_level') != LogLevel.info:
+      return False
+    dflag = event.get('dflag')
+    if dflag is False                            : return False
+    if dflag                         in debug_set: return False
+    if dflag is None and DBG.TWISTED in debug_set: return False
+    return True
+  except Exception:
+    print('EXCEPTION (IN BORINGTWISTED CHECK)',
+          traceback.format_exc(), file=org_stderr)
+    return False
+
 @implementer(twisted.logger.ILogFilterPredicate)
 class LogNotBoringTwisted:
   def __call__(self, event):
-    yes = twisted.logger.PredicateResult.yes
-    no  = twisted.logger.PredicateResult.no
-    try:
-      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
-    except Exception:
-      print(traceback.format_exc(), file=org_stderr)
-      return yes
+    return (
+      twisted.logger.PredicateResult.no
+      if logevent_is_boringtwisted(event) else
+      twisted.logger.PredicateResult.yes
+    )
 
 #---------- default config ----------
 
 defcfg = '''
-[DEFAULT]
+[COMMON]
 max_batch_down = 65536
 max_queue_time = 10
 target_requests_outstanding = 3
@@ -131,22 +137,22 @@ max_batch_up = 4000
 http_retry = 5
 port = 80
 vroutes = ''
+ifname_client = hippo%%d
+ifname_server = shippo%%d
 
 #[server] or [<client>] overrides
-ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip %(rnets)s
+ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip,%(ifname)s %(rnets)s
 
 # relating to virtual network
 mtu = 1500
 
-[SERVER]
-server = SERVER
 # addrs = 127.0.0.1 ::1
 # url
 
 # relating to virtual network
 vvnetwork = 172.24.230.192
 # vnetwork = <prefix>/<len>
-# vadd  r  = <ipaddr>
+# vaddr    = <ipaddr>
 # vrelay   = <ipaddr>
 
 
@@ -380,11 +386,20 @@ def _cfg_process_putatives():
   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)
+  serverclient_re = regexp.compile(
+        server_pat + r' ' + '(?:' + client_pat + '|LIMIT)')
 
   for cs in cfg.sections():
-    if cs == 'LIMIT':
-      # plan A "[LIMIT]"
+    log_debug_config('putatives: section [%s]...' % (cs))
+
+    def log_ignore(why):
+      log_debug_config('putatives: section [%s] X ignore: %s' % (cs, why))
+      print('warning: ignoring config section [%s] (%s)' % (cs, why),
+            file=sys.stderr)
+
+    if cs == 'LIMIT' or cs == 'COMMON':
+      # plan A "[LIMIT]" or "[COMMON]"
+      log_debug_config('putatives: section [%s] A ignore' % (cs))
       continue
 
     try:
@@ -394,6 +409,7 @@ def _cfg_process_putatives():
 
       if server_re.fullmatch(cs):
         # plan C "[<servername>]"
+        log_debug_config('putatives: section [%s] C <server>' % (cs))
         putative(servers, cs, cs)
         continue
 
@@ -403,6 +419,7 @@ def _cfg_process_putatives():
 
         if pcs == 'LIMIT':
           # plan E "[<servername> LIMIT]"
+          log_debug_config('putatives: section [%s] E <server> LIMIT' % (cs))
           continue
 
         try:
@@ -410,25 +427,27 @@ def _cfg_process_putatives():
           ci = ipaddr(pc)
         except AddressValueError:
           # plan F "[<some thing we do not understand>]"
-          # well, we ignore this
-          print('warning: ignoring config section %s' % cs, file=sys.stderr)
+          log_ignore('bad-addr')
           continue
 
         else: # no AddressValueError
-          # plan D "[<servername> <client]" part 3
+          # plan D "[<servername> <client>]" part 3
+          log_debug_config('putatives: section [%s] D <server> <client>'
+                           % (cs))
           putative(clients, ci, pcs)
           putative(servers, pss, pss)
           continue
 
     else: # no AddressValueError
       # plan B "[<client>" part 2
+      log_debug_config('putatives: section [%s] B <client>' % (cs))
       putative(clients, ci, cs)
       continue
 
   return (servers, clients)
 
-def cfg_process_common(c, ss):
-  c.mtu = cfg.getint(ss, 'mtu')
+def cfg_process_general(c, ss):
+  c.mtu = cfg1getint(ss, 'mtu')
 
 def cfg_process_saddrs(c, ss):
   class ServerAddr():
@@ -444,27 +463,30 @@ def cfg_process_saddrs(c, ss):
         self._endpointfactory = twisted.internet.endpoints.TCP6ServerEndpoint
         self._inurl = b'[%s]'
     def make_endpoint(self):
-      return self._endpointfactory(reactor, self.port, self.addr)
+      return self._endpointfactory(reactor, self.port,
+                                   interface= '%s' % 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
+    def __repr__(self):
+      return 'ServerAddr'+repr((self.port,self.addr))
 
-  c.port = cfg.getint(ss,'port')
+  c.port = cfg1getint(ss,'port')
   c.saddrs = [ ]
-  for addrspec in cfg.get(ss, 'addrs').split():
+  for addrspec in cfg1get(ss, 'addrs').split():
     sa = ServerAddr(c.port, addrspec)
     c.saddrs.append(sa)
 
 def cfg_process_vnetwork(c, ss):
-  c.vnetwork = ipnetwork(cfg.get(ss,'vnetwork'))
+  c.vnetwork = ipnetwork(cfg1get(ss,'vnetwork'))
   if c.vnetwork.num_addresses < 3 + 2:
     raise ValueError('vnetwork needs at least 2^3 addresses')
 
 def cfg_process_vaddr(c, ss):
   try:
-    c.vaddr = cfg.get(ss,'vaddr')
+    c.vaddr = cfg1get(ss,'vaddr')
   except NoOptionError:
     cfg_process_vnetwork(c, ss)
     c.vaddr = next(c.vnetwork.hosts())
@@ -475,13 +497,24 @@ def cfg_search_section(key,sections):
       return section
   raise NoOptionError(key, repr(sections))
 
+def cfg_get_raw(*args, **kwargs):
+  # for passing to cfg_search
+  return cfg.get(*args, raw=True, **kwargs)
+
 def cfg_search(getter,key,sections):
   section = cfg_search_section(key,sections)
   return getter(section, key)
 
+def cfg1get(section,key, getter=cfg.get,**kwargs):
+  section = cfg_search_section(key,[section,'COMMON'])
+  return getter(section,key,**kwargs)
+
+def cfg1getint(section,key, **kwargs):
+  return cfg1get(section,key, getter=cfg.getint,**kwargs);
+
 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'])
+  val = cfg_search(cfg1getint, key, sections)
+  lim = cfg_search(cfg1getint, key, ['%s LIMIT' % ss, 'LIMIT'])
   cc.__dict__[key] = min(val,lim)
 
 def cfg_process_client_common(cc,ss,cs,ci):
@@ -491,12 +524,12 @@ def cfg_process_client_common(cc,ss,cs,ci):
   sections = ['%s %s' % (ss,cs),
               cs,
               ss,
-              'DEFAULT']
+              'COMMON']
 
   try: pwsection = cfg_search_section('password', sections)
   except NoOptionError: return None
     
-  pw = cfg.get(pwsection, 'password')
+  pw = cfg1get(pwsection, 'password')
   cc.password = pw.encode('utf-8')
 
   cfg_process_client_limited(cc,ss,sections,'target_requests_outstanding')
@@ -513,10 +546,14 @@ def cfg_process_ipif(c, sections, varmap):
   #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__)
+  c.ipif_command = cfg1get(section,'ipif', vars=c.__dict__)
 
 #---------- startup ----------
 
+def log_debug_config(m):
+  if not DBG.CONFIG in debug_set: return
+  print('DBG.CONFIG:', m)
+
 def common_startup(process_cfg):
   # calls process_cfg(putative_clients, putative_servers)
 
@@ -528,7 +565,7 @@ def common_startup(process_cfg):
   def readconfig(pathname, mandatory=True):
     def log(m, p=pathname):
       if not DBG.CONFIG in debug_set: return
-      print('DBG.CONFIG: %s: %s' % (m, pathname))
+      log_debug_config('%s: %s' % (m, p))
 
     try:
       files = os.listdir(pathname)
@@ -546,7 +583,7 @@ def common_startup(process_cfg):
     # is a directory
     log('directory')
     re = regexp.compile('[^-A-Za-z0-9_]')
-    for f in os.listdir(cdir):
+    for f in os.listdir(pathname):
       if re.search(f): continue
       subpath = pathname + '/' + f
       try:
@@ -562,12 +599,26 @@ def common_startup(process_cfg):
     need_defcfg = False
     readconfig(value)
 
+  def oc_extra_config(od,os, value, op):
+    readconfig(value)
+
+  def read_defconfig():
+    readconfig('/etc/hippotat/config.d', False)
+    readconfig('/etc/hippotat/passwords.d', False)
+    readconfig('/etc/hippotat/master.cfg',   False)
+
+  def oc_defconfig(od,os, value, op):
+    nonlocal need_defcfg
+    need_defcfg = False
+    read_defconfig(value)
+
   def dfs_less_detailed(dl):
     return [df for df in DBG.iterconstants() if df <= dl]
 
   def ds_default(od,os,dl,op):
     global debug_set
-    debug_set = set(dfs_less_detailed(debug_def_detail))
+    debug_set.clear
+    debug_set |= set(dfs_less_detailed(debug_def_detail))
 
   def ds_select(od,os, spec, op):
     for it in spec.split(','):
@@ -624,22 +675,34 @@ just `+': all DFLAGs.
                        action='callback',
                        callback= oc_config)
 
+  optparser.add_option('--extra-config',
+                       nargs=1,
+                       type='string',
+                       metavar='CONFIGFILE',
+                       dest='configfile',
+                       action='callback',
+                       callback= oc_extra_config)
+
+  optparser.add_option('--default-config',
+                       action='callback',
+                       callback= oc_defconfig)
+
   (opts, args) = optparser.parse_args()
   if len(args): optparser.error('no non-option arguments please')
 
   if need_defcfg:
-    readconfig('/etc/hippotat/config',   False)
-    readconfig('/etc/hippotat/config.d', False)
+    read_defconfig()
 
   try:
     (pss, pcs) = _cfg_process_putatives()
-    process_cfg(pss, pcs)
+    process_cfg(opts, pss, pcs)
   except (configparser.Error, ValueError):
     traceback.print_exc(file=sys.stderr)
     print('\nInvalid configuration, giving up.', file=sys.stderr)
     sys.exit(12)
 
-  #print(repr(debug_set), file=sys.stderr)
+
+  #print('X', debug_set, file=sys.stderr)
 
   log_formatter = twisted.logger.formatEventAsClassicLogText
   stdout_obs = twisted.logger.FileLogObserver(sys.stdout, log_formatter)
@@ -648,15 +711,17 @@ just `+': all DFLAGs.
   stdsomething_obs = twisted.logger.FilteringLogObserver(
     stderr_obs, [pred], stdout_obs
   )
-  log_observer = twisted.logger.FilteringLogObserver(
+  global file_log_observer
+  file_log_observer = twisted.logger.FilteringLogObserver(
     stdsomething_obs, [LogNotBoringTwisted()]
   )
   #log_observer = stdsomething_obs
   twisted.logger.globalLogBeginner.beginLoggingTo(
-    [ log_observer, crash_on_critical ]
+    [ file_log_observer, crash_on_critical ]
     )
 
 def common_run():
   log_debug(DBG.INIT, 'entering reactor')
   if not _crashing: reactor.run()
-  print('CRASHED (end)', file=sys.stderr)
+  print('ENDED', file=sys.stderr)
+  sys.exit(16)