send.py revision 1816:ecb6cb1337e8
1#!/usr/bin/env python
2# Copyright (c) 2005 The Regents of The University of Michigan
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met: redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer;
9# redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution;
12# neither the name of the copyright holders nor the names of its
13# contributors may be used to endorse or promote products derived from
14# this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27#
28# Authors: Ali Saidi
29#          Nathan Binkert
30
31import os, os.path, re, socket, sys
32from os import environ as env, listdir
33from os.path import basename, isdir, isfile, islink, join as joinpath, normpath
34from filecmp import cmp as filecmp
35from shutil import copy
36
37def nfspath(dir):
38    if dir.startswith('/.automount/'):
39        dir = '/n/%s' % dir[12:]
40    elif not dir.startswith('/n/'):
41        dir = '/n/%s%s' % (socket.gethostname().split('.')[0], dir)
42    return dir
43
44def syncdir(srcdir, destdir):
45    srcdir = normpath(srcdir)
46    destdir = normpath(destdir)
47    if not isdir(destdir):
48        sys.exit('destination directory "%s" does not exist' % destdir)
49
50    for root, dirs, files in os.walk(srcdir):
51        root = normpath(root)
52        prefix = os.path.commonprefix([root, srcdir])
53        root = root[len(prefix):]
54        if root.startswith('/'):
55            root = root[1:]
56        for rem in [ d for d in dirs if d.startswith('.') or d == 'SCCS']:
57            dirs.remove(rem)
58
59        for entry in dirs:
60            newdir = joinpath(destdir, root, entry)
61            if not isdir(newdir):
62                os.mkdir(newdir)
63                print 'mkdir', newdir
64
65        for i,d in enumerate(dirs):
66            if islink(joinpath(srcdir, root, d)):
67                dirs[i] = joinpath(d, '.')
68
69        for entry in files:
70            dest = normpath(joinpath(destdir, root, entry))
71            src = normpath(joinpath(srcdir, root, entry))
72            if not isfile(dest) or not filecmp(src, dest):
73                print 'copy %s %s' % (dest, src)
74                copy(src, dest)
75
76progpath = nfspath(sys.path[0])
77progname = basename(sys.argv[0])
78usage = """\
79Usage:
80    %(progname)s [-c] [-e] [-f] [-j <jobfile>] [-q queue] [-v] <regexp>
81    -c           clean directory if job can be run
82    -e           only echo pbs command info, don't actually send the job
83    -f           force the job to run regardless of state
84    -q <queue>   submit job to the named queue
85    -j <jobfile> specify the jobfile (default is <basedir>/test.py)
86    -v           be verbose
87
88    %(progname)s [-j <jobfile>] -l [-v] <regexp>
89    -j <jobfile> specify the jobfile (default is <basedir>/test.py)
90    -l           list job names, don't submit
91    -v           be verbose (list job parameters)
92
93    %(progname)s -h
94    -h           display this help
95""" % locals()
96
97try:
98    import getopt
99    opts, args = getopt.getopt(sys.argv[1:], '-cd:efhj:lq:v')
100except getopt.GetoptError:
101    sys.exit(usage)
102
103clean = False
104onlyecho = False
105exprs = []
106force = False
107listonly = False
108queue = ''
109verbose = False
110rootdir = nfspath(os.getcwd())
111jfile = 'test.py'
112for opt,arg in opts:
113    if opt == '-c':
114        clean = True
115    if opt == '-d':
116        rootdir = arg
117    if opt == '-e':
118        onlyecho = True
119    if opt == '-f':
120        force = True
121    if opt == '-h':
122        print usage
123        sys.exit(0)
124    if opt == '-j':
125        jfile = arg
126    if opt == '-l':
127        listonly = True
128    if opt == '-q':
129        queue = arg
130    if opt == '-v':
131        verbose = True
132
133basedir = joinpath(rootdir, 'Base')
134linkdir = joinpath(rootdir, 'Link')
135
136for arg in args:
137    exprs.append(re.compile(arg))
138
139if not listonly and not onlyecho and isdir(linkdir):
140    if verbose:
141        print 'Checking for outdated files in Link directory'
142    syncdir(linkdir, basedir)
143
144import job, jobfile, pbs
145
146test = jobfile.JobFile(joinpath(basedir, jfile))
147
148joblist = []
149for jobname in test.jobs:
150    if not exprs:
151        joblist.append(jobname)
152        continue
153
154    for expr in exprs:
155        if expr.match(jobname):
156            joblist.append(jobname)
157            break
158
159if listonly:
160    if verbose:
161        for jobname in joblist:
162            test.printinfo(jobname)
163    else:
164        for jobname in joblist:
165            print jobname
166    sys.exit(0)
167
168if not onlyecho:
169    jl = []
170    for jobname in joblist:
171        jobdir = joinpath(rootdir, jobname)
172        if os.path.exists(jobname):
173            if not force:
174                if os.path.isfile(joinpath(jobdir, '.success')):
175                    continue
176
177                if os.path.isfile(joinpath(jobdir, '.start')) and \
178                       not os.path.isfile(joinpath(jobdir, '.stop')):
179                    continue
180
181            if not clean:
182                sys.exit('job directory not clean!')
183
184            job.cleandir(jobdir)
185        else:
186            os.mkdir(jobdir)
187        jl.append(jobname)
188    joblist = jl
189
190def setname(jobid, jobname):
191    # since pbs can handle jobnames of 15 characters or less, don't
192    # use the raj hack.
193    if len(jobname) <= 15:
194        return
195
196    import socket
197    s = socket.socket()
198    # Connect to pbs.pool and send the jobid/jobname pair to port
199    # 24465 (Raj didn't realize that there are only 64k ports and
200    # setup inetd to point to port 90001)
201    s.connect(("pbs.pool", 24465))
202    s.send("%s %s\n" % (jobid, jobname))
203    s.close()
204
205for jobname in joblist:
206    jobdir = joinpath(rootdir, jobname)
207
208    if not onlyecho and not os.path.isdir(jobdir):
209        sys.exit('%s is not a directory.  Cannot build job' % jobdir)
210
211    print 'Job name:       %s' % jobname
212    print 'Job directory:  %s' % jobdir
213
214    qsub = pbs.qsub()
215    qsub.pbshost = 'simpool.eecs.umich.edu'
216    qsub.stdout = joinpath(jobdir, 'jobout')
217    qsub.name = jobname[:15]
218    qsub.join = True
219    qsub.node_type = 'FAST'
220    qsub.env['ROOTDIR'] = rootdir
221    qsub.env['JOBNAME'] = jobname
222    if len(queue):
223        qsub.queue = queue
224    qsub.build(joinpath(progpath, 'job.py'))
225
226    if verbose:
227        print 'PBS Command:    %s' % qsub.command
228
229    if not onlyecho:
230        ec = qsub.do()
231        if ec == 0:
232            jobid = qsub.result
233            print 'PBS Jobid:      %s' % jobid
234            setname(jobid, jobname)
235        else:
236            print 'PBS Failed'
237