+####################
+class TestInstance:
+ def __init__ (self, buildname, pid=0):
+ self.pids=[]
+ if pid!=0: self.pid.append(pid)
+ self.buildname=buildname
+ # latest trace line
+ self.trace=''
+ # has a KO test
+ self.broken_steps=[]
+ self.timestamp = 0
+
+ def set_timestamp (self,timestamp): self.timestamp=timestamp
+ def set_now (self): self.timestamp=int(time.time())
+ def pretty_timestamp (self): return time.strftime("%Y-%m-%d:%H-%M",time.localtime(self.timestamp))
+
+
+ def add_pid (self,pid):
+ self.pids.append(pid)
+ def set_broken (self,plcindex, step):
+ self.broken_steps.append ( (plcindex, step,) )
+
+ def line (self):
+ double='=='
+ if self.pids: double='*'+double[1]
+ if self.broken_steps: double=double[0]+'B'
+ msg = " %s %s =="%(double,self.buildname)
+ if not self.pids: pass
+ elif len(self.pids)==1: msg += " (pid=%s)"%self.pids[0]
+ else: msg += " !!!pids=%s!!!"%self.pids
+ msg += " @%s"%self.pretty_timestamp()
+ if self.broken_steps:
+ msg += "\n BROKEN IN STEPS"
+ for (i,s) in self.broken_steps: msg += " %s@%s"%(s,i)
+ return msg
+
+class TestBox (Box):
+ def __init__ (self,hostname):
+ Box.__init__(self,hostname)
+ self.starting_ips=[]
+ self.test_instances=[]
+
+ def reboot (self, options):
+ # can't reboot a vserver VM
+ self.run_ssh (['pkill','run_log'],"Terminating current runs",
+ dry_run=options.dry_run)
+ self.run_ssh (['rm','-f',Starting.location],"Cleaning %s"%Starting.location,
+ dry_run=options.dry_run)
+
+ def get_test (self, buildname):
+ for i in self.test_instances:
+ if i.buildname==buildname: return i
+
+ # we scan ALL remaining test results, even the ones not running
+ def add_timestamp (self, buildname, timestamp):
+ i=self.get_test(buildname)
+ if i:
+ i.set_timestamp(timestamp)
+ else:
+ i=TestInstance(buildname,0)
+ i.set_timestamp(timestamp)
+ self.test_instances.append(i)
+
+ def add_running_test (self, pid, buildname):
+ i=self.get_test(buildname)
+ if not i:
+ self.test_instances.append (TestInstance (buildname,pid))
+ return
+ if i.pids:
+ print "WARNING: 2 concurrent tests run on same build %s"%buildname
+ i.add_pid (pid)
+
+ def add_broken (self, buildname, plcindex, step):
+ i=self.get_test(buildname)
+ if not i:
+ i=TestInstance(buildname)
+ self.test_instances.append(i)
+ i.set_broken(plcindex, step)
+
+ matcher_proc=re.compile (".*/proc/(?P<pid>[0-9]+)/cwd.*/root/(?P<buildname>[^/]+)$")
+ matcher_grep=re.compile ("/root/(?P<buildname>[^/]+)/logs/trace.*:TRACE:\s*(?P<plcindex>[0-9]+).*step=(?P<step>\S+).*")
+ def sense (self, options):
+ print 't',
+ self.sense_uptime()
+ self.starting_ips=[x for x in self.backquote_ssh(['cat',Starting.location], trash_err=True).strip().split('\n') if x]
+
+ # scan timestamps on all tests
+ # this is likely to not invoke ssh so we need to be a bit smarter to get * expanded
+ # xxx would make sense above too
+ command=['bash','-c',"grep . /root/*/timestamp /dev/null"]
+ ts_lines=self.backquote_ssh(command,trash_err=True).split('\n')
+ for ts_line in ts_lines:
+ if not ts_line.strip(): continue
+ # expect /root/<buildname>/timestamp:<timestamp>
+ try:
+ (ts_file,timestamp)=ts_line.split(':')
+ ts_file=os.path.dirname(ts_file)
+ buildname=os.path.basename(ts_file)
+ timestamp=int(timestamp)
+ t=self.add_timestamp(buildname,timestamp)
+ except: print 'WARNING, could not parse ts line',ts_line
+
+ command=['bash','-c',"grep KO /root/*/logs/trace* /dev/null" ]
+ trace_lines=self.backquote_ssh (command).split('\n')
+ for line in trace_lines:
+ if not line.strip(): continue
+ m=TestBox.matcher_grep.match(line)
+ if m:
+ buildname=m.group('buildname')
+ plcindex=m.group('plcindex')
+ step=m.group('step')
+ self.add_broken(buildname,plcindex, step)
+ else: header("command %r returned line that failed to match\n%s"%(command,line))
+
+ pids = self.backquote_ssh (['pgrep','run_log'],trash_err=True)
+ if not pids: return
+ command=['ls','-ld'] + ["/proc/%s/cwd"%pid for pid in pids.split("\n") if pid]
+ ps_lines=self.backquote_ssh (command).split('\n')
+ for line in ps_lines:
+ if not line.strip(): continue
+ m=TestBox.matcher_proc.match(line)
+ if m:
+ pid=m.group('pid')
+ buildname=m.group('buildname')
+ self.add_running_test(pid, buildname)
+ else: header("command %r returned line that failed to match\n%s"%(command,line))
+
+
+ def line (self):
+ return "%s (%s)"%(self.hostname,self.uptime())
+
+ def list (self):
+ if not self.test_instances:
+ header ("No known tests on %s"%self.line())
+ else:
+ header ("Known tests on %s"%self.line())
+ self.test_instances.sort(timestamp_sort)
+ for i in self.test_instances: print i.line()
+ if self.starting_ips:
+ header ("Starting IP addresses on %s"%self.line())
+ self.starting_ips.sort()
+ for starting in self.starting_ips: print starting
+