/**
* 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.selector.site;
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.namespace.Pegasus;
import edu.isi.pegasus.planner.partitioner.graph.GraphNode;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* A site selector than ends up doing grouping jobs together on the basis of
* an identifier specifed in the dax for the jobs, and schedules them on to the
* same site. Currently, the identifier is key <code>group</code> in the pegasus
* profile namespace. All the jobs that do not have a group associated with them
* are put in one default group and end up being scheduled on the same pool.
* A limitation of this site selector is that it does not check whether all the
* jobs can be scheduled on a particular pool or not. It just checks whether
* the first job can be or not. The reason for that is after the grouping the
* the selector just hands the first job in each group to the other site selectors
* that work on jobs. Currently, it hands it to the Random Site Selector.
*
* In the DAX, a job tagged with groups will look as follows
* <pre>
*
* {@code
* <job id="ID000001" namespace="pegasus" name="preprocess" version="1.0" level="3">
* <profile namespace="pegasus" key="group">group-1</profile>
* <argument>-a top -T 6 -i <filename file="f.a"/> -o <filename file="f.b"/> </argument>
* <uses file="f.a" link="input" register="false" transfer="true" type="data"/>
* <uses file="f.b" link="output" register="true" transfer="true" type="data"/>
* </job>
* }
*
* </pre>
*
* @author Karan Vahi
* @author Gaurang Mehta
* @author Mei-Hui Su
*
* @version $Revision$
*/
public class Group extends Abstract {
/**
* The description of the site selector.
*/
private static final String mDescription =
"Site selector doing clustering on the basis of key group in pegasus namespace";
/**
* The name of the group into which jobs are grouped if no group is
* specified in the dax.
*/
private static final String mDefaultGroup = "default";
/**
* The map containing the the jobs grouped by the key group.
*/
private Map mGroupMap;
/**
* The handle to the internal site selector that is used to schedule jobs
* amongst the groups.
*/
private AbstractPerJob mSelector;
/**
* The default constructor.
*/
public Group() {
mGroupMap = new TreeMap();
mSelector = new Random();
// mLogger = LogManager.getInstance();
}
/**
* Initializes the site selector.
*
* @param bag the bag of objects that is useful for initialization.
*
*/
public void initialize( PegasusBag bag ){
super.initialize( bag );
mSelector.initialize( bag );
}
/**
* Returns the description of the site selector.
*
* @return description.
*/
public String description() {
return mDescription;
}
/**
* The call out to map a list of jobs on to the execution pools. A default
* implementation is provided that internally calls mapJob2ExecPool(Job,
* String,String,String) to map each of the jobs sequentially to an execution site.
* The reason for this method is to support site selectors that
* make their decision on a group of jobs i.e use backtracking to reach a good
* decision.
* The implementation that calls out to an executable using Runtime does not
* implement this method, but relies on the default implementation defined
* here.
*
* @param workflow the workflow that needs to be scheduled.
* @param sites the list of <code>String</code> objects representing the
* execution pools that can be used.
*
*/
public void mapWorkflow( ADag workflow, List sites) {
Job job;
List l = null;
int i = 0;
for(Iterator it = workflow.nodeIterator();it.hasNext(); ){
GraphNode node = ( GraphNode )it.next();
job = ( Job )node.getContent();
//put the jobs into the map grouped by key VDS_GROUP_KEY
insert(job);
}
//traverse through the group map and send off the first job
//in each group to the internal site selector.
for(Iterator it = mGroupMap.entrySet().iterator();it.hasNext();){
Map.Entry entry = (Map.Entry)it.next();
boolean defaultGroup = entry.getKey().equals( mDefaultGroup );
mLogger.log("[Group Selector]Mapping jobs in group " + entry.getKey(),
LogManager.DEBUG_MESSAGE_LEVEL);
l = (List)entry.getValue();
String msg = "\t{";
boolean first = true;
for(Iterator it1 = l.iterator();it1.hasNext();){
msg += (first)? "" : ",";
msg += ((Job)it1.next()).jobName ;
first = false;
}
msg += "}";
mLogger.log(msg,LogManager.DEBUG_MESSAGE_LEVEL);
//hand of the first job to the internal selector
job = (Job)l.get(0);
mSelector.mapJob( job, sites );
//traverse thru the remaining jobs in the group
for(Iterator it1 = l.iterator();it1.hasNext();){
Job j = (Job)it1.next();
if ( defaultGroup ){
//each job in the group has to be
//mapped individually
mSelector.mapJob( j, sites);
}
else{
//mapping same as the one for
//for the first job in group
j.setSiteHandle( job.getSiteHandle() );
}
}
}
}
/**
* Inserts the job into the group map.
*
* @param job the job to be inserted.
*/
private void insert(Job job){
Object obj = job.vdsNS.get(Pegasus.GROUP_KEY);
if(obj != null && ((String)obj).equalsIgnoreCase(mDefaultGroup)){
//throw an exception?
throw new RuntimeException( "The group name " + mDefaultGroup +
" is a reserved keyword for the selector." +
" Use another group name in your DAX" );
}
String key = (obj == null)?
//no group specified. set to default
mDefaultGroup:
//get the value from the profile
(String)obj;
if(mGroupMap.containsKey(key)){
//there is already a group associated.
List l = (List)mGroupMap.get(key);
l.add(job);
}
else{
//insert a new entry to the map
List l = new LinkedList();
l.add(job);
mGroupMap.put(key,l);
}
}
}