#!/usr/bin/env python
#\if DOXYGEN_IGNORE ############################################################
# #
# Copyright (C) 2016 by John Spitzak #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
# #
#\endif ########################################################################
################################################################################
#\defgroup difxnewExperiment DiFXnewExperiment
#
#\brief Create a new DiFX experiment on the DiFX server.
#
# Usage: DiFXnewExperiment [options] directory
#
# DiFXnewExperiment creates a new DiFX experiment in a specified directory
# location on the DiFX server. It will create jobs using vex2difx, then
# run the "calc" process on each job to prepare them to be run by DiFX. Nominally
# it requires valid .v2d and .vex files as well as a path where the experiments will be created
# on the server, but behavior varies depending on what information you give it.
#
# DiFXnewExperiment is meant to operate in a defined manner - jobs are expected
# to be created in the specified directory. When run in the "nominal" mode, where
# a .vex, .v2d, and experiment path are created, it will alter the .v2d file content
# to assure that jobs are created in the defined path (the "vex" path in the .v2d
# file determines this). The subsequent calc operations will then work. If you
# "fool" it by using one of the modes below where the .v2d file is not altered, you
# can possibly get it to produce jobs elsewhere, but then the calc operation will
# not be performed.
#
#
Modes of Operation
#
# If you provide a directory, a .vex file path and a .v2d file path (the
# .vex file can be specified using either the "-vex" or "-svex" argument, the .v2d file is
# specified using either the "-v2d" or "-sv2d" argument, and the directory is the final
# argument), DiFXnewExperiment will create the directory (if necessary) and copy the
# .v2d file and .vex files into it. The .v2d file will be altered to have the proper
# path to the new copy of the .vex file. Jobs will be created in this new directory.
#
# An example creating a new experiment using a .vex file and .v2d file on the DiFX server.
# The .vex file has three scans, each of which creates a job. The specified directory
# ("/data1/correlator/newExp") is created and the .vex and .v2d files are copied to it.
# The .v2d file is altered to point to the new "local" .vex copy. Then .input files are created
# by vex2difx, after which the calc process is run to create .im files:
#\code{.py}
#./DiFXnewExperiment --v2d /home/oper/April2016.v2d --vex /home/oper/April2016.vex /data1/correlator/newExp
#Creating directory "/data1/correlator/newExp".
#/data/correlator/newExp/April2016_01.input was created
#/data/correlator/newExp/April2016_02.input was created
#/data/correlator/newExp/April2016_03.input was created
#/data/correlator/newExp/April2016_01.im was created
#/data/correlator/newExp/April2016_02.im was created
#/data/correlator/newExp/April2016_03.im was created
#\endcode
#
# The following command would operate in a similar way, except the .v2d file would come
# from a "local" (i.e. on the same machine as the client, not the DiFX server) file path
# (the .vex file MUST be on the DiFX server however):
#\code{.py}
#./DiFXnewExperiment --sv2d /home/local/April2016.v2d --vex /home/oper/April2016.vex /data1/correlator/newExp
#...
#\endcode
#
# If you provide only a .v2d file path and a directory the directory will be created
# containing a copy of the .vex file specified in the .v2d, along with a new .v2d file that
# has the vex specification altered to point to the new copy.
#
# In this example, the .vex file contained in the specified .v2d file copied to the new
# directory. If the .vex specification in the .v2d is not a full path, which is likely,
# a full path will be created by appending the given path to the path of the .v2d file - this
# will only work if the .v2d file is remote (a local .v2d file must contain the full path to
# the .vex already):
#\code{.py}
#./DiFXnewExperiment --v2d /home/oper/April2016.v2d /data1/correlator/newExp
#Creating directory "/data1/correlator/newExp".
#Using "/home/oper/April2016.vex" as .vex file source.
#/data/correlator/newExp/April2016_01.input was created
#/data/correlator/newExp/April2016_02.input was created
#/data/correlator/newExp/April2016_03.input was created
#/data/correlator/newExp/April2016_01.im was created
#/data/correlator/newExp/April2016_02.im was created
#/data/correlator/newExp/April2016_03.im was created
#\endcode
#
# If you provide only a .vex file and a directory the directory will be created
# along with a newly-created .v2d file that has only the "vex" specification in it. This will work
# fine if the .vex file contains all information required for vex2difx to work.
#
# If you provide a directory and a .v2d file name without path information
# (using only the "--v2d" option for a remote .v2d file name - a local file name
# will not work), DiFXnewExperiment will assume the directory exists and already
# contains the .v2d file.
#
# If you provide a .v2d file path but no directory DiFXnewExperiment
# will create jobs using the .v2d file in the directory given by the .v2d file's
# full path.
#
# If you provide only a directory DiFXnewExperiment will search that
# directory for an existing .v2d file and use it to create jobs.
#
# If instead of a directory you provide a full path to a .v2d file (not using
# the "--v2d" option) DiFXnewExperiment will use the .v2d file to create jobs
# (this is identical to using the --v2d option with no directory path).
#
# Command Line Arguments
#
#
# -c, --calc APP | Run using the specified calc application. By default
# calcif2 will be used.
# |
-D, --difx VERSION | Run using a specific DiFX version. If not specified
# the value of the DIFX_VERSION environment variable will
# be used. Failing that, "DIFX-DEVEL" will be used.
# |
-h, --help | Print help information and quit.
# |
-H, --hostname NAME | Use NAME as the host of the DiFX Server program.
# Default is to use DIFX_CONTROL_HOST environment variable.
# |
-P, --port PORT | Use PORT as the TCP port to communicated with the DiFX Server.
# Default is to use DIFX_CONTROL_PORT environment variable.
# |
--svex FILE | Use the LOCAL file as the "source" of the .vex data. This file
# will be copied to the experiment directory, which will be created.
# This option is similar to the -vex option, but it allows you to use
# a local file. The full path to the local file should be given.
# |
--sv2d FILE | Use the LOCAL file as the "source" of the .v2d data. This file
# will be copied to the experiment directory, which will be created.
# This option is similar to the -v2d option, but it allows you to use
# a local file. The full path to the local file should be given.
# |
-t, --timeout SEC | Use SEC seconds as the timeout value for each job. This is the
# amount of time DiFXrun will wait before it gives up on a
# "silent" (i.e. no messages received from) job and declares it
# non-responsive. Default value is 300.0.
# |
--vex PATH | Copy the .vex file given by PATH (the full path to it on the
# DiFX server) to the experiment directory.
# |
--v2d PATH | Copy the .v2d file given by PATH (the full path to it on the
# DiFX server) to the experiment directory.
# |
" % ( sys.argv[0] )
print ""
print "Options can include:"
print ""
print " --calc APP"
print " -c APP Run the calc process using the given calc application. If"
print " not specified the default \"calcif2\" will be used."
print ""
print " --difx VERSION"
print " -D VERSION Run using a specific DiFX version. If not specified"
print " the value of the DIFX_VERSION environment variable will"
print " be used. Failing that, \"DIFX-DEVEL\" will be used."
print ""
print " --help"
print " -h Print this help information and quit."
print ""
print " --hostname NAME"
print " -H NAME Use NAME as the host of the difxServer program."
print " Default is to use DIFX_CONTROL_HOST environment variable."
print ""
print " --port PORT"
print " -P PORT Use PORT as the TCP port to communicated with the difxServer."
print " Default is to use DIFX_CONTROL_PORT environment variable."
print ""
print " --sv2d FILE Use the LOCAL file as the \"source\" of the .v2d data. This file"
print " will be copied to the experiment directory, which will be created."
print " This option is similar to the --v2d option, but it allows you to use"
print " a local file. The full path to the local file should be given."
print ""
print " --vex PATH Copy the .vex file given by PATH (the full path to it on the"
print " DiFX server) to the directory."
print ""
print " --v2d PATH Copy the .v2d file given by PATH (the full path to it on the"
print " DiFX server) to the directory. Giving the full path to a .v2d file triggers"
print " the creation of the directory."
print ""
exit( 0 )
elif sys.argv[i] in [ "-c", "--calc" ]:
calcCommand = sys.argv[i+1]
i = i + 2
elif sys.argv[i] in [ "-H", "--hostname" ]:
host = sys.argv[i+1]
i = i + 2
elif sys.argv[i] in [ "-D", "--difx" ]:
DiFXVersion = sys.argv[i+1]
i = i + 2
elif sys.argv[i] in [ "-P", "--port" ]:
port = int( sys.argv[i+1] )
i = i + 2
elif sys.argv[i] in [ "--sv2d" ]:
v2dLocal = sys.argv[i+1]
i = i + 2
elif sys.argv[i] in [ "-t", "--timeout" ]:
timeout = int( sys.argv[i+1] )
i = i + 2
elif sys.argv[i] in [ "--vex" ]:
vexPath = sys.argv[i+1]
i = i + 2
elif sys.argv[i] in [ "--v2d" ]:
v2dPath = sys.argv[i+1]
i = i + 2
else:
passDir = sys.argv[i]
i = i + 1
except RuntimeError:
print "Usage: %s [options] <.input path>" % ( sys.argv[0] )
exit( 0 )
# If no directory was specified, but a .v2d path was, assume we are supposed
# to create jobs using the .v2d file.
if passDir == None:
if v2dPath != None:
# Extract the pass path and v2d file name out of the v2dPath.
try:
idx = v2dPath.rindex( "/" )
passDir = v2dPath[:idx]
v2dFile = v2dPath[idx + 1:]
except ValueError:
print "Improper path name in v2d path."
exit( 0 )
else:
print "No path for the experiment was provided."
exit( 0 )
else:
# See if the v2dPath specification begins with a "/" character. This means
# the directory should be created (if necessary) and the file at this path
# copied to it.
if v2dPath != None and v2dPath[0] == '/':
copyRemote = v2dPath
try:
idx = v2dPath.rindex( "/" )
v2dFile = v2dPath[idx + 1:]
except ValueError:
print "Unable to extract .v2d file name from \"" + v2dPath + "\"."
exit( 0 )
createDir = True
# There might also be a local file we want to use as the .v2d file.
elif v2dLocal != None and v2dLocal[0] == '/':
copyLocal = v2dLocal
try:
idx = v2dLocal.rindex( "/" )
v2dFile = v2dLocal[idx + 1:]
except ValueError:
print "Unable to extract .v2d file name from \"" + v2dLocal + "\"."
exit( 0 )
createDir = True
# If the .v2d file specification does not have path information, it is assumed
# to be in the directory, which is assumed to exist (we'll check all of this of
# course!).
elif v2dPath != None:
v2dFile = v2dPath
else:
# See if the passDir contains a .v2d file name already.
try:
idx = passDir.rindex( ".v2d" )
try:
inx = passDir.rindex( "/" )
if inx > idx or len( passDir ) - idx != 4:
print "Path \"" + passDir + "\" may contain a .v2d file but looks wrong."
exit( 0 )
else:
v2dFile = passDir[inx + 1:]
passDir = passDir[:inx]
except ValueError:
print "Path \"" + passDir + "\" may contain a .v2d file but looks wrong."
exit( 0 )
except ValueError:
# Didn't find the .v2d file name, so we'll need to look in the directory
# for it, if that directory exists. If it DOESN'T, we'll create it and make
# a new .v2d file containing only a .vex specification, if that was given.
buildMissingV2d = True
locateV2d = True
# Check the directory name.
if passDir[0] != "/":
print "Experiment directory \"" + passDir + "\" must be a complete path."
exit( 0 )
# Start the vex2difx class, set the version, etc.
print "Making client connection..."
difx = DiFXvex2difx.Client()
difx.connect()
if not difx.socketOK:
difx.close()
exit( 0 )
difx.monitor()
difx.waitTime( timeout )
difx.version( DiFXVersion )
# It is possible we need to build a new .v2d file. We will only do this when the
# specified directory path does not exist and a .vex specification is supplied
# with no .v2d specification.
if buildMissingV2d:
# Do we have a .vex specification?
if vexPath != None:
# Make sure the specified directory does not exist. If it doesn't, cause
# it to be created.
dirList = difx.ls( passDir )
if dirList == None:
createDir = True
locateV2d = False
# Create the specified directory if we are doing that - before we do so, make
# sure it doesn't exist already!.
if createDir:
dirList = difx.ls( passDir )
if dirList == None:
# Directory doesn't exist!
print "Creating directory \"" + passDir + "\"."
difx.mkdir( passDir )
else:
print "\"" + passDir + "\" exists."
# See that the pass directory exists.
dirList = difx.ls( passDir, "-l -d" )
if dirList == None:
# Directory doesn't exist!
if createDir:
print "\"" + passDir + "\" does not exist - creating the directory must have failed."
else:
print "this is what we are doing"
print "\"" + passDir + "\" does not exist!"
difx.close()
exit( 0 )
else:
# Something was returned by the ls command, but we need to be sure it is a directory
# and that we can write there.
lineData = dirList[0]
# First character should be a "d" - or its not a directory!
if lineData[0] != "d":
print "\"" + passDir + "\" does not appear to be a directory."
difx.close()
exit( 0 )
# Test of we can write something there
difx.sendFile( passDir + "/thisisatest", "this is a test" )
dirList = difx.ls( passDir + "/thisisatest" )
if dirList == None:
print "\"" + difx.serverUser + "\" does not appear to have write permission for \"" + passDir + "\"."
difx.close()
exit( 0 )
else:
difx.rm( passDir + "/thisisatest" )
# Locate a .v2d file in that directory if we need to do that.
if locateV2d:
dirList = difx.ls( passDir + "/*.v2d" )
if dirList == None:
print "Directory \"" + passDir + "\" does not contain any .v2d files."
difx.close()
exit( 0 )
else:
try:
idx = dirList[0].rfind( "/" )
v2dFile = dirList[0][idx + 1:]
print "Using existing .v2d file \"" + v2dFile + "\" from directory \"" + passDir + "\"."
except:
print "Problem parsing a legal .v2d file from \"" + dirList[0] + "\"."
difx.close()
exit( 0 )
# Make a copy of a remote .v2d file if so specified.
fileStr = None
if copyRemote != None:
# Make sure the remote file exists.
dirList = difx.ls( copyRemote )
if dirList == None:
print "No file \"" + copyRemote + "\" exists on the DiFX server."
difx.close()
exit( 0 )
# Make sure we can read it (this also gives us the contents, which we use below).
fileStr = difx.getFile( copyRemote )
if fileStr == None or len( fileStr ) == 0:
print "File \"" + copyRemote + "\" cannot be read on the DiFX server."
difx.close()
exit( 0 )
# Or make a copy of a local .v2d file if so specified.
elif copyLocal != None:
try:
f = open( copyLocal )
except IOError:
print ".v2d file \"" + copyLocal + "\" does not exist on the local machine."
difx.close()
exit( 0 )
fileStr = f.read()
if len( fileStr ) == 0:
print ".v2d file \"" + copyLocal + "\" has zero length."
difx.close()
exit( 0 )
elif buildMissingV2d:
fileStr = "vex = " + vexPath
v2dFile = "auto.v2d"
print "Creating new .v2d file containing only .vex specification."
# Either a remote or local .v2d file need to be altered to show the proper .vex path and
# then copied to the new experiment directory.
if copyRemote or copyLocal or buildMissingV2d:
# Were we given a .vex specification? If so, copy that file to the new directory.
# If not, get the full path of the "vex" instruction from the .v2d content and
# copy that. In either case, the .v2d content must be altered to point to this
# new .vex file.
if vexPath == None and not buildMissingV2d:
# Locate the vexPath in the .v2d file.
v2dLines = fileStr.splitlines()
foundVexSpec = False
for line in v2dLines:
if not foundVexSpec and line.strip()[0:3] == "vex" and line.strip()[3:].strip()[0] == "=":
foundVexSpec = True
vexPath = line.strip()[3:].strip()[1:].strip()
# See if this .vex path is "complete", in that it contains a full path. We can only
# guess at this - we assume it is complete if the first character is a slash. If
# not, use the path of the .v2d file as its path (the source of the .vex file
# must be a complete path).
if vexPath[0] != "/":
# Not a full path - add the path to the .v2d file. We can only do this if it is a remote
# file.
if copyRemote != None:
try:
idx = copyRemote.rfind( "/" )
vexPath = copyRemote[:idx] + "/" + vexPath
except ValueError:
print "Problem creating a complete path to .vex file \"" + vexPath + "\"."
difx.close()
exit( 0 )
print "Using \"" + vexPath + "\" as .vex file source."
else:
print ".v2d file contains incomplete path \"" + vexPath + "\" to .vex file."
difx.close()
exit( 0 )
if vexPath == None:
if copyRemote != None:
print "No .vex file specification in \"" + copyRemote + "\" - illegal .v2d file!"
else:
print "No .vex file specification in \"" + copyLocal + "\" - illegal .v2d file!"
difx.close()
exit( 0 )
# Make sure the specified .vex exists.
vexContent = difx.getFile( vexPath )
if vexContent == None or len( vexContent ) == 0:
print "File \"" + vexPath + "\" cannot be read on the DiFX server."
difx.close()
exit( 0 )
# Split the file name off of the full path - we'll use the same name in our new directory
vexFile = None
try:
idx = vexPath.rindex( "/" )
if idx < len( vexPath ) - 2:
difx.sendFile( passDir + "/" + vexPath[idx + 1:], vexContent )
vexFile = vexPath[idx + 1:]
else:
print "No valid .vex file name in \"" + vexPath + "\"."
difx.close()
exit( 0 )
except ValueError:
print "Unable to find .vex file name in \"" + vexPath + "\"."
difx.close()
exit( 0 )
# Replace the name of the .vex file in the .v2d with our new file (no path)
# so that all job creation will be local.
if vexFile == None:
# This shouldn't be possible.
print "Unknown .vex file."
difx.close()
exit( 0 )
# Split up the .v2d content and locate the .vex specification.
v2dLines = fileStr.splitlines()
foundVexSpec = False
counter = 0
for line in v2dLines:
if not foundVexSpec and line.strip()[0:3] == "vex" and line.strip()[3:].strip()[0] == "=":
foundVexSpec = True
v2dLines[counter] = "vex = " + vexFile + "\n"
counter = counter + 1
fileStr = "\n".join( v2dLines )
difx.sendFile( passDir + "/" + v2dFile, fileStr )
# Make sure the .v2d file now exists in the specified directory.
dirList = difx.ls( passDir + "/" + v2dFile );
if dirList == None:
print "\"" + passDir + "/" + v2dFile + "\" does not exist."
difx.close()
exit( 0 )
# Add callback functions for different results
difx.newFileCallback( newFileCallback )
difx.processCompleteCallback( processCompleteCallback )
# Set the calc command if the user has set it, otherwise go with the default
if calcCommand != None:
difx.calcCommand( calcCommand )
difx.v2dFile( v2dFile )
difx.passPath( passDir )
difx.runVex2Difx()
difx.close()