#!/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()