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:
020: import org.griphyn.cPlanner.common.LogManager;
021:
022: import org.griphyn.cPlanner.common.PegasusProperties;
023:
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.TreeMap;
027:
028: /**
029: * This helper class helps in handling the arguments specified in the
030: * Condor namespace by the user either through dax or through profiles in pool.
031: *
032: * @author Karan Vahi
033: * @version $Revision: 452 $
034: */
035:
036: public class Condor extends Namespace {
037:
038: /**
039: * The name of the namespace that this class implements.
040: */
041: public static final String NAMESPACE_NAME = Profile.CONDOR;
042:
043: /**
044: * The name of the key that denotes the arguments of the job.
045: */
046: public static final String ARGUMENTS_KEY = "arguments";
047:
048: /**
049: * The name of the key that denotes the requirements of the job.
050: */
051: public static final String REQUIREMENTS_KEY = "requirements";
052:
053: /**
054: * The name of the key that denotes the condor universe key.
055: */
056: public static final String UNIVERSE_KEY = "universe";
057:
058: /**
059: * The name of the key that denotes the File System Domain. Is actually
060: * propogated to the expression for the Requirements Key.
061: *
062: * @see #REQUIREMENTS_KEY
063: */
064: public static final String FILE_SYSTEM_DOMAIN_KEY = "filesystemdomain";
065:
066: /**
067: * The name of the key that specifies the grid job type.
068: */
069: public static final String GRID_JOB_TYPE_KEY = "grid_type";
070:
071: /**
072: * The name of the key that specifies the jobmanager type.
073: */
074: public static final String JOBMANAGER_TYPE_KEY = "jobmanager_type";
075:
076: /**
077: * The name of the key that specifies transfer of input files.
078: */
079: public static final String TRANSFER_IP_FILES_KEY = "transfer_input_files";
080:
081: /**
082: * The name of the key that specifies transfer of input files.
083: */
084: public static final String TRANSFER_OP_FILES_KEY = "transfer_output_files";
085:
086: /**
087: * The name of the key that specifies the priority for the job.
088: */
089: public static final String PRIORITY_KEY = "priority";
090:
091: /**
092: * The condor universe key value for vanilla universe.
093: */
094: public static final String VANILLA_UNIVERSE = "vanilla";
095:
096: /**
097: * The condor universe key value for grid universe.
098: */
099: public static final String GRID_UNIVERSE = "grid";
100:
101: /**
102: * The condor universe key value for vanilla universe.
103: */
104: public static final String GLOBUS_UNIVERSE = "globus";
105:
106: /**
107: * The condor universe key value for scheduler universe.
108: */
109: public static final String SCHEDULER_UNIVERSE = "scheduler";
110:
111: /**
112: * The condor universe key value for standard universe.
113: */
114: public static final String STANDARD_UNIVERSE = "standard";
115:
116: /**
117: * The condor universe key value for local universe.
118: */
119: public static final String LOCAL_UNIVERSE = "local";
120:
121: /**
122: * The name of the implementing namespace. It should be one of the valid
123: * namespaces always.
124: *
125: * @see Namespace#isNamespaceValid(String)
126: */
127: protected String mNamespace;
128:
129: /**
130: * The default constructor.
131: */
132: public Condor() {
133: mProfileMap = new TreeMap();
134: mNamespace = NAMESPACE_NAME;
135: }
136:
137: /**
138: * The overloaded constructor
139: *
140: * @param mp map containing the profile keys.
141: */
142: public Condor(Map mp) {
143: mProfileMap = new TreeMap(mp);
144: mNamespace = NAMESPACE_NAME;
145: }
146:
147: /**
148: * Returns the name of the namespace associated with the profile implementations.
149: *
150: * @return the namespace name.
151: * @see #NAMESPACE_NAME
152: */
153: public String namespaceName() {
154: return mNamespace;
155: }
156:
157: /**
158: * Adds an input file that is to be transferred from the submit host via
159: * the Condor File Transfer Mechanism. It also sets the associated condor
160: * keys like when_to_transfer and should_transfer_files.
161: *
162: * @param file the path to the file on the submit host.
163: */
164: public void addIPFileForTransfer(String file) {
165: //sanity check
166: if (file == null || file.length() == 0) {
167: return;
168: }
169: String files;
170: //check if the key is already set.
171: if (this .containsKey(Condor.TRANSFER_IP_FILES_KEY)) {
172: //update the existing list.
173: files = (String) this .get(Condor.TRANSFER_IP_FILES_KEY);
174: files = files + "," + file;
175: } else {
176: files = file;
177: //set the additional keys only once
178: this .construct("should_transfer_files", "YES");
179: this .construct("when_to_transfer_output", "ON_EXIT");
180: }
181: this .construct(Condor.TRANSFER_IP_FILES_KEY, files);
182: }
183:
184: /**
185: * Adds an output file that is to be transferred from the submit host via
186: * the Condor File Transfer Mechanism. It also sets the associated condor
187: * keys like when_to_transfer and should_transfer_files.
188: *
189: * @param file the path to the file on the submit host.
190: */
191: public void addOPFileForTransfer(String file) {
192: //sanity check
193: if (file == null || file.length() == 0) {
194: return;
195: }
196: String files;
197: //check if the key is already set.
198: if (this .containsKey(Condor.TRANSFER_OP_FILES_KEY)) {
199: //update the existing list.
200: files = (String) this .get(Condor.TRANSFER_OP_FILES_KEY);
201: files = files + "," + file;
202: } else {
203: files = file;
204: //set the additional keys only once
205: this .construct("should_transfer_files", "YES");
206: this .construct("when_to_transfer_output", "ON_EXIT");
207: }
208: this .construct(Condor.TRANSFER_OP_FILES_KEY, files);
209: }
210:
211: /**
212: * Additional method to handle the Condor namespace with
213: * convenience mappings. Currently the following keys
214: * are not supported keys as they clash with Pegasus
215: * internals
216: *
217: * <pre>
218: * arguments - not supported, got from the arguments tag in DAX
219: * copy_to_spool - supported, limited to LCG sites at present where one needs
220: * to stage in the kickstart. Pegasus sets it to false by default
221: * for arch start stuff on the local pool, unless the user
222: * overrides it.
223: * environment - not supported, use env namespace fpr this
224: * executable - not supported, this is got from the transformation catalog
225: * FileSystemDomain - supported, but is propogated to the classad expression
226: * for requirements.
227: * globusscheduler - not supported, Pegasus determines this on the basis of
228: * it's planning strategy
229: * globusrsl - not supported, rsl to populated through Globus namespace.
230: * grid_type - OK (like gt2, gt4, condor)
231: * log - not supported, as it has to be same for the whole dag
232: * notification - OK
233: * noop_job - OK (used for synchronizing jobs in graph)
234: * noop_job_exit_signal - OK
235: * noop_job_exit_code - OK
236: * periodic_release - OK
237: * periodic_remove - OK
238: * priority - OK
239: * queue - required thing. always added
240: * remote_initialdir- not allowed, the working directory is picked up from
241: * pool file and properties file
242: * stream_error - not supported, as it is applicable only for globus jobs.
243: * However it can be set thru properties.
244: * stream_output - not supported, as it is applicable only for globus jobs.
245: * However it can be set thru properties.
246: * transfer_executable - supported, limited to LCG sites at present where one needs
247: * to stage in the kickstart.
248: * transfer_input_files - supported, especially used to transfer proxies in
249: * case of glide in pools.
250: * universe - supported, especially used to incorporate glide in pools.
251: * </pre>
252: *
253: * @param key is the key within the globus namespace, must be lowercase!
254: * @param value is the value for the given key.
255: *
256: * @return MALFORMED_KEY
257: * VALID_KEY
258: * UNKNOWN_KEY
259: * NOT_PERMITTED_KEY
260: */
261: public int checkKey(String key, String value) {
262: // sanity checks first
263: int res = 0;
264:
265: if (key == null || key.length() < 2 || value == null
266: || value.length() < 1) {
267: res = MALFORMED_KEY;
268: return res;
269: }
270:
271: //before checking convert the key to lower case
272: key = key.toLowerCase();
273:
274: switch (key.charAt(0)) {
275: case 'a':
276: if (key.compareTo("arguments") == 0) {
277: res = NOT_PERMITTED_KEY;
278: } else {
279: res = UNKNOWN_KEY;
280: }
281: break;
282:
283: case 'c':
284: if (key.compareTo("copy_to_spool") == 0) {
285: res = VALID_KEY;
286: } else {
287: res = UNKNOWN_KEY;
288: }
289: break;
290:
291: case 'e':
292: if (key.compareTo("environment") == 0
293: || key.compareTo("executable") == 0) {
294: res = NOT_PERMITTED_KEY;
295: } else {
296: res = UNKNOWN_KEY;
297: }
298: break;
299:
300: case 'f':
301: //want to preserve case
302: if (key.compareTo(FILE_SYSTEM_DOMAIN_KEY) == 0) {
303: res = VALID_KEY;
304: } else {
305: res = UNKNOWN_KEY;
306: }
307: break;
308:
309: case 'g':
310: if (key.compareTo(GRID_JOB_TYPE_KEY) == 0) {
311: res = VALID_KEY;
312: } else if (key.compareTo("globusscheduler") == 0
313: || key.compareTo("globusrsl") == 0) {
314: res = NOT_PERMITTED_KEY;
315: } else {
316: res = UNKNOWN_KEY;
317: }
318: break;
319:
320: case 'j':
321: if (key.compareTo(JOBMANAGER_TYPE_KEY) == 0) {
322: res = VALID_KEY;
323: } else {
324: res = UNKNOWN_KEY;
325: }
326: break;
327:
328: case 'l':
329: if (key.compareTo("log") == 0) {
330: res = NOT_PERMITTED_KEY;
331: } else {
332: res = UNKNOWN_KEY;
333: }
334: break;
335:
336: case 'n':
337: if (key.compareTo("notification") == 0
338: || key.compareTo("noop_job") == 0
339: || key.compareTo("noop_job_exit_code") == 0
340: || key.compareTo("noop_job_exit_signal") == 0) {
341:
342: res = VALID_KEY;
343: } else {
344: res = UNKNOWN_KEY;
345: }
346: break;
347:
348: case 'p':
349: if (key.compareTo("periodic_release") == 0
350: || key.compareTo("periodic_remove") == 0
351: || key.compareTo(PRIORITY_KEY) == 0) {
352: res = VALID_KEY;
353: } else {
354: res = UNKNOWN_KEY;
355: }
356: break;
357:
358: case 'q':
359: if (key.compareTo("queue") == 0) {
360: res = NOT_PERMITTED_KEY;
361: } else {
362: res = UNKNOWN_KEY;
363: }
364: break;
365:
366: case 'r':
367: if (key.compareTo("remote_initialdir") == 0) {
368: res = NOT_PERMITTED_KEY;
369: } else {
370: res = UNKNOWN_KEY;
371: }
372: break;
373:
374: case 's':
375: if (key.compareTo("stream_error") == 0
376: || key.compareTo("stream_output") == 0) {
377: res = NOT_PERMITTED_KEY;
378: } else {
379: res = UNKNOWN_KEY;
380: }
381: break;
382:
383: case 't':
384: if (key.compareTo("transfer_executable") == 0
385: || key.compareTo(TRANSFER_IP_FILES_KEY) == 0) {
386: res = VALID_KEY;
387: } else {
388: res = UNKNOWN_KEY;
389: }
390: break;
391:
392: case 'u':
393: if (key.compareTo(UNIVERSE_KEY) == 0) {
394: res = VALID_KEY;
395: } else {
396: res = UNKNOWN_KEY;
397:
398: }
399: break;
400:
401: default:
402: res = UNKNOWN_KEY;
403: }
404:
405: return res;
406: }
407:
408: /**
409: * It puts in the namespace specific information specified in the properties
410: * file into the namespace. The name of the pool is also passed, as many of
411: * the properties specified in the properties file are on a per pool basis.
412: * It handles the periodic_remove and periodic_release characteristics for
413: * condor jobs.
414: *
415: * @param properties the <code>PegasusProperties</code> object containing
416: * all the properties that the user specified at various
417: * places (like .chimerarc, properties file, command line).
418: * @param pool the pool name where the job is scheduled to run.
419: */
420: public void checkKeyInNS(PegasusProperties properties, String pool) {
421: String value = null;
422:
423: //Karan Oct 19, 2005. The values in property file
424: //should only be treated as default. Need to reverse
425: //below.
426:
427: //get the periodic release values always a default
428: //value is got if not specified.
429: String releaseval = properties.getCondorPeriodicReleaseValue();
430: String oldval = (String) this .removeKey("periodic_release");
431: oldval = (oldval == null) ?
432: //put in default value
433: "3"
434: :
435: //keep the one from profiles or dax
436: oldval;
437: releaseval = (releaseval == null) ? oldval : releaseval;
438:
439: String removeval = properties.getCondorPeriodicRemoveValue();
440: oldval = (String) this .removeKey("periodic_remove");
441: if (oldval == null) {
442: //maintaining backward compatibility
443: oldval = properties.getCondorPeriodicReleaseValue();
444: }
445: oldval = (oldval == null) ?
446: //put in default value
447: "3"
448: :
449: //keep the one from profiles or dax
450: oldval;
451: removeval = (removeval == null) ? oldval : removeval;
452:
453: if (Integer.parseInt(removeval) > Integer.parseInt(releaseval)) {
454: removeval = releaseval;
455: //throw a warning down
456: mLogger.log(" periodic_remove > periodic_release "
457: + "for job " + /*sinfo.jobName +*/
458: ". Setting periodic_remove=periodic_release",
459: LogManager.WARNING_MESSAGE_LEVEL);
460: }
461:
462: //construct the periodic_release and periodic_remove
463: //values only if their final computed values are > 0
464: if (Integer.parseInt(releaseval) > 0) {
465: //value = "(NumSystemHolds <= " + releaseval + ")";
466: this .construct("periodic_release", releaseval);
467: }
468: if (Integer.parseInt(removeval) > 0) {
469: //value = "(NumSystemHolds > " + removeval + ")";
470: this .construct("periodic_remove", removeval);
471: }
472:
473: //the job priorities from property file
474: //picked as default
475: String priority = containsKey(this .PRIORITY_KEY) ? (String) this
476: .get(this .PRIORITY_KEY)
477: : null;
478: priority = (priority == null) ?
479: //pick default from properties
480: properties.getJobPriority()
481: :
482: //prefer existing
483: priority;
484: //construct only if priority is not null
485: if (priority != null) {
486: this .construct(this .PRIORITY_KEY, priority);
487: }
488:
489: }
490:
491: /**
492: * This checks the whether a key value pair specified is valid in the current
493: * namespace or not by calling the checkKey function and then on the basis of
494: * the values returned puts them into the associated map in the class.
495: * In addition it transfers the FILE_SYSTEM_DOMAIN_KEY to the
496: * REQUIREMENTS_KEY.
497: *
498: * @param key key that needs to be checked in the namespace for validity.
499: * @param value value of the key
500: *
501: */
502: public void checkKeyInNS(String key, String value) {
503: int rslVal = checkKey(key, value);
504:
505: switch (rslVal) {
506:
507: case Namespace.MALFORMED_KEY:
508: //key is malformed ignore
509: malformedKey(key, value);
510: break;
511:
512: case Namespace.NOT_PERMITTED_KEY:
513: notPermitted(key);
514: break;
515:
516: case Namespace.UNKNOWN_KEY:
517: unknownKey(key, value);
518: break;
519:
520: case Namespace.VALID_KEY:
521: if (key.equalsIgnoreCase(FILE_SYSTEM_DOMAIN_KEY)) {
522: //set it to the REQUIREMENTS_KEY
523: key = REQUIREMENTS_KEY;
524: //construct the classad expression
525: value = FILE_SYSTEM_DOMAIN_KEY + " == " + "\"" + value
526: + "\"";
527: }
528: construct(key, value);
529: break;
530: }
531:
532: }
533:
534: /**
535: * Merge the profiles in the namespace in a controlled manner.
536: * In case of intersection, the new profile value overrides, the existing
537: * profile value.
538: *
539: * @param profiles the <code>Namespace</code> object containing the profiles.
540: */
541: public void merge(Namespace profiles) {
542: //check if we are merging profiles of same type
543: if (!(profiles instanceof Condor)) {
544: //throw an error
545: throw new IllegalArgumentException(
546: "Profiles mismatch while merging");
547: }
548: String key, value;
549: for (Iterator it = profiles.getProfileKeyIterator(); it
550: .hasNext();) {
551: //construct directly. bypassing the checks!
552: key = (String) it.next();
553: value = (String) profiles.get(key);
554:
555: //override only if key is not transfer_ip_files
556: if (key.equals(this .TRANSFER_IP_FILES_KEY)) {
557: //add to existing
558: this .addIPFileForTransfer(value);
559: }
560:
561: //overriding the arguments makes no sense.
562: if (key.equals(this .ARGUMENTS_KEY)) {
563: continue;
564: } else {
565: this .construct(key, value);
566: }
567: }
568: }
569:
570: /**
571: * Constructs a new element of the format (key=value). All the keys
572: * are converted to lower case before storing.
573: *
574: * @param key is the left-hand-side
575: * @param value is the right hand side
576: */
577: public void construct(String key, String value) {
578: mProfileMap.put(key.toLowerCase(), value);
579: }
580:
581: /**
582: * Returns a boolean value, that a particular key is mapped to in this
583: * namespace. If the key is mapped to a non boolean
584: * value or the key is not populated in the namespace false is returned.
585: *
586: * @param key The key whose boolean value you desire.
587: *
588: * @return boolean
589: */
590: public boolean getBooleanValue(Object key) {
591: boolean value;
592: if (mProfileMap.containsKey(key)) {
593: value = Boolean.valueOf((String) mProfileMap.get(key))
594: .booleanValue();
595: } else {
596: //the key is not in the namespace
597: //return false
598: return false;
599: }
600: return value;
601: }
602:
603: /**
604: * Converts the contents of the map into the string that can be put in the
605: * Condor file for printing.
606: *
607: * @return the textual description
608: */
609: public String toString() {
610: StringBuffer st = new StringBuffer();
611: String key = null;
612: String value = null;
613:
614: Iterator it = mProfileMap.keySet().iterator();
615: while (it.hasNext()) {
616: key = (String) it.next();
617: value = (String) mProfileMap.get(key);
618: st.append(key).append(" = ").append(value).append("\n");
619: }
620:
621: return st.toString();
622: }
623:
624: /**
625: * Returns a copy of the current namespace object.
626: *
627: * @return the Cloned object
628: */
629: public Object clone() {
630: return new Condor(this.mProfileMap);
631: }
632:
633: }
|