001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015:
016: package org.griphyn.cPlanner.namespace;
017:
018: import org.griphyn.cPlanner.classes.Profile;
019: import org.griphyn.cPlanner.common.PegasusProperties;
020:
021: import org.griphyn.cPlanner.namespace.aggregator.Aggregator;
022: import org.griphyn.cPlanner.namespace.aggregator.MIN;
023: import org.griphyn.cPlanner.namespace.aggregator.MAX;
024: import org.griphyn.cPlanner.namespace.aggregator.Sum;
025: import org.griphyn.cPlanner.namespace.aggregator.Update;
026:
027: import java.util.Iterator;
028: import java.util.Map;
029: import java.util.HashMap;
030: import java.util.TreeMap;
031:
032: /**
033: * This helper class helps in handling the globus rsl key value pairs that
034: * come through profile information for namespace Globus.
035: * The information can either come in through transformation catalog, site catalog
036: * or through profile tags in DAX.
037: *
038: * @author Karan Vahi
039: * @version $Revision: 50 $
040: */
041:
042: public class Globus extends Namespace {
043:
044: /**
045: * The name of the namespace that this class implements.
046: */
047: public static final String NAMESPACE_NAME = Profile.GLOBUS;
048:
049: /**
050: * The table that maps the various globus profile keys to their aggregator
051: * functions.
052: *
053: * @see #Aggregator
054: */
055: public static Map mAggregatorTable;
056:
057: /**
058: * The default aggregator to be used for profile aggregation, if none specified
059: * in the aggregator table;
060: */
061: public static Aggregator mDefaultAggregator = new Update();
062:
063: /**
064: * Initializer block that populates the Aggregator table just once.
065: */
066: static {
067: mAggregatorTable = new HashMap(5);
068: Aggregator max = new MAX();
069: Aggregator sum = new Sum();
070:
071: //all the times need to be added
072: mAggregatorTable.put("maxtime", sum);
073: mAggregatorTable.put("maxcputime", sum);
074: mAggregatorTable.put("maxwalltime", sum);
075:
076: //for the memory rsl params we take max
077: mAggregatorTable.put("maxmemory", max);
078: mAggregatorTable.put("minmemory", max);
079: }
080:
081: /**
082: * The name of the implementing namespace. It should be one of the valid
083: * namespaces always.
084: *
085: * @see Namespace#isNamespaceValid(String)
086: */
087: protected String mNamespace;
088:
089: /**
090: * The default constructor.
091: */
092: public Globus() {
093: mProfileMap = new TreeMap();
094: mNamespace = NAMESPACE_NAME;
095: }
096:
097: /**
098: * The overloaded constructor
099: *
100: * @param map a possibly empty map.
101: */
102: public Globus(Map map) {
103: mProfileMap = new TreeMap(map);
104: mNamespace = NAMESPACE_NAME;
105: }
106:
107: /**
108: * Returns the name of the namespace associated with the profile
109: * implementations.
110: *
111: * @return the namespace name.
112: * @see #NAMESPACE_NAME
113: */
114: public String namespaceName() {
115: return mNamespace;
116: }
117:
118: /**
119: * Constructs a new element of the format (key=value). All the keys
120: * are converted to lower case before storing.
121: *
122: * @param key is the left-hand-side
123: * @param value is the right hand side
124: */
125: public void construct(String key, String value) {
126: mProfileMap.put(key.toLowerCase(), value);
127: }
128:
129: /**
130: * Additional method to handle the globus namespace with
131: * convenience mappings. Currently supported keys are:
132: *
133: * <pre>
134: * arguments - not supported, clashes with Condor
135: * count - OK
136: * directory - not supported, clashes with Pegasus
137: * dryRun - OK, beware the consequences!
138: * environment - not supported, use env namespace
139: * executable - not supported, clashes with Condor
140: * gramMyjob - OK
141: * hostCount - OK
142: * jobType - OK to handle MPI jobs
143: * maxCpuTime - OK
144: * maxMemory - OK
145: * maxTime - OK
146: * maxWallTime - OK
147: * minMemory - OK
148: * project - OK
149: * queue - OK
150: * stdin - not supported, clashes with Pegasus
151: * stdout - not supported, clashes with Pegasus
152: * stderr - not supported, clashes with Pegasus
153: *
154: * rls - OK: Chimera's generic extension (AOB)
155: * </pre>
156: *
157: * @param key is the key within the globus namespace, must be lowercase!
158: * @param value is the value for the given key.
159: *
160: * @return MALFORMED_KEY
161: * VALID_KEY
162: * UNKNOWN_KEY
163: * NOT_PERMITTED_KEY
164: */
165: public int checkKey(String key, String value) {
166: // sanity checks first
167: int res = 0;
168:
169: if (key == null || key.length() < 2 || value == null
170: || value.length() < 1) {
171: res = MALFORMED_KEY;
172: return res;
173: }
174:
175: //before checking convert the key to lower case
176: key = key.toLowerCase();
177:
178: switch (key.charAt(0)) {
179: case 'a':
180: if (key.compareTo("arguments") == 0) {
181: res = NOT_PERMITTED_KEY;
182: } else {
183: res = UNKNOWN_KEY;
184: }
185: break;
186:
187: case 'c':
188: if (key.compareTo("count") == 0) {
189: res = VALID_KEY;
190: } else {
191: res = UNKNOWN_KEY;
192: }
193: break;
194:
195: case 'd':
196: if (key.compareTo("directory") == 0) {
197: res = NOT_PERMITTED_KEY;
198: } else if (key.compareTo("dryrun") == 0) {
199: res = VALID_KEY;
200: } else {
201: res = UNKNOWN_KEY;
202: }
203: break;
204:
205: case 'e':
206: if (key.compareTo("environment") == 0
207: || key.compareTo("executable") == 0) {
208: res = NOT_PERMITTED_KEY;
209: } else {
210: res = UNKNOWN_KEY;
211: }
212: break;
213:
214: case 'g':
215: if (key.compareTo("grammyjob") == 0) {
216: res = VALID_KEY;
217: } else {
218: res = UNKNOWN_KEY;
219: }
220: break;
221:
222: case 'h':
223: if (key.compareTo("hostcount") == 0) {
224: res = VALID_KEY;
225: } else {
226: res = UNKNOWN_KEY;
227: }
228: break;
229:
230: case 'j':
231: if (key.compareTo("jobtype") == 0) {
232:
233: // FIXME: Gaurang?
234: res = VALID_KEY;
235: } else {
236: res = UNKNOWN_KEY;
237: }
238: break;
239:
240: case 'm':
241: if (key.compareTo("maxcputime") == 0
242: || key.compareTo("maxmemory") == 0
243: || key.compareTo("maxtime") == 0
244: || key.compareTo("maxwalltime") == 0
245: || key.compareTo("minmemory") == 0) {
246: res = VALID_KEY;
247: } else {
248: res = UNKNOWN_KEY;
249: }
250: break;
251:
252: case 'p':
253: if (key.compareTo("project") == 0) {
254: res = VALID_KEY;
255: } else {
256: res = UNKNOWN_KEY;
257: }
258: break;
259:
260: case 'q':
261: if (key.compareTo("queue") == 0) {
262: res = VALID_KEY;
263: } else {
264: res = UNKNOWN_KEY;
265: }
266: break;
267:
268: case 'r':
269: if (key.compareTo("rsl") == 0) {
270:
271: // our own extension mechanism, no warnings here
272: // Note: The value IS the RSL!!!
273: new String(value);
274: } else {
275: res = UNKNOWN_KEY;
276: }
277: break;
278:
279: case 's':
280: if (key.compareTo("stdin") == 0
281: || key.compareTo("stdout") == 0
282: || key.compareTo("stderr") == 0) {
283: res = NOT_PERMITTED_KEY;
284: } else {
285: res = UNKNOWN_KEY;
286:
287: }
288: default:
289: res = UNKNOWN_KEY;
290: }
291:
292: return res;
293: }
294:
295: /**
296: * Merge the profiles in the namespace in a controlled manner.
297: * In case of intersection, the new profile value overrides, the existing
298: * profile value.
299: *
300: * @param profiles the <code>Namespace</code> object containing the profiles.
301: */
302: public void merge(Namespace profiles) {
303: //check if we are merging profiles of same type
304: if (!(profiles instanceof Globus)) {
305: //throw an error
306: throw new IllegalArgumentException(
307: "Profiles mismatch while merging");
308: }
309: String key;
310: Aggregator agg;
311: for (Iterator it = profiles.getProfileKeyIterator(); it
312: .hasNext();) {
313: key = (String) it.next();
314: agg = this .aggregator(key);
315: //load the appropriate aggregator to merge the profiles
316: this .construct(key, agg.compute((String) get(key),
317: (String) profiles.get(key), "0"));
318: }
319: }
320:
321: /**
322: * It puts in the namespace specific information specified in the properties
323: * file into the namespace. The name of the pool is also passed, as many of
324: * the properties specified in the properties file are on a per pool basis.
325: * An empty implementation for the timebeing. It is handled in the submit
326: * writer.
327: *
328: * @param properties the <code>PegasusProperties</code> object containing
329: * all the properties that the user specified at various
330: * places (like .chimerarc, properties file, command line).
331: * @param pool the pool name where the job is scheduled to run.
332: */
333: public void checkKeyInNS(PegasusProperties properties, String pool) {
334:
335: //the time rsl's are correctly handled here.
336: //the other RSL's are handled in the CodeGenerator.
337: enforceMinTime(properties, "maxwalltime");
338: enforceMinTime(properties, "maxtime");
339: enforceMinTime(properties, "maxcputime");
340:
341: }
342:
343: /**
344: * Converts the contents of the map into the string that can be put in the
345: * Condor file for printing.
346: *
347: * @return the textual description.
348: */
349: public String toString() {
350: return convert(mProfileMap);
351: }
352:
353: /**
354: * Returns a copy of the current namespace object
355: *
356: * @return the Cloned object
357: */
358: public Object clone() {
359: return new Globus(this .mProfileMap);
360: }
361:
362: /**
363: * Enforces a minimum time if specified in the properties.
364: *
365: * @param properties the properties object holding the properties.
366: * @param key the RSL time key .
367: */
368: protected void enforceMinTime(PegasusProperties properties,
369: String key) {
370: //try to get the existing value if any
371: String val = (String) this .get(key);
372: int existing = (val == null) ? -1 : Integer.parseInt(val);
373:
374: int value = properties.getMinimumRemoteSchedulerTime(key);
375: //we enforce the min value specified in properties only if it is
376: //greater than the existing value
377: if (value > existing) {
378: //user asked for enforcement
379: this .construct(key, Integer.toString(value));
380: }
381: }
382:
383: /**
384: * Returns the aggregator to be used for the profile key while merging.
385: * If no aggregator is found, the then default Aggregator (Update) is used.
386: *
387: * @param key the key for which the aggregator is found.
388: *
389: * @return the aggregator for the profile key.
390: */
391: protected Aggregator aggregator(String key) {
392: Object aggregator = this .mAggregatorTable.get(key);
393: return (aggregator == null) ? mDefaultAggregator
394: : (Aggregator) aggregator;
395: }
396:
397: /**
398: * Converts a map with RSL kv-pairs into an RSL string.
399: *
400: * @param rsl is the RSL map to convert
401: * @return the new string to use in globusrsl of Condor.
402: */
403: private String convert(java.util.Map rsl) {
404: StringBuffer result = new StringBuffer();
405: for (Iterator i = rsl.keySet().iterator(); i.hasNext();) {
406: String key = (String) i.next();
407: String value = (String) rsl.get(key);
408: if (value != null && value.length() > 0) {
409: if (key.compareTo("rsl") == 0) {
410: result.append(value);
411: } else {
412: result.append('(').append(key).append('=').append(
413: value).append(')');
414: }
415: }
416: }
417: return result.toString();
418: }
419:
420: }
|