/** * Copyright 2007-2008 University Of Southern California * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.isi.pegasus.planner.transfer.sls; import edu.isi.pegasus.planner.catalog.site.classes.SiteStore; import edu.isi.pegasus.planner.classes.PegasusBag; import edu.isi.pegasus.planner.classes.FileTransfer; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.PegasusFile; import edu.isi.pegasus.planner.classes.PlannerOptions; import edu.isi.pegasus.planner.common.PegasusProperties; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.common.util.PegasusURL; import edu.isi.pegasus.planner.catalog.replica.ReplicaCatalogEntry; import edu.isi.pegasus.planner.catalog.site.classes.FileServer; import edu.isi.pegasus.planner.catalog.site.classes.FileServerType; import edu.isi.pegasus.planner.classes.PlannerCache; import edu.isi.pegasus.planner.common.PegasusConfiguration; import edu.isi.pegasus.planner.transfer.SLS; import edu.isi.pegasus.planner.namespace.Pegasus; import java.io.File; import java.util.Iterator; import java.util.Collection; import java.util.LinkedList; /** * This uses the Condor File Transfer mechanism for the second level staging. * * It will work only if the Pegasus Style profile ( pegasus::style ) has a value * of condor. * * @author Karan Vahi * @version $Revision$ */ public class Condor implements SLS { /** * A short description of the transfer implementation. */ public static final String DESCRIPTION = "Condor File Transfer Mechanism"; /** * The handle to the site catalog. */ // protected PoolInfoProvider mSiteHandle; protected SiteStore mSiteStore; /** * The handle to the properties. */ protected PegasusProperties mProps; /** * The handle to the planner options. */ protected PlannerOptions mPOptions; /** * The handle to the logging manager. */ protected LogManager mLogger; /** * A boolean indicating whether to bypass first level staging for inputs */ private boolean mBypassStagingForInputs; /** * A SimpleFile Replica Catalog, that tracks all the files that are being * materialized as part of workflow execution. */ private PlannerCache mPlannerCache; /** * The default constructor. */ public Condor() { } /** * Initializes the SLS implementation. * * @param bag the bag of objects. Contains access to catalogs etc. */ public void initialize( PegasusBag bag ) { mProps = bag.getPegasusProperties(); mPOptions = bag.getPlannerOptions(); mLogger = bag.getLogger(); mSiteStore = bag.getHandleToSiteStore(); mBypassStagingForInputs = mProps.bypassFirstLevelStagingForInputs(); mPlannerCache = bag.getHandleToPlannerCache(); } /** * Returns a boolean whether the SLS implementation does a condor based * modification or not. By condor based modification we mean whether it * uses condor specific classads to achieve the second level staging or not. * * @return false */ public boolean doesCondorModifications(){ return true; } /** * Constructs a command line invocation for a job, with a given sls file. * The SLS maybe null. In the case where SLS impl does not read from a file, * it is advised to create a file in generateSLSXXX methods, and then read * the file in this function and put it on the command line. * * @param job the job that is being sls enabled * @param slsFile the slsFile can be null * * @return invocation string */ public String invocationString( Job job, File slsFile ){ return null; } /** * Returns a boolean indicating whether it will an input file for a job * to do the transfers. Transfer reads from stdin the file transfers that * it needs to do. Always returns true, as we need to transfer the proxy * always. * * @param job the job being detected. * * @return false */ public boolean needsSLSInputTransfers( Job job ) { return false; } /** * Returns a boolean indicating whether it will an output file for a job * to do the transfers. Transfer reads from stdin the file transfers that * it needs to do. * * @param job the job being detected. * * @return false */ public boolean needsSLSOutputTransfers( Job job ) { return false; } /** * Returns the LFN of sls input file. * * @param job Job * * @return the name of the sls input file. */ public String getSLSInputLFN( Job job ){ return null; } /** * Returns the LFN of sls output file. * * @param job Job * * @return the name of the sls input file. */ public String getSLSOutputLFN( Job job ){ return null; } /** * Generates a second level staging file of the input files to the worker * node directory. * * @param job job for which the file is being created * @param fileName name of the file that needs to be written out. * @param stagingSiteServer the file server on the staging site to be used * for retrieval of files i.e the get operation * @param stagingSiteDirectory directory on the head node of the compute site. * @param workerNodeDirectory worker node directory * * @return a Collection of FileTransfer objects listing the transfers that * need to be done. * * @see #needsSLSInputTransfers( Job) */ public Collection<FileTransfer> determineSLSInputTransfers( Job job, String fileName, FileServer stagingSiteServer, String stagingSiteDirectory, String workerNodeDirectory ) { return null; } /** * Generates a second level staging file of the input files to the worker * node directory. * * @param job the job for which the file is being created * @param fileName the name of the file that needs to be written out. * @param stagingSiteServer the file server on the staging site to be used * for retrieval of files i.e the put operation * @param stagingSiteDirectory the directory on the head node of the * compute site. * @param workerNodeDirectory the worker node directory * * @return a Collection of FileTransfer objects listing the transfers that * need to be done. * * @see #needsSLSOutputTransfers( Job) */ public Collection<FileTransfer> determineSLSOutputTransfers(Job job, String fileName, FileServer stagingSiteServer, String stagingSiteDirectory, String workerNodeDirectory) { return null; } /** * Modifies a job for the first level staging to headnode.This is to add * any files that needs to be staged to the head node for a job specific * to the SLS implementation. If any file needs to be added, a <code>FileTransfer</code> * object should be created and added as an input or an output file. * * * @param job the job * @param submitDir the submit directory * @param slsInputLFN the sls input file if required, that is used for * staging in from the head node to worker node directory. * @param slsOutputLFN the sls output file if required, that is used * for staging in from the head node to worker node directory. * @return boolean */ public boolean modifyJobForFirstLevelStaging( Job job, String submitDir, String slsInputLFN, String slsOutputLFN ) { return true; } /** * Modifies a compute job for second level staging. Adds the appropriate * condor classads. It assumes that all the files are being moved to and from * the submit directory directly. Ignores any headnode parameters passed. * * * @param job the job to be modified. * @param stagingSiteURLPrefix the url prefix for the server on the staging site * @param stagingSitedirectory the directory on the staging site, where the nput data * is read from and the output data written out. * @param workerNodeDirectory the directory in the worker node tmp * * @return boolean indicating whether job was successfully modified or * not. * */ public boolean modifyJobForWorkerNodeExecution( Job job, String stagingSiteURLPrefix, String stagingSiteDirectory, String workerNodeDirectory ) { //sanity check on style of the job //handle the -w option that asks kickstart to change //directory before launching an executable. String style = (String)job.vdsNS.get( Pegasus.STYLE_KEY ); if( style == null || !( style.equals( Pegasus.CONDOR_STYLE ) || style.equals( Pegasus.GLIDEIN_STYLE ) || style.equals( Pegasus.CONDORC_STYLE ) || style.equals( Pegasus.CREAMCE_STYLE ) ) ){ mLogger.log( "Invalid style " + style + " for the job " + job.getName(), LogManager.ERROR_MESSAGE_LEVEL ); return false; } //remove any directory. let condor figure it out job.condorVariables.removeKey( "remote_initialdir" ); //set the initial dir to the headnode directory //as this is the directory where we are staging //the input and output data job.condorVariables.construct( "initialdir", stagingSiteDirectory ); Collection<String> files = new LinkedList(); //iterate through all the input files for( Iterator it = job.getInputFiles().iterator(); it.hasNext(); ){ PegasusFile pf = ( PegasusFile )it.next(); String lfn = pf.getLFN(); //sanity check case sanityCheckForDeepLFN( job.getID(), lfn, "input" ); ReplicaCatalogEntry cacheLocation = null; String pfn = null; if( mBypassStagingForInputs ){ //we retrieve the URL from the Planner Cache as a get URL //bypassed URL's are stored as GET urls in the cache and //associated with the compute site //we need a GET URL. we don't know what site is associated with //the source URL. Get the first matching one //PM-698 cacheLocation = mPlannerCache.lookup( lfn, FileServerType.OPERATION.get ); } if( cacheLocation == null ){ //nothing in the cache //construct the location with respect to the staging site //we add just the lfn as we are setting initialdir pfn = lfn; } else{ //construct the URL wrt to the planner cache location pfn = cacheLocation.getPFN(); if( pfn.startsWith( PegasusURL.FILE_URL_SCHEME ) ){ //we let other url's pass through to ensure pfn = new PegasusURL( pfn ).getPath(); } } //add an input file for transfer files.add( pfn ); } job.condorVariables.addIPFileForTransfer( files ); files = new LinkedList(); //iterate and add output files for transfer back for( Iterator it = job.getOutputFiles().iterator(); it.hasNext(); ){ PegasusFile pf = ( PegasusFile )it.next(); String lfn = pf.getLFN(); //sanity check case sanityCheckForDeepLFN( job.getID(), lfn, "output" ); //ignore any input files of FileTransfer as they are first level //staging put in by Condor Transfer refiner if( pf instanceof FileTransfer ){ continue; } //add an output file for transfer files.add( lfn ); } job.condorVariables.addOPFileForTransfer( files ); return true; } /** * Complains for a deep lfn if separator character is found in the lfn * * @param id the id of the associated job * @param lfn lfn of file * @param type type of file as string */ private void sanityCheckForDeepLFN(String id, String lfn, String type) throws RuntimeException { if( lfn.contains( File.separator) ){ StringBuilder sb = new StringBuilder(); sb.append( "Condor File Transfers don't support deep LFN's. "). append( " The " ).append( type ).append( " file " ).append( lfn ).append(" for job " ).append( id ). append( " has a file separator. Set the property pegasus.data.configuration to " ). append( PegasusConfiguration.NON_SHARED_FS_CONFIGURATION_VALUE ).append( " ." ); throw new RuntimeException( sb.toString() ); } } }