/** * 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.common; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.planner.catalog.site.classes.Directory; import edu.isi.pegasus.planner.catalog.site.classes.DirectoryLayout; import edu.isi.pegasus.planner.catalog.site.classes.FileServer; import edu.isi.pegasus.planner.catalog.site.classes.InternalMountPoint; import edu.isi.pegasus.planner.catalog.site.classes.SiteCatalogEntry; import edu.isi.pegasus.planner.catalog.site.classes.SiteStore; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.PegasusBag; import edu.isi.pegasus.planner.classes.PlannerOptions; import edu.isi.pegasus.planner.code.generator.condor.CondorStyle; import edu.isi.pegasus.planner.code.generator.condor.CondorStyleException; import edu.isi.pegasus.planner.code.generator.condor.CondorStyleFactory; import edu.isi.pegasus.planner.namespace.Pegasus; import java.io.File; import java.util.Iterator; import java.util.Properties; /** * A utility class that returns JAVA Properties that need to be set based on * a configuration value * * @author Karan Vahi * @version $Revision$ */ public class PegasusConfiguration { /** * The property key for pegasus configuration. */ public static final String PEGASUS_CONFIGURATION_PROPERTY_KEY = "pegasus.data.configuration"; /** * The value for the S3 configuration. */ public static final String DEPRECATED_S3_CONFIGURATION_VALUE = "S3"; /** * The value for the non shared filesystem configuration. */ public static final String SHARED_FS_CONFIGURATION_VALUE = "sharedfs"; /** * The default data configuration value */ public static String DEFAULT_DATA_CONFIGURATION_VALUE = SHARED_FS_CONFIGURATION_VALUE; /** * The value for the non shared filesystem configuration. */ public static final String NON_SHARED_FS_CONFIGURATION_VALUE = "nonsharedfs"; /** * The value for the condor configuration. */ public static final String CONDOR_CONFIGURATION_VALUE = "condorio"; /** * The value for the condor configuration. */ public static final String DEPRECATED_CONDOR_CONFIGURATION_VALUE = "Condor"; /** * The logger to use. */ private LogManager mLogger; /** * Overloaded Constructor * * @param logger the logger to use. */ public PegasusConfiguration( LogManager logger ){ mLogger = logger; } /** * Loads configuration specific properties into PegasusProperties, * and adjusts planner options accordingly. * * @param properties the Pegasus Properties * @param options the PlannerOptions . */ public void loadConfigurationPropertiesAndOptions( PegasusProperties properties, PlannerOptions options ){ this.loadConfigurationProperties( properties ); //PM-1190 if integrity checking is turned on, turn on the stat of //files also if( properties.doIntegrityChecking() ){ //this.checkAndSetProperty(properties, PegasusProperties.PEGASUS_KICKSTART_STAT_PROPERTY, "true" ); } } /** * Returns the staging site to be used for a job. The determination is made * on the basis of the following * - data configuration value for job * - from planner command line options * - If a staging site is not determined from the options it is set to be the execution site for the job * * @param job the job for which to determine the staging site * * @return the staging site */ public String determineStagingSite( Job job, PlannerOptions options ){ //check to see if job has data.configuration set if( !job.vdsNS.containsKey( Pegasus.DATA_CONFIGURATION_KEY ) ){ throw new RuntimeException( "Internal Planner Error: Data Configuration should have been set for job " + job.getID() ); } String conf = job.vdsNS.getStringValue( Pegasus.DATA_CONFIGURATION_KEY ); //shortcut for condorio if( conf.equalsIgnoreCase( PegasusConfiguration.CONDOR_CONFIGURATION_VALUE) ){ //sanity check against the command line option //we are leaving the data configuration to be per site //by this check String stagingSite = options.getStagingSite( job.getSiteHandle() ); if( stagingSite == null ){ stagingSite = "local"; } else if (!( stagingSite.equalsIgnoreCase( "local" ) )){ StringBuffer sb = new StringBuffer(); sb.append( "Mismatch in the between execution site ").append( job.getSiteHandle() ). append( " and staging site " ).append( stagingSite ). append( " for job " ).append( job.getID() ). append( " . For Condor IO staging site should be set to local ." ); throw new RuntimeException( sb.toString() ); } return stagingSite; } String ss = options.getStagingSite( job.getSiteHandle() ); ss = (ss == null) ? job.getSiteHandle(): ss; //check for sharedfs if( conf.equalsIgnoreCase( PegasusConfiguration.SHARED_FS_CONFIGURATION_VALUE) && !ss.equalsIgnoreCase( job.getSiteHandle()) ){ StringBuffer sb = new StringBuffer(); sb.append( "Mismatch in the between execution site ").append( job.getSiteHandle() ). append( " and staging site " ).append( ss ). append( " for job " ).append( job.getID() ). append( " . For sharedfs they should be the same" ); throw new RuntimeException( sb.toString() ); } return ss; } /** * Updates Site Store and options based on the planner options set by the user * on the command line * * @param store the outputSite store * @param options the planner options. */ public void updateSiteStoreAndOptions( SiteStore store, PlannerOptions options ) { //sanity check to make sure that output outputSite is loaded String outputSite = options.getOutputSite(); if( options.getOutputSite() != null ){ if( !store.list().contains( outputSite ) ){ StringBuffer error = new StringBuffer( ); error.append( "The output site [" ).append( outputSite ). append( "] not loaded from the site catalog." ); throw new RuntimeException( error.toString() ); } } //check if a user specified an output directory String directory = options.getOutputDirectory(); String externalDirectory = options.getOutputDirectory();//the external view of the directory if relative if( directory != null ){ outputSite = ( outputSite == null )? "local": //user did not specify an output site, default to local outputSite;//stick with what user specified options.setOutputSite( outputSite ); SiteCatalogEntry entry = store.lookup( outputSite ); //we first check for local directory DirectoryLayout storageDirectory = entry.getDirectory( Directory.TYPE.local_storage ); if( storageDirectory == null || storageDirectory.isEmpty()){ //default to shared directory storageDirectory = entry.getDirectory( Directory.TYPE.shared_storage ); } if( storageDirectory == null || storageDirectory.isEmpty()){ if( outputSite.equals( "local") ){ //PM-1081 create a default storage directory for local site //with a stub file server. follow up code will update //it with the output-dir value Directory dir = new Directory(); dir.setType( Directory.TYPE.local_storage); dir.addFileServer( new FileServer( "file", "file://", "" )); entry.addDirectory(dir); storageDirectory = dir; } else{ //now throw an error throw new RuntimeException( "No storage directory specified for output site " + outputSite ); } } boolean append = ( directory.startsWith( File.separator ) )? false: //we use the path specified true; //we need to append to path in site catalog on basis of site handle //update the internal mount point and external URL's InternalMountPoint imp = storageDirectory.getInternalMountPoint(); if( imp == null || imp.getMountPoint() == null ){ //now throw an error throw new RuntimeException( "No internal mount point specified for HeadNode Storage Directory for output site " + outputSite ); } if( append ){ //we update the output directory on basis of what is specified //in the site catalog for the site. For local site, we resolve //the relative path from the command line/environment if( outputSite.equals( "local" ) ){ directory = new File( directory ).getAbsolutePath(); externalDirectory = directory; //we have the directory figured out for local site //that should be the one to be used for external file servers append = false; } else{ directory = new File( imp.getMountPoint(), directory).getAbsolutePath(); } } //update all storage file server paths to refer to the directory StringBuffer message = new StringBuffer(); message.append( "Updating internal storage file server paths for site " ).append( outputSite ). append( " to directory " ).append( directory ); mLogger.log( message.toString(), LogManager.CONFIG_MESSAGE_LEVEL ); imp.setMountPoint( directory ); for( FileServer.OPERATION op : FileServer.OPERATION.values() ){ for( Iterator<FileServer> it = storageDirectory.getFileServersIterator(op); it.hasNext();){ FileServer server = it.next(); if( append ){ //get the mount point and append StringBuffer mountPoint = new StringBuffer(); mountPoint.append( server.getMountPoint() ).append( File.separator ).append( externalDirectory ); server.setMountPoint( mountPoint.toString() ); } else{ server.setMountPoint( directory ); } } } //log the updated output site entry mLogger.log( "Updated output site entry is " + entry, LogManager.DEBUG_MESSAGE_LEVEL ); } //PM-960 lets do some post processing of the sites CondorStyleFactory factory = new CondorStyleFactory(); PegasusBag bag = new PegasusBag(); bag.add( PegasusBag.PEGASUS_LOGMANAGER, mLogger ); bag.add( PegasusBag.PEGASUS_PROPERTIES, PegasusProperties.getInstance()); bag.add( PegasusBag.SITE_STORE, store ); factory.initialize( bag ); for( Iterator<SiteCatalogEntry> it = store.entryIterator(); it.hasNext(); ){ SiteCatalogEntry s = it.next(); CondorStyle style = factory.loadInstance(s); mLogger.log( "Style detected for site " + s.getSiteHandle() + " is " + style.getClass(), LogManager.DEBUG_MESSAGE_LEVEL ); try { style.apply(s); } catch (CondorStyleException ex) { throw new RuntimeException( "Unable to apply style to site " + s , ex ); } } } /** * Loads configuration specific properties into PegasusProperties * * @param properties the Pegasus Properties. */ private void loadConfigurationProperties( PegasusProperties properties ){ String configuration = properties.getProperty( PEGASUS_CONFIGURATION_PROPERTY_KEY ) ; Properties props = this.getConfigurationProperties(configuration); for( Iterator it = props.keySet().iterator(); it.hasNext(); ){ String key = (String) it.next(); String value = props.getProperty( key ); this.checkAndSetProperty( properties, key, value ); } } /** * Returns Properties corresponding to a particular configuration. * * @param configuration the configuration value. * * @return Properties */ public Properties getConfigurationProperties( String configuration ){ Properties p = new Properties( ); //sanity check if( configuration == null ){ //default is the sharedfs configuration = SHARED_FS_CONFIGURATION_VALUE; } if( configuration.equalsIgnoreCase( DEPRECATED_S3_CONFIGURATION_VALUE ) || configuration.equalsIgnoreCase( NON_SHARED_FS_CONFIGURATION_VALUE ) ){ //throw warning for deprecated value if( configuration.equalsIgnoreCase( DEPRECATED_S3_CONFIGURATION_VALUE ) ){ mLogger.log( deprecatedValueMessage( PEGASUS_CONFIGURATION_PROPERTY_KEY,DEPRECATED_S3_CONFIGURATION_VALUE ,NON_SHARED_FS_CONFIGURATION_VALUE ), LogManager.WARNING_MESSAGE_LEVEL ); } //we want the worker package to be staged, unless user sets it to false explicitly p.setProperty( PegasusProperties.PEGASUS_TRANSFER_WORKER_PACKAGE_PROPERTY, "true" ); } else if ( configuration.equalsIgnoreCase( CONDOR_CONFIGURATION_VALUE ) || configuration.equalsIgnoreCase( DEPRECATED_CONDOR_CONFIGURATION_VALUE ) ){ //throw warning for deprecated value if( configuration.equalsIgnoreCase( DEPRECATED_CONDOR_CONFIGURATION_VALUE ) ){ mLogger.log( deprecatedValueMessage( PEGASUS_CONFIGURATION_PROPERTY_KEY,DEPRECATED_CONDOR_CONFIGURATION_VALUE ,CONDOR_CONFIGURATION_VALUE ), LogManager.WARNING_MESSAGE_LEVEL ); } //we want the worker package to be staged, unless user sets it to false explicitly p.setProperty( PegasusProperties.PEGASUS_TRANSFER_WORKER_PACKAGE_PROPERTY, "true" ); } else if( configuration.equalsIgnoreCase( SHARED_FS_CONFIGURATION_VALUE ) ){ //PM-624 //we should not explicitly set it to false. false is default value //in Pegasus Properties. //p.setProperty( "pegasus.execute.*.filesystem.local", "false" ); } else{ throw new RuntimeException( "Invalid value " + configuration + " specified for property " + PegasusConfiguration.PEGASUS_CONFIGURATION_PROPERTY_KEY ); } return p; } /** * Returns a boolean indicating whether a job is setup for worker node execution or not * * @param job * * @return */ public boolean jobSetupForWorkerNodeExecution( Job job ){ String configuration = job.vdsNS.getStringValue( Pegasus.DATA_CONFIGURATION_KEY ) ; return ( configuration == null )? false: //DEFAULT is sharedfs case if nothing is specified ( configuration.equalsIgnoreCase( CONDOR_CONFIGURATION_VALUE ) || configuration.equalsIgnoreCase( NON_SHARED_FS_CONFIGURATION_VALUE )|| configuration.equalsIgnoreCase( DEPRECATED_CONDOR_CONFIGURATION_VALUE ) ); } /** * Returns a boolean indicating if job has to be executed using condorio * * @param job * * @return boolean */ public boolean jobSetupForCondorIO( Job job, PegasusProperties properties ){ String configuration = job.vdsNS.getStringValue( Pegasus.DATA_CONFIGURATION_KEY ) ; return ( configuration == null )? this.setupForCondorIO( properties ): configuration.equalsIgnoreCase( CONDOR_CONFIGURATION_VALUE ) || configuration.equalsIgnoreCase( DEPRECATED_CONDOR_CONFIGURATION_VALUE ); } /** * Returns a boolean indicating if properties are setup for condor io * * @param properties * * @return boolean */ private boolean setupForCondorIO( PegasusProperties properties ){ String configuration = properties.getProperty( PEGASUS_CONFIGURATION_PROPERTY_KEY ) ; return ( configuration == null )? false: configuration.equalsIgnoreCase( CONDOR_CONFIGURATION_VALUE ) || configuration.equalsIgnoreCase( DEPRECATED_CONDOR_CONFIGURATION_VALUE ); } /** * Checks for a property, if it does not exist then sets the property to * the value passed * * @param key the property key * @param value the value to set to */ protected void checkAndSetProperty( PegasusProperties properties, String key, String value ) { String propValue = properties.getProperty( key ); if( propValue == null ){ //set the value properties.setProperty( key, value ); } else{ //log a warning StringBuffer sb = new StringBuffer(); sb.append( "Property Key " ).append( key ).append( " already set to " ). append( propValue ).append( ". Will not be set to - ").append( value ); mLogger.log( sb.toString(), LogManager.DEBUG_MESSAGE_LEVEL ); } } /** * Returns the deperecated value message * * @param property the property * @param deprecatedValue the deprecated value * @param updatedValue the updated value * * @return message */ protected String deprecatedValueMessage(String property, String deprecatedValue, String updatedValue) { StringBuffer sb = new StringBuffer(); sb.append( " The property " ).append( property ) .append( " = " ).append( deprecatedValue ). append( " is deprecated. Replace with ").append( property ) .append( " = " ). append( updatedValue ); return sb.toString(); } }