aboutsummaryrefslogtreecommitdiff
path: root/steuermann
diff options
context:
space:
mode:
Diffstat (limited to 'steuermann')
-rw-r--r--steuermann/config.py2
-rw-r--r--steuermann/db.sql3
-rw-r--r--steuermann/hosts.ini21
-rw-r--r--steuermann/nodes.py10
-rw-r--r--steuermann/report.py37
-rw-r--r--steuermann/run.py3
-rw-r--r--steuermann/run_all.py75
-rw-r--r--steuermann/specfile.exy27
8 files changed, 119 insertions, 59 deletions
diff --git a/steuermann/config.py b/steuermann/config.py
index c422dc2..7419703 100644
--- a/steuermann/config.py
+++ b/steuermann/config.py
@@ -1,5 +1,5 @@
def open_db() :
import sqlite3
- return sqlite3.connect('/ssbwebv1/data2/steuermann/steuermann.sb')
+ return sqlite3.connect('/ssbwebv1/data2/steuermann/steuermann.db')
logdir = '/ssbwebv1/data2/steuermann/logs'
diff --git a/steuermann/db.sql b/steuermann/db.sql
index 664f5df..dd6a569 100644
--- a/steuermann/db.sql
+++ b/steuermann/db.sql
@@ -16,7 +16,8 @@ CREATE TABLE status (
status VARCHAR(5),
-- N = not started
- -- S = started, not finished
+ -- R = started, not finished
+ -- S = skipped
-- P = prereq not satisfied, so not attempted
-- 0-255 = exit code
diff --git a/steuermann/hosts.ini b/steuermann/hosts.ini
index d0c5729..0f0c043 100644
--- a/steuermann/hosts.ini
+++ b/steuermann/hosts.ini
@@ -16,19 +16,22 @@
; definitions common to various operating system environments
[all]
+hostname=no_such_machine
local=[ 'sh', '-c', '%(script)s' ]
maxproc=2
[linux:csh]
-hostname=no_such_machine
-run=[ 'ssh', '-q', '%(hostname)s', 'source .steuermann.%(hostname)s; cd %(workdir)s; hostname; %(script)s ' ]
+; for CSH
+;
+; -q = quiet
+; -x = do not forward X windows; prevents errors locking .Xauthority when lots of ssh come in at the same time
+run=[ 'ssh', '-q', '-x', '%(hostname)s', ' cd %(workdir)s; set node=%(node)s; source bin/.steuermann.%(hostname)s; %(script)s; show_status $status ' ]
like=all
[mac:csh]
like=linux:csh
[windows:cmd]
-hostname=no_such_machine
run=[ 'python', '-m', 'steuermann.windows_comm', '%(hostname)s', '%(script)s' ]
like=all
@@ -56,37 +59,37 @@ like=cadeau
[herbert]
hostname=herbert
like=linux:csh
-workdir=/herbert/data1/sienkiew/steuermann
+workdir=/herbert/data1/iraf/steuermann
maxproc=4
[thor]
hostname=thor
like=linux:csh
-workdir=/thor/data2/sienkiew/steuermann
+workdir=/thor/data2/iraf/steuermann
maxproc=4
[arzach]
hostname=arzach
like=linux:csh
-workdir=/arzach/data1/sienkiew/steuermann
+workdir=/arzach/data1/iraf/steuermann
maxproc=4
[bond]
hostname=bond
like=mac:csh
-workdir=/Users/sienkiew/work/steuermann
+workdir=/Users/iraf/work/steuermann
maxproc=8
[cadeau]
hostname=cadeau
like=mac:csh
-workdir=/Users/sienkiew/work/steuermann
+workdir=/Users/iraf/work/steuermann
maxproc=8
[banana]
hostname=banana
like=mac:csh
-workdir=/Users/sienkiew/work/steuermann
+workdir=/Users/iraf/work/steuermann
maxproc=4
; There is a section [ALL] that is used with every machine name
diff --git a/steuermann/nodes.py b/steuermann/nodes.py
index 0173793..5e9852a 100644
--- a/steuermann/nodes.py
+++ b/steuermann/nodes.py
@@ -3,7 +3,7 @@ Stuff related to the tree structure of the command set
'''
import fnmatch
-import exyapps.runtime
+#import exyapps.runtime
#####
@@ -55,15 +55,11 @@ class command_tree(object):
# make the actual connection between nodes
def connect( self, before, after, required, line ) :
- if not after in self.node_index :
+ if not before in self.node_index :
if required :
print "error: %s happens after non-existant %s - line %s"%(before,after,line)
return
- if not before in self.node_index :
- print "error: before node %s does not exist %s"%(before,line)
- return
-
if not after in self.node_index :
print "error: after node %s does not exist %s"%(after,line)
return
@@ -309,7 +305,7 @@ def read_file_list( file_list ) :
current_file_name = x
sc = specfile.specfileScanner( open(x,'r').read() )
p = specfile.specfile( scanner=sc, data=di )
- result = exyapps.runtime.wrap_error_reporter( p, 'start' )
+ result = specfile.wrap_error_reporter( p, 'start' )
di.finish()
return di
diff --git a/steuermann/report.py b/steuermann/report.py
index ca8d064..82da984 100644
--- a/steuermann/report.py
+++ b/steuermann/report.py
@@ -6,6 +6,7 @@ Generate reports from the database
import time
import sys
import pandokia.text_table as text_table
+import pandokia.common
import StringIO
# this will be reset by the cgi main program if we are in a real cgi
@@ -22,6 +23,10 @@ def info_callback_status( db, run, tablename, host, cmd ) :
#
+ok_status = ( '0', 'N', 'S' )
+
+simple_status = ( 'N', 'P', 'S', 'W' )
+
def info_callback_gui( db, run, tablename, host, cmd ) :
c = db.cursor()
c.execute("SELECT status, start_time, end_time FROM status WHERE run = ? AND host = ? AND tablename = ? AND cmd = ?",(
@@ -38,16 +43,32 @@ def info_callback_gui( db, run, tablename, host, cmd ) :
# t_result = '%s %s %s'%(status, start_time, end_time )
t_result = status
- if status != '0' and status != 'N' :
- status = '<font color=red>%s</font>'%status
-
- if status != 'N' and status != 'P' :
- link = "<a href='%s?action=log&name=%s/%s:%s/%s'>*</a>"%(cginame, run, host, tablename, cmd )
+ if status not in ok_status :
+ d_status = '<font color=red>%s</font>'%status
else :
- link = ''
+ d_status = status
- # h_result = '%s %s %s %s'%(link, status, start_time, end_time)
- h_result = '%s %s'%(link, status )
+ if status in simple_status :
+ link = '%s'%d_status
+ else :
+ link = " <a href='%s?action=log&name=%s/%s:%s/%s'>%s</a>"%(cginame, run, host, tablename, cmd, d_status )
+
+ if not status in simple_status :
+ try :
+ st = pandokia.common.parse_time(start_time)
+ st = '%d:%02d'%(st.hour,st.minute)
+ except ValueError :
+ st = '?'
+ try :
+ et = pandokia.common.parse_time(end_time )
+ et = '%d:%02d'%(et.hour,et.minute)
+ except ValueError :
+ et = '?'
+
+ link = link + " : " + st + " - " + et
+
+ # h_result = '%s %s %s %s'%(link, d_status, start_time, end_time)
+ h_result = link
return ( t_result, h_result )
diff --git a/steuermann/run.py b/steuermann/run.py
index cb2af58..36cda80 100644
--- a/steuermann/run.py
+++ b/steuermann/run.py
@@ -86,7 +86,8 @@ class runner(object):
script_type=node.script_type,
host=node.host,
table=node.table,
- cmd=node.cmd
+ cmd=node.cmd,
+ node=node.name,
)
if debug :
diff --git a/steuermann/run_all.py b/steuermann/run_all.py
index 1c6fe28..e7bff61 100644
--- a/steuermann/run_all.py
+++ b/steuermann/run_all.py
@@ -14,6 +14,8 @@ import nodes
import steuermann.config
+import pandokia.helpers.easyargs as easyargs
+
try :
import readline
@@ -35,18 +37,20 @@ def main() :
import atexit
atexit.register(readline.write_history_file, history)
+ opt, args = easyargs.get( { '-a' : '--all',
+ '--all' : '-a',
+ } )
+
+ #
#
- all = sys.argv[1] == '-a'
- if all :
- di_nodes = nodes.read_file_list( sys.argv[2:] )
- else :
- di_nodes = nodes.read_file_list( sys.argv[1:] )
+ all = '--all' in opt
+
+ di_nodes = nodes.read_file_list( args )
xnodes = di_nodes.node_index
run_name = str(datetime.datetime.now()).replace(' ','_')
db = steuermann.config.open_db()
- register_database(db, run_name, xnodes)
if all :
run_all(xnodes, run_name, db)
@@ -83,11 +87,16 @@ def do_flag( xnodes, name, recursive, fn, verbose ) :
raise Exception()
def set_want( node ) :
+ # if we said we want it, mark it as wanted and don't skip
node.wanted = 1
node.skip = 0
def set_skip( node ) :
- node.wanted = 0
+ # If we want to skip it, mark it as IS wanted and skip.
+ # Wanted makes us try to run it, then skip makes the run a nop.
+ # This means that stuff that comes after it can run, but only
+ # at the right point in the sequence.
+ node.wanted = 1
node.skip = 1
@@ -119,6 +128,8 @@ pre node show what must come before a node
def run_interactive( xnodes, run_name, db) :
+ register_database(db, run_name, xnodes)
+
runner = run.runner( xnodes, steuermann.config.logdir )
for x in xnodes :
@@ -179,6 +190,15 @@ def run_interactive( xnodes, run_name, db) :
elif n == 'skip' :
cmd_flagging( l, xnodes, set_skip )
+ elif n == 'reset' :
+ print "marking all as not finished"
+ for x in xnodes :
+ xnodes[x].finished = 0
+
+ run_name = str(datetime.datetime.now()).replace(' ','_')
+ print "new run name",run_name
+ register_database(db, run_name, xnodes)
+
elif n == 'list' :
print_all = '-a' in l
l = sorted ( [ x for x in xnodes ] )
@@ -188,10 +208,17 @@ def run_interactive( xnodes, run_name, db) :
if print_all :
print " AFTER", ' '.join([ a.name for a in xnodes[x].predecessors ])
- elif n == 'start' :
- keep_running = 1
-
elif n == 'wait' :
+ c = db.cursor()
+ for x in xnodes :
+ host, tablename, cmd = nodes.crack_name(x)
+ if xnodes[x].wanted :
+ status = 'W'
+ c.execute("UPDATE status SET status = 'W' WHERE run = ? AND host = ? AND tablename = ? AND cmd = ? AND status = 'N'",
+ (run_name, host, tablename, cmd) )
+
+ db.commit()
+
while 1 :
( keep_running, no_sleep ) = run_step( runner, xnodes, run_name, db )
if not keep_running :
@@ -274,6 +301,8 @@ def register_database(db, run, xnodes ) :
def run_all(xnodes, run_name, db) :
+ register_database(db, run_name, xnodes)
+
runner = run.runner( xnodes )
for x in xnodes :
@@ -304,12 +333,6 @@ def run_step( runner, xnodes, run_name, db ) :
# skip nodes that we do not need to consider running because
- # - we explicitly ask to skip it; also mark it finished
- # so that things that come after can run
- if x.skip :
- x.finished = 1
- continue
-
# - it is not wanted
if not x.wanted :
continue
@@ -337,19 +360,29 @@ def run_step( runner, xnodes, run_name, db ) :
if released == len(x.released) :
host, table, cmd = nodes.crack_name(x_name)
- if runner.run(x, run_name) :
- # returns true/false whether it actually ran it - it may not because of resource limits
+ # we are now ready to let it run. If it is marked skipped, just say it ran really fast.
+ if x.skip :
+ x.finished = 1
+ no_sleep = 1
+ keep_running = 1
db.execute("UPDATE status SET start_time = ?, status = 'S' WHERE ( run = ? AND host = ? AND tablename = ? AND cmd = ? )",
- ( str(datetime.datetime.now()), run_name, host, table, cmd ) )
+ ( str(datetime.datetime.now()), run_name, host, table, cmd ) )
db.commit()
+ else :
+ if runner.run(x, run_name) :
+ # returns true/false whether it actually ran it - it may not because of resource limits
+ db.execute("UPDATE status SET start_time = ?, status = 'R' WHERE ( run = ? AND host = ? AND tablename = ? AND cmd = ? )",
+ ( str(datetime.datetime.now()), run_name, host, table, cmd ) )
+ db.commit()
+
# if anything has exited, we process it and update the status in the database
while 1 :
who_exited = runner.poll()
if not who_exited :
break
- # print "SOMETHING EXITED",who_exited
+ print "SOMETHING EXITED",who_exited
# yes, something exited - no sleep, and keep running
no_sleep = 1
keep_running = 1
@@ -357,8 +390,6 @@ def run_step( runner, xnodes, run_name, db ) :
# note who and log it
x_host, x_table, x_cmd = nodes.crack_name(who_exited[0])
- xnodes[who_exited[0]].wanted = 0
-
db.execute("UPDATE status SET end_time = ?, status = ? WHERE ( run = ? AND host = ? AND tablename = ? AND cmd = ? )",
( str(datetime.datetime.now()), who_exited[1], run_name, x_host, x_table, x_cmd ) )
db.commit()
diff --git a/steuermann/specfile.exy b/steuermann/specfile.exy
index 5dc715f..7cdf1f3 100644
--- a/steuermann/specfile.exy
+++ b/steuermann/specfile.exy
@@ -31,10 +31,11 @@ parser specfile:
rule table_section:
TABLE tablename {{ table_name = tablename }}
- HOST
- {{ hostlist = [ ] }}
- ( name {{ hostlist.append(name) }} )+
- command_list
+ HOST {{ hostlist = [ ] }}
+ (
+ name {{ hostlist.append(name) }}
+ )+
+ command_list
# command_list is a list of (command, pos) where command is the text from the file and pos is the location in the file
{{ self.data.add_command_list( table_name, hostlist, command_list ) }}
@@ -42,16 +43,22 @@ parser specfile:
# one or more commands, appended together into a list
{{ cmlist = [ ] }}
command {{ cmlist.append( command ) }}
- [ command_list {{ cmlist += command_list }} ]
+ [
+ command_list {{ cmlist += command_list }}
+ ]
{{ return cmlist }}
-
- | {{ return [ ] }}
+ | {{ return [ ] }}
rule command:
# a single command, including any number of AFTER clauses
- CMD {{ cmd_pos = self._scanner.get_pos() }} cmdname {{ cmd_name=cmdname; script=cmdname; x_after_clause = [ ] }}
- [ RUN string {{ script = string[1:-1]; script_type='r' }} | LOCAL string {{ script = string[1:-1]; script_type='l' }} ]
- ( {{ after_pos = self._scanner.get_pos() }} AFTER optword after_spec {{ x_after_clause.append( (after_spec,optword, after_pos) ) }} )*
+ CMD {{ cmd_pos = self._scanner.get_pos() }}
+ cmdname {{ cmd_name=cmdname; script=cmdname; x_after_clause = [ ] }}
+ [ RUN string {{ script = string[1:-1]; script_type='r' }}
+ | LOCAL string {{ script = string[1:-1]; script_type='l' }}
+ ]
+ ( {{ after_pos = self._scanner.get_pos() }}
+ AFTER optword after_spec {{ x_after_clause.append( (after_spec, optword, after_pos) ) }}
+ )*
{{ return ( cmd_name, script, script_type, x_after_clause, cmd_pos ) }}
# in the AFTER clause, you can say OPT to mean the node is optional (not an error if it does not exist)