aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE28
-rw-r--r--MANIFEST.in6
-rw-r--r--README20
-rw-r--r--cdbs.sm13
-rw-r--r--dev.sm27
-rwxr-xr-xgo46
-rw-r--r--init.sm8
-rw-r--r--scripts/smc5
-rw-r--r--scripts/smcron7
-rw-r--r--scripts/steuermann_report.cgi186
-rw-r--r--setup.py50
-rw-r--r--steuermann/db.sql25
-rw-r--r--steuermann/hosts.ini27
-rw-r--r--steuermann/nodes.py4
-rw-r--r--steuermann/report.py15
-rw-r--r--steuermann/run.py57
-rw-r--r--steuermann/run_all.py21
-rw-r--r--steuermann/run_cron.py82
-rw-r--r--x_build.sm80
-rw-r--r--x_dist.sm104
20 files changed, 731 insertions, 80 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..829d1a9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (C) 2011 Association of Universities for Research in Astronomy (AURA)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. The name of AURA and its representatives may not be used to endorse
+or promote products derived from this software without specific prior
+written permission.
+
+THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..87d6d04
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+recursive-include docs
+recursive-include scripts
+recursive-include steuermann
+recursive-include tests
+include go LICENSE Makefile README
+
diff --git a/README b/README
index 8054a62..98ed970 100644
--- a/README
+++ b/README
@@ -1,11 +1,23 @@
Steuermann 2.0
--
-requires exyapps, pandokia
+To install:
-make
+ requires exyapps, pandokia
-python setup.py install
+ make
+ # compiles the parser
+
+ python setup.py install
+ # installs
+
+ Put the installed script steuermann_report.cgi on your web server.
+ It remembers where the rest was installed.
+
+To run:
+
+ requires pandokia
+
+ smc [ -a ] [ -r run_name ] file.sm
-smc [ -a ] [ -r run_name ] file.sm
diff --git a/cdbs.sm b/cdbs.sm
new file mode 100644
index 0000000..dfa9b0f
--- /dev/null
+++ b/cdbs.sm
@@ -0,0 +1,13 @@
+TABLE cdbs HOST bond cadeau
+ CMD local_copy RUN "cdbs_copy"
+ AFTER init/*
+
+TABLE cdbs HOST bond
+ CMD mac_package RUN "/Users/iraf/daily_build/mac_package/make_packages cdbs"
+ AFTER cdbs/local_copy
+
+TABLE cdbs HOST arzach
+ CMD ftp RUN "cdbs_arc"
+ AFTER init/*
+
+
diff --git a/dev.sm b/dev.sm
index cb55c8e..fe20c1c 100644
--- a/dev.sm
+++ b/dev.sm
@@ -6,20 +6,27 @@
# arzach assembles all the source code
TABLE assemble HOST arzach
+
+ CMD svnsync RUN "assemble_svnsync"
+ AFTER init/*
+ AFTER OPT irafx_update
+
CMD dev.stsci_python RUN "assemble_stsci_python dev"
AFTER init/*
+ AFTER svnsync
CMD dev.stsci_iraf RUN "assemble_stsci_iraf dev"
AFTER init/*
+ AFTER svnsync
CMD dev.axe RUN "assemble_axe dev"
AFTER init/*
+ AFTER svnsync
CMD dev.hstcal RUN "assemble_hstcal dev"
AFTER init/*
+ AFTER svnsync
- CMD nop RUN "sleep 1"
- AFTER init/*
####################
####################
@@ -73,7 +80,7 @@ TABLE build HOST thor arzach
AFTER herbert:build/dev.stsci_iraf*
TABLE build HOST cadeau
- CMD dev.stsci_iraf_64hack RUN "build_stsci_iraf_64hack dev cadeau"
+ CMD dev.stsci_iraf_64hack RUN "build_stsci_iraf_64hack dev bond"
AFTER bond:build/dev.stsci_iraf*
# stsci_python documentation
@@ -105,7 +112,7 @@ TABLE distribute HOST herbert thor arzach
AFTER stamp/dev
CMD dev.stsci_iraf RUN "synctool - stsci_iraf_dev"
AFTER stamp/dev
- CMD dev.hstcal RUN "synctool - hstcal_dev"
+ CMD dev.hstcal RUN "synctool - hstcaldev"
AFTER stamp/dev
CMD dev.motd RUN "synctool - irafdev/iraf/unix/hlib/motd"
AFTER distribute/dev.iraf
@@ -124,7 +131,7 @@ TABLE distribute HOST arzach
AFTER stamp/dev
CMD jwcalibdev.stsci_iraf RUN "synctool jwcalibdev: stsci_iraf_dev"
AFTER stamp/dev
- CMD jwcalibdev.hstcal RUN "synctool jwcalibdev: hstcal_dev"
+ CMD jwcalibdev.hstcal RUN "synctool jwcalibdev: hstcaldev"
AFTER stamp/dev
CMD jwcalibdev.motd RUN "synctool jwcalibdev: irafdev/iraf/unix/hlib/motd"
AFTER jwcalibdev.iraf
@@ -138,7 +145,7 @@ TABLE distribute_other HOST arzach
AFTER stamp/dev
CMD goods.stsci_iraf RUN "synctool goods12: stsci_iraf_dev"
AFTER stamp/dev
- CMD goods.hstcal RUN "synctool goods12: hstcal_dev"
+ CMD goods.hstcal RUN "synctool goods12: hstcaldev"
AFTER stamp/dev
CMD goods.motd RUN "synctool goods12: irafdev/iraf/unix/hlib/motd"
AFTER goods.iraf
@@ -152,7 +159,7 @@ TABLE distribute_other HOST arzach
AFTER stamp/dev
CMD witserv1.stsci_iraf RUN "synctool witserv1: stsci_iraf_dev"
AFTER stamp/dev
- CMD witserv1.hstcal RUN "synctool witserv1: hstcal_dev"
+ CMD witserv1.hstcal RUN "synctool witserv1: hstcaldev"
AFTER stamp/dev
CMD witserv1.motd RUN "synctool witserv1: irafdev/iraf/unix/hlib/motd"
AFTER witserv1.iraf
@@ -166,7 +173,7 @@ TABLE distribute_other HOST arzach
AFTER stamp/dev
CMD dmsinsvm.stsci_iraf RUN "synctool dmsinsvm: stsci_iraf_dev"
AFTER stamp/dev
- CMD dmsinsvm.hstcal RUN "synctool dmsinsvm: hstcal_dev"
+ CMD dmsinsvm.hstcal RUN "synctool dmsinsvm: hstcaldev"
AFTER stamp/dev
CMD dmsinsvm.motd RUN "synctool dmsinsvm: irafdev/iraf/unix/hlib/motd"
AFTER dmsinsvm.iraf
@@ -180,7 +187,7 @@ TABLE distribute_other HOST thor
AFTER stamp/dev
CMD udf1.stsci_iraf RUN "synctool udf1: stsci_iraf_dev"
AFTER stamp/dev
- CMD udf1.hstcal RUN "synctool udf1: hstcal_dev"
+ CMD udf1.hstcal RUN "synctool udf1: hstcaldev"
AFTER stamp/dev
CMD udf1.motd RUN "synctool udf1: irafdev/iraf/unix/hlib/motd"
AFTER udf1.iraf
@@ -194,7 +201,7 @@ TABLE distribute_other HOST thor
AFTER stamp/dev
CMD royal.stsci_iraf RUN "synctool royal: stsci_iraf_dev"
AFTER stamp/dev
- CMD royal.hstcal RUN "synctool royal: hstcal_dev"
+ CMD royal.hstcal RUN "synctool royal: hstcaldev"
AFTER stamp/dev
CMD royal.motd RUN "synctool royal: irafdev/iraf/unix/hlib/motd"
AFTER royal.iraf
diff --git a/go b/go
new file mode 100755
index 0000000..4ecadff
--- /dev/null
+++ b/go
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+n=3
+
+rm -rf build
+
+p=`which python`
+
+case "$p"
+in
+/usr/stsci/*) : ;;
+*)
+ echo why is python set to $p
+ echo path = $PATH
+ exit 1
+ ;;
+esac
+
+unset PYTHONPATH
+
+there=/ssbwebv1/data2/steuermann/s$n
+
+rm -rf $there
+
+python setup.py -q install --home $there
+
+rm -f /eng/ssb/websites/ssb/steuermann/s$n.cgi
+
+ln -s $there/bin/steuermann_report.cgi /eng/ssb/websites/ssb/steuermann/s$n.cgi
+
+if grep -q s$n.cgi /eng/ssb/websites/ssb/index.html
+then
+ :
+else
+ id=/eng/ssb/websites/ssb
+
+ sed 's?<\!--STEUERMANN-->?<\!--STEUERMANN--><a href="steuermann/s'$n'.cgi">s'$n'</a> <br> ?' < $id/index.html > tmp
+
+ cp tmp $id/index.html
+ ls -l $id/index.html
+
+fi
+
+chgrp -R ssb $there
+
+
diff --git a/init.sm b/init.sm
index af058f6..f073939 100644
--- a/init.sm
+++ b/init.sm
@@ -1,12 +1,6 @@
-TABLE init HOST bond cadeau arzach thor herbert
+TABLE init HOST bond cadeau arzach thor herbert ssb
CMD sendscripts LOCAL "/eng/ssb/auto/steuermann_scripts/init_sendscripts %(hostname)s %(workdir)s"
CMD sysstat RUN "sysstat"
AFTER sendscripts
-TABLE init HOST arzach
- CMD svnsync RUN "assemble_svnsync"
- AFTER OPT init/irafx_update
- AFTER sendscripts
- AFTER sysstat
-
diff --git a/scripts/smc b/scripts/smc
index 3937855..0fc03f7 100644
--- a/scripts/smc
+++ b/scripts/smc
@@ -1,4 +1,7 @@
#! python
-import steuermann.run_all as run_all
+import sys
+STEUERMANN_DIR_HERE
+sys.path.insert(0, addpath)
+import steuermann.run_all as run_all
run_all.main()
diff --git a/scripts/smcron b/scripts/smcron
new file mode 100644
index 0000000..25a43b0
--- /dev/null
+++ b/scripts/smcron
@@ -0,0 +1,7 @@
+#! python
+import sys
+STEUERMANN_DIR_HERE
+sys.path.insert(0, addpath)
+
+import steuermann.run_cron as run_cron
+run_cron.main()
diff --git a/scripts/steuermann_report.cgi b/scripts/steuermann_report.cgi
index abf371b..6953b0d 100644
--- a/scripts/steuermann_report.cgi
+++ b/scripts/steuermann_report.cgi
@@ -6,6 +6,7 @@ import os
import sys
import re
import datetime
+import pandokia.text_table
STEUERMANN_DIR_HERE
@@ -18,6 +19,25 @@ cgitb.enable()
form = cgi.FieldStorage(keep_blank_values=1)
cginame = os.getenv("SCRIPT_NAME")
+permission_modify=1
+
+html_header='''Content-type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+<TITLE>%(title)s</TITLE>
+</HEAD>
+<BODY>
+'''
+
+html_trailer='''
+</BODY>
+</HTML>
+'''
+
+##########
+
def sqltime(arg) :
if arg is None :
return None
@@ -36,30 +56,169 @@ def sqltime(arg) :
##########
+
+def normalize_run_name(db, name) :
+
+ if name == 'daily_latest' :
+ c = db.execute("SELECT max(run) FROM sm_runs WHERE run like 'daily_%'")
+ run, = c.fetchone()
+ return run
+
+ return name
+
+##########
# if no action specified, show the list of runs
#
-if not 'action' in form :
- print 'content-type: text/html'
- print ''
+if 'action' in form :
+ action = form['action'].value
+else :
+ action = 'index'
+
+if action == 'index' :
+ print html_header % { 'title' : 'Steuermann' }
+ print "<a href=%s?action=runs>runs</a><br>"%cginame
+ print "<a href=%s?action=crons>crons</a><br>"%cginame
+ print html_trailer
+ sys.exit(0)
+
+
+##########
+# list the runs
+
+elif action == 'crons' :
+ print html_header % { 'title' : 'Steuermann List' }
+ db = steuermann.config.open_db()
+ c = db.cursor()
+
+ c.execute("SELECT host, name, decollision, start_time, end_time, duration, status FROM sm_crons ORDER BY start_time desc")
+
+ tt = pandokia.text_table.text_table()
+ tt.set_html_table_attributes("border=1")
+ tt.define_column('host')
+ tt.define_column('name')
+ tt.define_column('start_time')
+ tt.define_column('end_time')
+ tt.define_column('duration')
+ tt.define_column('status')
+ if permission_modify :
+ tt.define_column('delete')
+
+ link="%s?action=%s&host=%s&name=%s&decol=%s"
+ for host, name, decollision, start_time, end_time, duration, status in c :
+ row = tt.get_row_count()
+ short_name = name.split('--')[0]
+ tt.set_value(row, 'host', host )
+ tt.set_value(row, 'name', text=short_name, link=link%(cginame,'cronlog',host,name,decollision) )
+ tt.set_value(row, 'start_time', start_time)
+ if end_time is None :
+ end_time = ''
+ if end_time.startswith(start_time[0:11]) :
+ tt.set_value(row, 'end_time', end_time[11:])
+ else :
+ tt.set_value(row, 'end_time', end_time)
+ tt.set_value(row, 'duration', duration)
+ tt.set_value(row, 'status', status)
+ if permission_modify :
+ tt.set_value(row, 'delete', 'arf')
+
+ print tt.get_html()
+ print html_trailer
+ sys.exit(0)
+
+elif action == 'runs' :
+ print html_header % { 'title' : 'Steuermann List' }
+ print 'sort: '
+ for x in ( 'name_asc', 'name_desc', 'time_asc', 'time_desc' ) :
+ print "<a href=%s?action=runs&order=%s>%s</a>"%(cginame,x,x)
+ print "<br><br>"
db = steuermann.config.open_db()
c = db.cursor()
- c.execute('SELECT DISTINCT run FROM sm_status ORDER BY run DESC')
- for run, in c :
- print "<a href=%s?action=status&run=%s>%s</a><br>"%(cginame, run, run)
+ order='create_time DESC'
+
+ if 'order' in form :
+ x = form['order'].value
+ if x == 'name_asc' :
+ order = 'run ASC'
+ elif x == 'name_desc' :
+ order = 'run DESC'
+ elif x == 'time_asc' :
+ order ='create_time ASC'
+ elif x == 'time_desc' :
+ order ='create_time DESC'
+
+ c.execute('SELECT DISTINCT run, create_time FROM sm_runs ORDER BY %s'%order)
+ print "<table>"
+ for run, create_time in c :
+ print "<tr>"
+ print "<td>"
+ print "<a href=%s?action=status&run=%s>%s</a>"%(cginame, run, run)
+ print "</td><td>"
+ if permission_modify :
+ print "<a href=%s?action=delete&run=%s>delete</a>"%(cginame, run)
+ print "</td>"
+ print "</tr>"
+ print "</table>"
+ print html_trailer
sys.exit(0)
-action = form['action'].value
##########
# status means show the status of a particular run
#
-if action == 'status' :
+elif action == 'delete' :
+ if permission_modify :
+ print 'content-type: text/plain\n'
+ in_run = form['run'].value
+ db = steuermann.config.open_db()
+ in_run = normalize_run_name(db,in_run)
+ c = db.cursor()
+ c1 = db.cursor()
+ print "RUN=",in_run
+ c.execute("SELECT run FROM sm_runs WHERE run LIKE ?",(in_run,))
+ for run, in c :
+ print "echo run ",run
+ filename = '%s/run/%s'%(steuermann.config.logdir,run)
+ print "rm -rf ",filename
+ c.execute("DELETE FROM sm_runs WHERE run LIKE ?",(in_run,))
+ db.commit()
+ sys.exit(0)
+
+##########
+#
+
+elif action == 'status' :
db = steuermann.config.open_db()
import steuermann.report
steuermann.report.cginame = cginame
- print 'content-type: text/html'
+ print html_header % { 'title' : 'Steuermann Status' }
print ''
run = form['run'].value
+ run = normalize_run_name(db,run)
print steuermann.report.report_html( db, run, info_callback=steuermann.report.info_callback_gui )
+ print html_trailer
+ sys.exit(0)
+
+
+elif action == 'cronlog' :
+ print 'content-type: text/plain'
+ print ''
+ host = form['host'].value
+ name = form['name'].value
+ decol = form['decol'].value
+ db = steuermann.config.open_db()
+ c = db.cursor()
+
+ c.execute("SELECT host, name, decollision, start_time, end_time, status, logfile FROM sm_crons WHERE host = ? AND name = ? and decollision = ?", (host, name, decol) )
+ for host, name, decollision, start_time, end_time, status, logfile in c :
+ print "----------"
+ print "%s: %s\n"%(host,name)
+ print "decol=%s"%decollision
+ print start_time
+ print end_time
+ print "status=",status
+ print "----------"
+ f=open(steuermann.config.logdir + '/cron/' + logfile,"r")
+ sys.stdout.write(f.read())
+ f.close()
sys.exit(0)
##########
@@ -103,7 +262,7 @@ elif action == 'log' :
for x in [ ' ' + x for x in notes.split('\n') ] :
print x
print ""
- filename = '%s/%s/%s:%s.%s.log'%(steuermann.config.logdir,run,host,table,cmd)
+ filename = '%s/run/%s/%s:%s.%s.log'%(steuermann.config.logdir,run,host,table,cmd)
try :
f=open(filename,'r')
except IOError:
@@ -124,7 +283,7 @@ elif action == 'log' :
# info means show information about the system
#
elif action == 'info' :
- print 'content-type: text/html\n'
+ print html_header % { 'title': 'Steuermann Info' }
print 'db credentials: ',steuermann.config.db_creds,'<br>'
print 'logdir: ',steuermann.config.logdir,'<br>'
db = steuermann.config.open_db()
@@ -135,11 +294,12 @@ elif action == 'info' :
cur.execute("select count(*) from sm_runs")
l = cur.fetchone()
print "runs: %s\n"%l[0],'<br>'
+ print html_trailer
sys.exit(0)
##########
-print 'content-type: text/html'
+print html_header
print ''
print 'no recognized action?'
-
+print html_trailer
diff --git a/setup.py b/setup.py
index 167baec..e79d1e8 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,38 @@
import distutils.core
+long_desc = '''
+Steuermann is a control system for continuous integration. You write
+one or more config files defining processes to run on various computers
+along with dependencies.
+
+For example, you can declare that task A runs on computer X, but only
+after task B runs (to completion) on computer Y.
+
+You can invoke steuermann in automatic mode to run all the processes in
+the necessary order. This is the way to perform automatic builds/tests.
+
+You can invoke steuermann manually to select a subset of processes
+to execute. This is a way to repeat steps without running everything
+again, for example to repeat a failed portion of an automatic build/test
+sequence.
+
+'''
+
+classifiers = [
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Console',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: BSD License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python :: 2',
+ 'Topic :: Software Development :: Build Tools',
+ 'Topic :: System :: Distributed Computing',
+ ]
+
+#
f=open('steuermann/__init__.py','r')
for x in f :
if x.startswith('__version__') :
@@ -7,22 +40,25 @@ for x in f :
break
f.close()
-print version
-
-command_list = [ 'smc', 'steuermann_report.cgi' ]
+#
+command_list = [ 'smc', 'smcron', 'steuermann_report.cgi' ]
use_usr_bin_env = [ ]
dir_set = 'addpath = "%s"\n'
args = {
- 'version' : "2.0dev",
+ 'name': 'steuermann',
+ 'version' : version,
'description' : "Steuermann Continuous Integration Control System",
+ 'long_description': long_desc,
'author' : "Mark Sienkiewicz",
- 'scripts' : ['scripts/smc', 'scripts/steuermann_report.cgi'],
- 'package_dir' : { 'steuermann' : 'steuermann', },
+ 'author_email' : "help@stsci.edu",
'url' : 'https://svn.stsci.edu/trac/ssb/etal/wiki/Steuermann',
+ 'scripts' : ['scripts/' + x for x in command_list ],
+ 'package_dir' : { 'steuermann' : 'steuermann', },
'license': 'BSD',
'packages': [ 'steuermann' ],
- 'package_data': { 'steuermann' : [ '*.sql', '*.ini', ], }
+ 'package_data': { 'steuermann' : [ '*.sql', '*.ini', ], },
+ 'classifiers': classifiers,
}
d = distutils.core.setup(
diff --git a/steuermann/db.sql b/steuermann/db.sql
index b6d2ae6..99bc752 100644
--- a/steuermann/db.sql
+++ b/steuermann/db.sql
@@ -43,8 +43,31 @@ create unique index sm_status_idx1 on sm_status ( run, host, tablename, cmd );
-- table lists all run names in the system
CREATE TABLE sm_runs (
- run VARCHAR(100)
+ run VARCHAR(100),
+ create_time VARCHAR(26)
);
CREATE UNIQUE INDEX sm_runs_idx1 ON sm_runs(run);
+
+-- table lists scheduled cron events - sort of like the old sr
+-- smcron name script
+CREATE TABLE sm_crons (
+ host VARCHAR,
+ -- what host we ran this on
+ name VARCHAR(100),
+ -- a descriptive name, not necessarily unique
+ decollision VARCHAR(10),
+ -- a cookie to make (host,name,decollision) unique
+ start_time VARCHAR(30),
+ end_time VARCHAR(30),
+ duration REAL,
+ --
+ status VARCHAR(5),
+ -- exit status
+ logfile VARCHAR(1000)
+ -- log file name, relative to config logdir+'/cron/'
+ );
+
+CREATE INDEX sm_crons_idx1 ON sm_crons( host, name, decollision );
+
diff --git a/steuermann/hosts.ini b/steuermann/hosts.ini
index f8c2e3b..abc114c 100644
--- a/steuermann/hosts.ini
+++ b/steuermann/hosts.ini
@@ -28,12 +28,15 @@ local=[ 'sh', '-c', '%(script)s' ]
;
; -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 ' ]
+run=[ 'ssh', '-q', '-x', '%(hostname)s', ' cd %(workdir)s; setenv sm_node %(node)s; setenv sm_run %(runname)s; source bin/.steuermann.%(hostname)s; %(script)s; show_status $status ' ]
like=all
[mac:csh]
like=linux:csh
+[solaris:csh]
+like=linux:csh
+
[windows:cmd]
run=[ 'python', '-m', 'steuermann.windows_comm', '%(hostname)s', '%(script)s' ]
like=all
@@ -59,6 +62,12 @@ like=cadeau
; actual machines
+[jwcalibdev]
+hostname=jwcalibdev
+like=linux:csh
+workdir=/data1/iraf/steuermann
+maxproc=32
+
[herbert]
hostname=herbert
like=linux:csh
@@ -77,6 +86,9 @@ like=linux:csh
workdir=/arzach/data1/iraf/steuermann
maxproc=4
+[localhost]
+like=ssb
+
[ssb]
hostname=ssbwebv1
like=linux:csh
@@ -101,5 +113,18 @@ like=mac:csh
workdir=/Users/iraf/work/steuermann
maxproc=4
+[aten]
+hostname=aten
+like=solaris:csh
+maxproc=1
+workdir=/aten/data2/iraf/steuermann
+
+[grail]
+hostname=grail
+like=solaris:csh
+maxproc=1
+workdir=/data/grail1/iraf/steuermann
+
+
; There is a section [ALL] that is used with every machine name
[ALL]
diff --git a/steuermann/nodes.py b/steuermann/nodes.py
index 0f24e36..476734a 100644
--- a/steuermann/nodes.py
+++ b/steuermann/nodes.py
@@ -212,6 +212,10 @@ class node(object) :
self.finished = 0
self.running = 0
+ #
+ self.wanted = 1
+ self.skip = 1
+
#####
# debug - make a string representation of all the nodes
diff --git a/steuermann/report.py b/steuermann/report.py
index cc90604..8d95d1f 100644
--- a/steuermann/report.py
+++ b/steuermann/report.py
@@ -11,18 +11,6 @@ import StringIO
# maybe the output is html 3.2 - in any case, it is way simpler than
# more recent standards.
-html_header='''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<HTML>
-<HEAD>
-<TITLE>%(title)s</TITLE>
-</HEAD>
-<BODY>
-'''
-
-html_trailer='''
-</BODY>
-</HTML>
-'''
# this will be reset by the cgi main program if we are in a real cgi
cginame = 'arf.cgi'
@@ -167,7 +155,6 @@ def report_text( db, run_name, info_callback = info_callback_status ) :
def report_html( db, run_name, info_callback = info_callback_status, hlevel=1 ) :
s = StringIO.StringIO()
- s.write(html_header % { 'title' : run_name } )
s.write('<h%d>%s</h%d>\n'%(hlevel,run_name,hlevel))
hlevel = hlevel + 1
@@ -179,8 +166,6 @@ def report_html( db, run_name, info_callback = info_callback_status, hlevel=1 )
t = get_table( db, run_name, tablename, info_callback, showdepth=1 )
s.write(t.get_html())
- s.write(html_trailer)
-
return s.getvalue()
#
diff --git a/steuermann/run.py b/steuermann/run.py
index af52a02..e83fca2 100644
--- a/steuermann/run.py
+++ b/steuermann/run.py
@@ -3,12 +3,32 @@ run processes asynchronously on various machines, with a callback
on process exit.
'''
+# to do someday:
+#
+# This feature should really be broken into 3 parts:
+# - remotely execute on another machine
+# - track concurrent execution
+# - reserve resource usage
+#
+# To start a process, ask for a resource reservation. (Currently, the
+# only resource we track is CPUs.) If we don't get a reservation, we
+# don't run right away.
+#
+# If we do, use the remote exec to run the process on the target machine.
+# This is the part that knows hosts.ini. (We also use hosts.ini to declare
+# resource availability.)
+#
+# When the process finishes, release the resource reservation.
+#
+
import subprocess
import time
import datetime
import os
+import os.path
import traceback
import sys
+import errno
import ConfigParser
@@ -56,7 +76,7 @@ class runner(object):
#####
# start a process
- def run( self, node, run_name, no_run = False ):
+ def run( self, node, run_name, no_run = False, logfile_name = None ):
try :
try :
@@ -72,14 +92,15 @@ class runner(object):
n = int(self.howmany.get(hostname,0))
if n >= int(args['maxproc']) :
- print "decline to run %s - %d other already running"%(node.name,n)
+ # print "decline to run %s - %d other already running"%(node.name,n)
return False
n = n + 1
self.howmany[hostname] = n
- print "running %s %s %d"%(hostname,node.name, n)
+ # print "running %s %s %d"%(hostname,node.name, n)
else :
- print "running %s %s no maxproc"%(hostname, node.name)
+ # print "running %s %s no maxproc"%(hostname, node.name)
+ pass
if debug :
print "run",node.name
@@ -96,6 +117,7 @@ class runner(object):
table=node.table,
cmd=node.cmd,
node=node.name,
+ runname=run_name,
)
if debug :
@@ -122,15 +144,20 @@ class runner(object):
if debug :
print "RUN",run
- # make sure the log directory is there
- logdir= self.logdir + "/%s"%run_name
- try :
- os.makedirs(logdir)
- except OSError:
- pass
+ if logfile_name is None :
+ # make sure the log directory is there
+ logdir= self.logdir + "/%s"%(run_name)
- # create a name for the log file, but do not use / in the name
- logfile_name = "%s/%s.log"%( logdir, node.name.replace('/','.') )
+ # create a name for the log file, but do not use / in the name
+ logfile_name = "%s/%s.log"%( logdir, node.name.replace('/','.') )
+
+ try :
+ os.makedirs( os.path.dirname(logfile_name) )
+ except OSError, e :
+ if e.errno == errno.EEXIST :
+ pass
+ else :
+ raise
# open the log file, write initial notes
logfile=open(logfile_name,"w")
@@ -142,6 +169,8 @@ class runner(object):
run = [ 'echo', 'no_run - node=', node.name ]
# start running the process
+ if debug :
+ print "RUN",run
p = subprocess.Popen(args=run,
stdout=logfile,
stderr=subprocess.STDOUT,
@@ -177,7 +206,8 @@ class runner(object):
n = self.howmany[hostname] - 1
self.howmany[hostname] = n
- print "finish %s %s %d"%(hostname,node_name,n)
+ if debug :
+ print "finish %s %s %d"%(hostname,node_name,n)
# note the termination of the process at the end of the log file
logfile = self.all_procs[node_name].logfile
@@ -270,7 +300,6 @@ class runner(object):
d1 = d1.copy()
d1.update(d)
d = d1
- print d
del d['like']
# default hostname is the name from the section header
diff --git a/steuermann/run_all.py b/steuermann/run_all.py
index f670abe..1e179a2 100644
--- a/steuermann/run_all.py
+++ b/steuermann/run_all.py
@@ -11,6 +11,7 @@ import datetime
import run
import report
import nodes
+import getpass
import steuermann.config
@@ -22,6 +23,9 @@ try :
except ImportError :
readline = None
+
+username=getpass.getuser()
+
#####
def main() :
@@ -68,7 +72,7 @@ def main() :
if '-r' in opt :
run_name = opt['-r']
else :
- run_name = str(datetime.datetime.now()).replace(' ','_')
+ run_name = "user_%s_%s"%(username,str(datetime.datetime.now()).replace(' ','_'))
db = steuermann.config.open_db()
@@ -348,7 +352,7 @@ def print_pre(who, xnodes, depth) :
def register_database(db, run, xnodes ) :
c = db.cursor()
- c.execute('INSERT INTO sm_runs ( run ) VALUES ( ? )', ( run, ) )
+ c.execute('INSERT INTO sm_runs ( run, create_time ) VALUES ( ?, ? )', ( run, str(datetime.datetime.now()).replace(' ','_')) )
c = db.cursor()
for x in xnodes :
@@ -363,14 +367,17 @@ def register_database(db, run, xnodes ) :
def run_all(xnodes, run_name, db) :
+ for x in xnodes :
+ x = xnodes[x]
+ x.finished = 0
+ x.running = 0
+ x.wanted = 1
+ x.skip = 0
+
register_database(db, run_name, xnodes)
- runner = run.runner( xnodes )
+ runner = run.runner( xnodes, steuermann.config.logdir )
- for x in xnodes :
- xnodes[x].finished = 0
- xnodes[x].running = 0
- xnodes[x].wanted = 1
while 1 :
( keep_running, no_sleep ) = run_step( runner, xnodes, run_name, db )
diff --git a/steuermann/run_cron.py b/steuermann/run_cron.py
new file mode 100644
index 0000000..39d5be7
--- /dev/null
+++ b/steuermann/run_cron.py
@@ -0,0 +1,82 @@
+import os
+import sys
+import time
+import steuermann.config
+import steuermann.run
+import datetime
+
+# bugs:
+# - this is a hideous hack that works around run.py not being designed for this
+# - the work directory on the target machine is poorly named
+#
+
+class fakenode(object):
+ pass
+
+def main() :
+
+ if len(sys.argv) < 4 :
+ print "smcron host name command"
+ print "(from ",sys.argv,")"
+ sys.exit(1)
+
+ host = sys.argv[1]
+ name = sys.argv[2]
+ script = ' '.join(sys.argv[3:])
+
+ db = steuermann.config.open_db()
+
+ c = db.cursor()
+
+ start_time = datetime.datetime.now()
+ day, tod = str(start_time).split(' ')
+
+ name = name + '--' + tod
+
+ logfile = '%s/%s.%s' % (day, host, name)
+
+ # something to reduce the possibility of a collision to very small - in this case, we assume that
+ # we can't start multiple processes at the same time from the same process number. i.e. the
+ # pid can not wrap in less than 1 clock tick
+ decollision = os.getpid()
+
+ c.execute("INSERT INTO sm_crons ( host, name, start_time, logfile, decollision ) VALUES ( ?, ?, ?, ?, ? )",
+ ( host, name, str(start_time), logfile, decollision ) )
+ db.commit()
+
+ # this is a hideous hack to plug on to an interface that was not designed for this
+
+ node = fakenode()
+ node.host = host
+ node.name = 'cron.' + name
+ node.table = None
+ node.cmd = None
+ node.script = script
+ if host == 'localhost' :
+ node.script_type = 'l' # local
+ else :
+ node.script_type = 'r' # remote
+
+ runner = steuermann.run.runner( nodes = { node.name : node }, logdir=None )
+ runner.run( node=node, run_name='', logfile_name = steuermann.config.logdir + '/cron/' + logfile )
+
+ n = 0.1
+ while 1 :
+ exited = runner.poll()
+ if exited :
+ break
+ if n < 2.0 :
+ n = n * 2.0
+ time.sleep(n)
+
+ status = exited[1]
+
+ end_time = datetime.datetime.now()
+ td = end_time - start_time
+
+ # only 1 decimal place because we don't poll often enough to make more reasonable.
+ td = "%.1f" % ( td.microseconds/1e6 + td.seconds + td.days * 24 * 3600 )
+ c.execute("UPDATE sm_crons SET end_time = ?, status = ?, duration = ? WHERE host = ? AND name = ? ",
+ (str(end_time), status, td, host, name ) )
+ db.commit()
+
diff --git a/x_build.sm b/x_build.sm
new file mode 100644
index 0000000..d5702ac
--- /dev/null
+++ b/x_build.sm
@@ -0,0 +1,80 @@
+## TODO:
+## add builds on ssbwebv1 for those things that we actually care about
+
+####################
+####################
+
+# arzach assembles all the source code
+TABLE assemble HOST arzach
+
+ CMD svnsync RUN "assemble_svnsync"
+ AFTER init/*
+ AFTER OPT irafx_update
+
+ CMD x.stsci_python RUN "assemble_stsci_python x"
+ AFTER init/*
+ AFTER svnsync
+
+ CMD x.stsci_iraf RUN "assemble_stsci_iraf x"
+ AFTER init/*
+ AFTER svnsync
+
+ CMD x.axe RUN "assemble_axe x"
+ AFTER init/*
+ AFTER svnsync
+
+ CMD x.hstcal RUN "assemble_hstcal x"
+ AFTER init/*
+ AFTER svnsync
+
+
+####################
+####################
+
+# install stsci_python into default environment
+# build hstcal
+# - everywhere
+TABLE build HOST herbert thor arzach bond cadeau
+ CMD x.py2.7 RUN "build_stsci_python x 2.7"
+ AFTER init/*
+ AFTER *:assemble/x.stsci_python
+ CMD x.hstcal RUN "build_hstcal x"
+ AFTER init/*
+ AFTER *:assemble/x.hstcal
+
+# stsdas and friends
+# - 32 bit only
+TABLE build HOST herbert bond
+ CMD x.axe RUN "build_axe x"
+ AFTER init/*
+ AFTER *:assemble/x.axe
+
+ CMD x.stsci_iraf RUN "build_stsci_iraf x"
+ AFTER init/*
+ AFTER *:assemble/x.stsci_iraf
+ AFTER build/x.axe
+
+ CMD x.stsci_iraf_log RUN "build_stsci_iraf_log x"
+ AFTER init/*
+ AFTER build/x.stsci_iraf
+
+ CMD x.stsci_iraf_help RUN "build_stsci_iraf_help x"
+ AFTER init/*
+ AFTER build/x.stsci_iraf
+
+
+# stsdas for 64 bit machines - get it from a related 32 bit system
+TABLE build HOST thor arzach
+ CMD x.stsci_iraf_64hack RUN "build_stsci_iraf_64hack x herbert"
+ AFTER herbert:build/x.stsci_iraf*
+
+TABLE build HOST cadeau
+ CMD x.stsci_iraf_64hack RUN "build_stsci_iraf_64hack x bond"
+ AFTER bond:build/x.stsci_iraf*
+
+
+# stamp the IRAF banner file when the builds are complete
+TABLE stamp HOST herbert thor arzach bond cadeau
+ CMD x RUN "build_stamp x"
+ AFTER build/*
+
diff --git a/x_dist.sm b/x_dist.sm
new file mode 100644
index 0000000..5218a9b
--- /dev/null
+++ b/x_dist.sm
@@ -0,0 +1,104 @@
+
+# regular distributions
+
+TABLE distribute HOST herbert thor arzach
+ CMD x.iraf RUN "synctool - irafx"
+ AFTER stamp/x
+ CMD x.pyssg RUN "synctool - pyssgx"
+ AFTER stamp/x
+ CMD x.stsci_iraf RUN "synctool - stsci_iraf_x"
+ AFTER stamp/x
+ CMD x.hstcal RUN "synctool - hstcal_x"
+ AFTER stamp/x
+ CMD x.motd RUN "synctool - irafx/iraf/unix/hlib/motd"
+ AFTER distribute/x.iraf
+
+TABLE distribute HOST bond cadeau
+ CMD irafx.pkg RUN "cd $HOME/daily_build/mac_package; ./clean ; ./build x " AFTER stamp/x
+ CMD irafx.dmg RUN "cd $HOME/daily_build/mac_package; ./distribute x" AFTER irafx.pkg
+
+# wads of special cases
+
+# jwcalibdev has local disk - some day it may do its own builds
+TABLE distribute HOST arzach
+ CMD jwcalibdev.iraf RUN "synctool jwcalibdev: irafx"
+ AFTER stamp/x
+ CMD jwcalibdev.pyssg RUN "synctool jwcalibdev: pyssgx"
+ AFTER stamp/x
+ CMD jwcalibdev.stsci_iraf RUN "synctool jwcalibdev: stsci_iraf_x"
+ AFTER stamp/x
+ CMD jwcalibdev.hstcal RUN "synctool jwcalibdev: hstcalx"
+ AFTER stamp/x
+ CMD jwcalibdev.motd RUN "synctool jwcalibdev: irafx/iraf/unix/hlib/motd"
+ AFTER jwcalibdev.iraf
+
+# goods - has RHE 5 only now
+
+TABLE distribute_other HOST arzach
+ CMD goods.iraf RUN "synctool goods12: irafx"
+ AFTER stamp/x
+ CMD goods.pyssg RUN "synctool goods12: pyssgx"
+ AFTER stamp/x
+ CMD goods.stsci_iraf RUN "synctool goods12: stsci_iraf_x"
+ AFTER stamp/x
+ CMD goods.hstcal RUN "synctool goods12: hstcalx"
+ AFTER stamp/x
+ CMD goods.motd RUN "synctool goods12: irafx/iraf/unix/hlib/motd"
+ AFTER goods.iraf
+
+# witserv1 - who are these guys?
+
+TABLE distribute_other HOST arzach
+ CMD witserv1.iraf RUN "synctool witserv1: irafx"
+ AFTER stamp/x
+ CMD witserv1.pyssg RUN "synctool witserv1: pyssgx"
+ AFTER stamp/x
+ CMD witserv1.stsci_iraf RUN "synctool witserv1: stsci_iraf_x"
+ AFTER stamp/x
+ CMD witserv1.hstcal RUN "synctool witserv1: hstcalx"
+ AFTER stamp/x
+ CMD witserv1.motd RUN "synctool witserv1: irafx/iraf/unix/hlib/motd"
+ AFTER witserv1.iraf
+
+# dmsinsvm - have a pipeline and irafx/irafdev on the same machine for INS
+
+TABLE distribute_other HOST arzach
+ CMD dmsinsvm.iraf RUN "synctool dmsinsvm: irafx"
+ AFTER stamp/x
+ CMD dmsinsvm.pyssg RUN "synctool dmsinsvm: pyssgx"
+ AFTER stamp/x
+ CMD dmsinsvm.stsci_iraf RUN "synctool dmsinsvm: stsci_iraf_x"
+ AFTER stamp/x
+ CMD dmsinsvm.hstcal RUN "synctool dmsinsvm: hstcalx"
+ AFTER stamp/x
+ CMD dmsinsvm.motd RUN "synctool dmsinsvm: irafx/iraf/unix/hlib/motd"
+ AFTER dmsinsvm.iraf
+
+# UDF - another funded project with their own machines
+
+TABLE distribute_other HOST thor
+ CMD udf1.iraf RUN "synctool udf1: irafx"
+ AFTER stamp/x
+ CMD udf1.pyssg RUN "synctool udf1: pyssgx"
+ AFTER stamp/x
+ CMD udf1.stsci_iraf RUN "synctool udf1: stsci_iraf_x"
+ AFTER stamp/x
+ CMD udf1.hstcal RUN "synctool udf1: hstcal_x"
+ AFTER stamp/x
+ CMD udf1.motd RUN "synctool udf1: irafx/iraf/unix/hlib/motd"
+ AFTER udf1.iraf
+
+# royal - a beowulf cluster
+
+TABLE distribute_other HOST thor
+ CMD royal.iraf RUN "synctool royal: irafx"
+ AFTER stamp/x
+ CMD royal.pyssg RUN "synctool royal: pyssgx"
+ AFTER stamp/x
+ CMD royal.stsci_iraf RUN "synctool royal: stsci_iraf_x"
+ AFTER stamp/x
+ CMD royal.hstcal RUN "synctool royal: hstcalx"
+ AFTER stamp/x
+ CMD royal.motd RUN "synctool royal: irafx/iraf/unix/hlib/motd"
+ AFTER royal.iraf
+