/* * * 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.catalog.site.classes; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import edu.isi.pegasus.planner.catalog.classes.SysInfo; import edu.isi.pegasus.planner.catalog.transformation.classes.VDSSysInfo; import edu.isi.pegasus.planner.classes.Job; import edu.isi.pegasus.planner.classes.PlannerOptions; import edu.isi.pegasus.planner.common.PegasusProperties; import edu.isi.pegasus.planner.namespace.Pegasus; import edu.isi.pegasus.planner.mapper.OutputMapperFactory; /** * The site store contains the collection of sites backed by a HashMap. * * @author Karan Vahi * @author Mats Rynge * @version $Revision$ */ public class SiteStore extends AbstractSiteData{ /** * The internal map that maps a site catalog entry to the site handle. */ private Map<String, SiteCatalogEntry> mStore; /** * The work dir path from the properties. */ private String mWorkDir; private PlannerOptions mPlannerOptions; /** * The file backend for the site catalog. */ private File mFileSource; /** * A boolean indicating whether to have a deep directory structure for * the storage directory or not. */ protected boolean mDeepStorageStructure; /** * The default constructor. */ public SiteStore(){ initialize(); } /** * The intialize method. */ public void initialize() { mStore = new HashMap<String, SiteCatalogEntry>( ); } /** * A setter method that is to be set to use getWorkDirectory functions, correctly. * * @param properties the <code>PegasusProperties</code> * @param options the <code>PlannerOptions</code> */ public void setForPlannerUse( PegasusProperties properties, PlannerOptions options ){ mPlannerOptions = options; mWorkDir = properties.getExecDirectory(); mDeepStorageStructure = properties.useDeepStorageDirectoryStructure() ; //||hashedOutputMapperUsed( properties ); } /** * Adds a site catalog entry to the store. * * @param entry the site catalog entry. * * @return previous value associated with specified key, or null * if there was no mapping for key */ public SiteCatalogEntry addEntry( SiteCatalogEntry entry ){ return this.mStore.put( entry.getSiteHandle() , entry ); } /** * Returns an iterator to SiteCatalogEntry objects in the store. * * @return Iterator<SiteCatalogEntry> */ public Iterator<SiteCatalogEntry> entryIterator(){ return this.mStore.values().iterator(); } /** * Returns the list of sites, in the store. * * @return list of sites */ public Set<String> list() { return mStore.keySet(); } /** * Returns SiteCatalogEntry matching a site handle. * * @param handle the handle of the site to be looked up. * * @return SiteCatalogEntry if exists else null. */ public SiteCatalogEntry lookup ( String handle ){ return this.mStore.get( handle ); } /** * Returns boolean indicating whether the store has a SiteCatalogEntry * matching a handle. * * @param handle the site handle / identifier. * * @return boolean */ public boolean contains( String handle ){ return this.mStore.containsKey( handle ); } /** * * @param sites the list of site identifiers for which sysinfo is required. * * * @return the sysinfo map */ public Map<String,SysInfo> getSysInfos( List<String> sites ) { HashMap result = new HashMap(); for ( Iterator i = sites.iterator(); i.hasNext(); ) { SiteCatalogEntry site = this.lookup (( String ) i.next()); if( site != null ){ result.put( site.getSiteHandle(), site.getSysInfo() ); } } return result; } /** * * @param sites the list of site identifiers for which sysinfo is required. * * * @return the sysinfo map */ /*private Map<String,VDSSysInfo> getVDSSysInfos( List<String> sites ) { HashMap result = new HashMap(); for ( Iterator i = sites.iterator(); i.hasNext(); ) { SiteCatalogEntry site = this.lookup (( String ) i.next()); if( site != null ){ result.put( site.getSiteHandle(), site.getVDSSysInfo() ); } } return result; } */ /** * Returns the <code>VDSSysInfo</code> for the site * * @param handle the site handle / identifier. * @return the VDSSysInfo else null */ public VDSSysInfo getVDSSysInfo( String handle ){ //sanity check if( !this.contains( handle ) ) { return null; } else{ return this.lookup( handle ).getVDSSysInfo(); } } /** * Returns the <code>SysInfo</code> for the site * * @param handle the site handle / identifier. * @return the SysInfo else null */ public SysInfo getSysInfo( String handle ){ //sanity check if( !this.contains( handle ) ) { return null; } else{ return this.lookup( handle ).getSysInfo(); } } /** * Returns the value of VDS_HOME for a site. * * @param handle the site handle / identifier. * * @return value if set else null. */ @Deprecated public String getVDSHome( String handle ){ //sanity check if( !this.contains( handle ) ) { return null; } else{ return this.lookup( handle ).getVDSHome(); } } /** * Returns the value of PEGASUS_HOME for a site. * * @param handle the site handle / identifier. * * @return value if set else null. */ @Deprecated public String getPegasusHome( String handle ){ if( !this.contains( handle ) ) { return null; } else{ return this.lookup( handle ).getPegasusHome(); } } /** * Returns an environment variable associated with the site. If a env value * is not specified in the site catalog, then only for local site * it falls back on the value retrieved from the environment. * * @param handle the site handle / identifier. * @param variable the name of the environment variable. * * @return value of the environment variable if found, else null */ public String getEnvironmentVariable( String handle, String variable ){ String value = null; //sanity check if( !this.contains( handle ) ) { value = null; } else{ value = this.lookup( handle ).getEnvironmentVariable( variable ); } /* Moved to SiteCatalogEntry Karan Dec 15, 2011 //change the preference order because of JIRA PM-471 if( value == null ){ //fall back only for local site the value in the env if( handle != null && handle.equals( "local" ) ){ //try to retrieve value from environment //for local site. value = System.getenv( variable ); } } */ return value; } /** * This is a soft state remove, that removes a GridGateway from a particular * site. The cause of this removal could be the inability to * authenticate against it at runtime. The successful removal lead Pegasus * not to schedule job on that particular grid gateway. * * @param handle the site handle with which it is associated. * @param contact the contact string for the grid gateway. * * @return true if was able to remove the jobmanager from the cache * false if unable to remove, or the matching entry is not found * or if the implementing class does not maintain a soft state. */ public boolean removeGridGateway( String handle, String contact ) { //sanity check if( !this.contains( handle ) ) { return false; } else{ return this.lookup( handle ).removeGridGateway( contact ); } } /** * This is a soft state remove, that removes a file server from a particular * pool entry. The cause of this removal could be the inability to * authenticate against it at runtime. The successful removal lead Pegasus * not to schedule any transfers on that particular gridftp server. * * @param handle the site handle with which it is associated. * @param url the contact string for the file server. * * @return true if was able to remove the gridftp from the cache * false if unable to remove, or the matching entry is not found * or if the implementing class does not maintain a soft state. * or the information about site is not in the site catalog. */ public boolean removeFileServer( String handle, String url ){ throw new UnsupportedOperationException( "Method remove( String , String ) not yet implmeneted" ); } /** * Returns a URL to the work directory as seen externally ( including external * mount point ). * * @param siteHandle the site handle. * @param operation the operation for which we need the server. * * @return the url */ public String getExternalWorkDirectoryURL( String siteHandle, FileServer.OPERATION operation ){ String url = null; SiteCatalogEntry site = this.lookup( siteHandle ); if( site == null ){ return url; } //select a file server FileServer fs = site.selectHeadNodeScratchSharedFileServer( operation ); return this.getExternalWorkDirectoryURL( fs, siteHandle ); } /** * Returns a URL to the work directory as seen externally ( including external * mount point ). * * @param server the FileServer to use * @param siteHandle the site handle. * * @return the url else null */ public String getExternalWorkDirectoryURL( FileServer server, String siteHandle ){ String url = null; if( server == null ){ return null; } url = server.getURLPrefix() + this.getExternalWorkDirectory( server, siteHandle ); return url; } /** * Return the work directory as seen externally (including external mount point) * * @param fs the FileServer with the file system * @param siteHandle the site for which you want the directory * * @return String corresponding to the mount point */ public String getExternalWorkDirectory( FileServer fs, String siteHandle) { StringBuffer path = new StringBuffer(); if ( mWorkDir.length() == 0 ) { // special case - no pegasus.dir.exec path.append( fs.getMountPoint() ); } else if ( mWorkDir.charAt( 0 ) != '/' ) { String mountPoint = fs.getMountPoint(); // path = fs.getMountPoint() + File.separator + mWorkDir; // not a absolute path given - append path.append( mountPoint ); if( mountPoint.charAt( mountPoint.length() - 1 ) == File.separatorChar ){ //no need to add path separator } else{ path.append( File.separator ); } } //always add the mWorkDir, whatever it is StringBuffer addon = new StringBuffer(); addon.append( mWorkDir ); String randDir = mPlannerOptions.getRandomDirName(); if ( randDir != null) { //append the random dir name to the //work dir constructed till now addon.append( File.separator ); //append withtout any modifications addon.append( randDir ); } path.append( addon.toString() ); return path.toString(); } /** * Return the relative directory that needs to be appended to the storage * directory for the workflow. * * * @return String corresponding to the mount point if the pool is found. * null if pool entry is not found. * */ public String getRelativeStorageDirectoryAddon( ) { String mount_point = ""; //check if we need to replicate the submit directory //structure on the storage directory if( mDeepStorageStructure ){ String leaf = ( this.mPlannerOptions.partOfDeferredRun() )? //if a deferred run then pick up the relative random directory //this.mUserOpts.getOptions().getRandomDir(): this.mPlannerOptions.getRelativeDirectory(): //for a normal run add the relative submit directory this.mPlannerOptions.getRelativeDirectory(); //PM-1124 if leaf is null, means fall back to relative submit directory if( leaf == null ){ leaf = this.mPlannerOptions.getRelativeSubmitDirectory(); } File f = new File( mount_point, leaf ); mount_point = f.getAbsolutePath(); } return mount_point; } /** * Return the storage mount point for a particular pool. * * @param site the site for which you want the storage-mount-point. * * @return String corresponding to the mount point if the pool is found. * null if pool entry is not found. * * @deprecated */ /* public String getExternalStorageDirectory( String site ) { String mount_point = mStorageDir; SiteCatalogEntry entry = this.lookup( site ); //sanity check if( entry == null ){ return null; } FileServer server = null; if ( mStorageDir.length() == 0 || mStorageDir.charAt( 0 ) != '/' ) { server = entry.selectStorageFileServerForStageout( FileServer.OPERATION.put ); mount_point = server.getMountPoint(); //removing the trailing slash if there int length = mount_point.length(); if ( length > 1 && mount_point.charAt( length - 1 ) == '/' ) { mount_point = mount_point.substring( 0, length - 1 ); } //append the Storage Dir File f = new File( mount_point, mStorageDir ); mount_point = f.getAbsolutePath(); } //check if we need to replicate the submit directory //structure on the storage directory if( mDeepStorageStructure ){ String leaf = ( this.mPlannerOptions.partOfDeferredRun() )? //if a deferred run then pick up the relative random directory //this.mUserOpts.getOptions().getRandomDir(): this.mPlannerOptions.getRelativeDirectory(): //for a normal run add the relative submit directory this.mPlannerOptions.getRelativeDirectory(); File f = new File( mount_point, leaf ); mount_point = f.getAbsolutePath(); } return mount_point; } */ /** * This determines the working directory on remote execution pool on the * basis of whether an absolute path is specified in the pegasus.dir.exec directory * or a relative path. * * @param handle the site handle of the site where a job has to be executed. * * @return the path to the pool work dir. * @throws RuntimeException in case of site not found in the site catalog. */ public String getInternalWorkDirectory( String handle ) { return this.getInternalWorkDirectory( handle, null, -1 ); } /** * This determines the working directory on remote execution pool for a * particular job. The job should have it's execution pool set. * * @param job <code>Job</code> object for the job. * * @return the path to the pool work dir. * @throws RuntimeException in case of site not found in the site catalog. */ public String getInternalWorkDirectory( Job job ) { return this.getInternalWorkDirectory( job, false ); } /** * This determines the working directory on remote execution pool or a staging * site for a particular job. The job should have it's execution pool set. * * @param job <code>Job</code> object for the job. * @param onStagingSite boolean indicating whether the work directory required * is the one on staging site. * * @return the path to the pool work dir. * @throws RuntimeException in case of site not found in the site catalog. */ public String getInternalWorkDirectory( Job job , boolean onStagingSite ) { return this.getInternalWorkDirectory( onStagingSite ? job.getStagingSiteHandle() : job.getSiteHandle(), job.vdsNS.getStringValue( Pegasus.REMOTE_INITIALDIR_KEY ), job.jobClass ); } /** * This determines the working directory on remote execution pool on the * basis of whether an absolute path is specified in the pegasus.dir.exec * directory or a relative path. * * @param handle the site handle of the site where a job has to be executed. * @param path the relative path that needs to be appended to the * workdir from the execution pool. * * @return the path to the pool work dir. * @throws RuntimeException in case of site not found in the site catalog. */ public String getInternalWorkDirectory( String handle, String path ) { return this.getInternalWorkDirectory( handle, path, -1 ); } /** * This determines the working directory on remote execution pool on the * basis of whether an absolute path is specified in the pegasus.dir.exec directory * or a relative path. If the job class happens to be a create directory job * it does not append the name of the random directory since the job is * trying to create that random directory. * * @param handle the site handle of the site where a job has to be executed. * @param path the relative path that needs to be appended to the * workdir from the execution pool. * @param jobClass the class of the job. * * @return the path to the pool work dir. * @throws RuntimeException in case of site not found in the site catalog. */ public String getInternalWorkDirectory( String handle, String path, int jobClass ) { //get the random directory name //sanitary check if( mPlannerOptions == null ){ throw new RuntimeException( "The initializeUseForPlanner() was not called before calling getWorkDirectory"); } if(jobClass == Job.CREATE_DIR_JOB ){ //the create dir jobs always run in the //workdir specified in the site catalog //return execPool.getHeadNodeFS().getScratch().getSharedDirectory().getInternalMountPoint().getMountPoint(); //Related to JIRA PM-67 http://pegasus.isi.edu/jira/browse/PM-67 //pegasus-get-sites generates site catalog with VO specific // storage mount points and work directories. These dont exist //by default. Hence the job needs to be launched in /tmp return File.separator + "tmp"; } SiteCatalogEntry execPool = this.lookup( handle ); if(execPool == null){ throw new RuntimeException("Entry for " + handle + " does not exist in the Site Catalog"); } String execPoolDir = mWorkDir; if ( mWorkDir.length() == 0 || mWorkDir.charAt( 0 ) != '/' ) { //means you have to append the //value specfied by pegasus.dir.exec File f = new File( execPool.getInternalMountPointOfWorkDirectory(), mWorkDir ); execPoolDir = f.getAbsolutePath(); } String randDir = mPlannerOptions.getRandomDirName(); if ( randDir != null) { //append the random dir name to the //work dir constructed till now File f = new File( execPoolDir, randDir ); execPoolDir = f.getAbsolutePath(); } //path takes precedence over random dir if ( path != null ) { //well i can do nesting conditional return but wont return ( path.length() == 0 || path.charAt( 0 ) != '/' ) ? //append the path new File( execPoolDir, path ).getAbsolutePath() : //else absolute path specified path; } return execPoolDir; } /** * Writes out the contents of the replica store as XML document * * @param writer * @param indent * @throws java.io.IOException */ public void toXML( Writer writer, String indent ) throws IOException { String newLine = System.getProperty( "line.separator", "\r\n" ); indent = (indent != null && indent.length() > 0 ) ? indent: ""; String newIndent = indent + "\t"; //iterate through all the entries and spit them out. for( Iterator<SiteCatalogEntry> it = this.entryIterator(); it.hasNext(); ){ it.next().toXML( writer, newIndent ); } //write out the footer writer.write( indent ); writer.write( "</sitecatalog>" ); writer.write( newLine ); } /** * Returns the clone of the object. * * @return the clone */ public Object clone(){ SiteStore obj; try{ obj = ( SiteStore ) super.clone(); obj.initialize(); //iterate through all the entries and spit them out. for( Iterator<SiteCatalogEntry> it = this.entryIterator(); it.hasNext(); ){ obj.addEntry( (SiteCatalogEntry)it.next().clone( )); } } catch( CloneNotSupportedException e ){ //somewhere in the hierarch chain clone is not implemented throw new RuntimeException("Clone not implemented in the base class of " + this.getClass().getName(), e ); } return obj; } /** * Accept method for the SiteStore object * * @param visitor that goes through it */ public void accept(SiteDataVisitor visitor) throws IOException { visitor.visit( this ); for( Iterator<SiteCatalogEntry> it = this.entryIterator(); it.hasNext(); ){ it.next().accept(visitor); } visitor.depart( this ); } /** * Returns a boolean indicating whether the Hashed Output Mapper was used or * not * * @param properties * * @return boolean */ private boolean hashedOutputMapperUsed(PegasusProperties properties) { String mapper = properties.getProperty( OutputMapperFactory.PROPERTY_KEY ); if( mapper == null ){ mapper = OutputMapperFactory.DEFAULT_OUTPUT_MAPPER_IMPLEMENTATION; } return mapper.equals( OutputMapperFactory.HASHED_OUTPUT_MAPPER_IMPLEMENTATION ); } /** * Set the file source. * * @param source the source. */ public void setFileSource(File source) { this.mFileSource = source; } /** * Returns the file source. * * @return */ public File getFileSource(){ return this.mFileSource; } }