/** * 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.code.generator; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.planner.classes.ADag; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.PegasusBag; import edu.isi.pegasus.planner.cluster.JobAggregator; import edu.isi.pegasus.planner.cluster.aggregator.JobAggregatorFactory; import edu.isi.pegasus.planner.cluster.aggregator.MPIExec; import edu.isi.pegasus.planner.code.CodeGeneratorException; import edu.isi.pegasus.planner.code.GridStart; import edu.isi.pegasus.planner.code.GridStartFactory; import edu.isi.pegasus.planner.namespace.Pegasus; import edu.isi.pegasus.planner.partitioner.graph.Graph; import edu.isi.pegasus.planner.partitioner.graph.GraphNode; /** * This code generator generates a shell script in the submit directory. * The shell script can be executed on the submit host to run the workflow * locally. * * @author Karan Vahi * @version $Revision$ */ public class PMC extends Abstract { /** * The handle to the GridStart Factory. */ protected GridStartFactory mGridStartFactory; /** * A boolean indicating whether grid start has been initialized or not. */ protected boolean mInitializeGridStart; /** * Handle to the PBS Code generator. */ private final PBS mPBS; /** * The default constructor. */ public PMC( ){ super(); mInitializeGridStart = true; mGridStartFactory = new GridStartFactory(); //instantiate the PBS Code generator. //we may need a factory later on mPBS = new PBS(); } /** * Initializes the Code Generator implementation. * * @param bag the bag of initialization objects. * * @throws CodeGeneratorException in case of any error occuring code generation. */ public void initialize( PegasusBag bag ) throws CodeGeneratorException{ super.initialize( bag ); mPBS.initialize( bag ); mLogger = bag.getLogger(); //create the base directory recovery File wdir = new File(mSubmitFileDir); wdir.mkdirs(); } /** * Generates the code for the concrete workflow in the GRMS input format. * The GRMS input format is xml based. One XML file is generated per * workflow. * * @param dag the concrete workflow. * * @return handle to the PMC file generated in the submit directory. * * @throws CodeGeneratorException in case of any error occuring code generation. */ public Collection<File> generateCode( ADag dag ) throws CodeGeneratorException{ Collection result = new ArrayList( 1 ); //PM-747 no need for conversion as ADag now implements Graph interface Graph workflow = dag; mGridStartFactory.initialize( mBag, dag, this.getDAGFilename(dag, POSTSCRIPT_LOG_SUFFIX ) ); Job prevJob = null; //traverse the workflow and enable the jobs with kickstart first for( Iterator<GraphNode> it = workflow.nodeIterator(); it.hasNext(); ){ GraphNode node = it.next(); Job job = (Job)node.getContent(); String site = job.getSiteHandle(); //sanity check if( !( prevJob == null || prevJob.getSiteHandle().equalsIgnoreCase( site ) )){ StringBuffer error = new StringBuffer(); error.append( "Site Mismatch between jobs " ).append( "(" ). append( job.getID() ).append( ":" ).append( site ).append( "," ). append( prevJob.getID() ).append( ":" ).append( prevJob.getSiteHandle() ). append( ") ").append( "." ). append( "For the PMC Code generator all jobs should be mapped to the same site. "); throw new CodeGeneratorException( error.toString() ); } GridStart gridStart = mGridStartFactory.loadGridStart( job , null ); //trigger the -w option for kickstart always job.vdsNS.construct( Pegasus.CHANGE_DIR_KEY , "true" ); //the stdin of the job is handled outside of //kickstart module for time being. //assumption is that we are always using kickstart //for this scenario String stdin = job.getStdIn(); StringBuffer kickstartPreArgs = new StringBuffer(); boolean prepend = false; if( stdin == null || stdin.length() == 0 ){ //nothing to do } else{ //construct the kickstart arguments for connecting // the task stdin kickstartPreArgs.append("-i "); if( stdin.startsWith( File.separator ) ){ kickstartPreArgs.append( stdin ); } else{ //prepend the submit dirctory //PM-833 figure out the job submit directory String jobSubmitDirectory = new File( job.getFileFullPath( mSubmitFileDir, ".in" )).getParent(); kickstartPreArgs.append( jobSubmitDirectory ).append( File.separator).append( stdin ); } kickstartPreArgs.append(' '); //reset stdin as we don't want //kickstart module to handle it job.setStdIn( "" ); prepend = true; } //enable the job if( !gridStart.enable( job,false ) ){ String msg = "Job " + job.getName() + " cannot be enabled by " + gridStart.shortDescribe() + " to run at " + job.getSiteHandle(); mLogger.log( msg, LogManager.FATAL_MESSAGE_LEVEL ); throw new CodeGeneratorException( msg ); } if( prepend ){ //job has already been kickstarted. //prepend the -i option for stdin StringBuffer args = new StringBuffer(); args.append( kickstartPreArgs ).append( job.getArguments() ); job.setArguments( args.toString() ); } prevJob = job; } //lets load the PMC cluster implementation //and generate the PMC file for it JobAggregator aggregator = JobAggregatorFactory.loadInstance( JobAggregatorFactory.MPI_EXEC_CLASS, dag, mBag); MPIExec pmcAggregator = (MPIExec)aggregator; String name = pmcBasename( dag ); //PM-660 designate that the graph is for the whole workflow pmcAggregator.generatePMCInputFile(workflow, name, false ); //lets generate the PBS input file mPBS.generateCode( dag ); //the dax replica store this.writeOutDAXReplicaStore( dag ); //write out the braindump file this.writeOutBraindump( dag ); //write out the nelogger file this.writeOutStampedeEvents( dag ); //write out the metrics file // this.writeOutWorkflowMetrics(dag); return result; } /** * Generates the code for a single job in the input format of the workflow * executor being used. * * @param dag the dag of which the job is a part of. * @param job the <code>Job</code> object holding the information about * that particular job. * * @throws CodeGeneratorException in case of any error occuring code generation. */ public void generateCode( ADag dag, Job job ) throws CodeGeneratorException{ throw new CodeGeneratorException( "The code generator only works on the workflow level" ); } /** * Returns a Map containing additional braindump entries that are specific * to a Code Generator. The entries added for this are from the scheduler * specific generator * * @param workflow the executable workflow * * @return Map */ public Map<String, String> getAdditionalBraindumpEntries( ADag workflow ) { Map<String, String> entries = this.mPBS.getAdditionalBraindumpEntries(workflow); entries.put("dag", this.getPathtoPMCFile(workflow)); return entries; } /** * Returns the basename for the pmc file for the dag * * @param dag the workflow * * @return the basenmae */ protected String pmcBasename( ADag dag ) { StringBuffer name = new StringBuffer(); name.append( dag.getLabel() ).append( "-" ). append( dag.getIndex() ).append( ".dag" ); return name.toString(); } /** * Returns the basename for the pmc file for the dag * * @param dag the workflow * * @return the basenmae */ protected String getPathtoPMCFile( ADag dag ) { StringBuilder script = new StringBuilder(); script.append( this.mSubmitFileDir ).append( File.separator ). append( this.pmcBasename(dag) ); return script.toString(); } }