extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/grid/ScriptUtil.java

Code
Comments
Other
Rev Date Author Line
4287 09 Jan 17 nicklas 1 package net.sf.basedb.reggie.grid;
4281 20 Dec 16 nicklas 2
6656 29 Mar 22 nicklas 3 import java.io.InputStream;
4599 28 Sep 17 nicklas 4 import java.util.ArrayList;
5619 20 Sep 19 nicklas 5 import java.util.HashMap;
4599 28 Sep 17 nicklas 6 import java.util.List;
5619 20 Sep 19 nicklas 7 import java.util.Map;
4599 28 Sep 17 nicklas 8
4599 28 Sep 17 nicklas 9 import net.sf.basedb.core.BooleanParameterType;
4281 20 Dec 16 nicklas 10 import net.sf.basedb.core.DbControl;
6635 09 Mar 22 nicklas 11 import net.sf.basedb.core.Include;
4599 28 Sep 17 nicklas 12 import net.sf.basedb.core.IntegerParameterType;
4287 09 Jan 17 nicklas 13 import net.sf.basedb.core.InvalidDataException;
6656 29 Mar 22 nicklas 14 import net.sf.basedb.core.ItemNotFoundException;
4599 28 Sep 17 nicklas 15 import net.sf.basedb.core.Job;
4281 20 Dec 16 nicklas 16 import net.sf.basedb.core.Sample;
4599 28 Sep 17 nicklas 17 import net.sf.basedb.core.StringParameterType;
4599 28 Sep 17 nicklas 18 import net.sf.basedb.opengrid.CmdResult;
4599 28 Sep 17 nicklas 19 import net.sf.basedb.opengrid.JobDefinition;
4599 28 Sep 17 nicklas 20 import net.sf.basedb.opengrid.JobStatus;
4599 28 Sep 17 nicklas 21 import net.sf.basedb.opengrid.OpenGrid;
4599 28 Sep 17 nicklas 22 import net.sf.basedb.opengrid.OpenGridCluster;
4599 28 Sep 17 nicklas 23 import net.sf.basedb.opengrid.OpenGridSession;
4281 20 Dec 16 nicklas 24 import net.sf.basedb.opengrid.ScriptBuilder;
7372 06 Oct 23 nicklas 25 import net.sf.basedb.opengrid.config.ClusterType;
7372 06 Oct 23 nicklas 26 import net.sf.basedb.opengrid.config.JobConfig;
6628 07 Mar 22 nicklas 27 import net.sf.basedb.opengrid.filetransfer.InputStreamUploadSource;
6628 07 Mar 22 nicklas 28 import net.sf.basedb.opengrid.filetransfer.UploadSource;
6635 09 Mar 22 nicklas 29 import net.sf.basedb.opengrid.service.OpenGridService;
6628 07 Mar 22 nicklas 30 import net.sf.basedb.reggie.Reggie;
6635 09 Mar 22 nicklas 31 import net.sf.basedb.reggie.XmlConfig;
4281 20 Dec 16 nicklas 32 import net.sf.basedb.reggie.dao.Annotationtype;
4281 20 Dec 16 nicklas 33 import net.sf.basedb.reggie.dao.ReggieItem;
4281 20 Dec 16 nicklas 34 import net.sf.basedb.reggie.dao.Subtype;
4281 20 Dec 16 nicklas 35 import net.sf.basedb.reggie.projectarchive.FilePermission;
4281 20 Dec 16 nicklas 36
4281 20 Dec 16 nicklas 37 /**
4281 20 Dec 16 nicklas 38   Utility methods for script generation.
4281 20 Dec 16 nicklas 39
4281 20 Dec 16 nicklas 40   @author nicklas
4281 20 Dec 16 nicklas 41   @since 4.xx
4281 20 Dec 16 nicklas 42 */
4281 20 Dec 16 nicklas 43 public final class ScriptUtil 
4281 20 Dec 16 nicklas 44 {
4281 20 Dec 16 nicklas 45   
4281 20 Dec 16 nicklas 46   // No instances
4281 20 Dec 16 nicklas 47   private ScriptUtil()
4281 20 Dec 16 nicklas 48   {}
4281 20 Dec 16 nicklas 49   
4281 20 Dec 16 nicklas 50   /**
4281 20 Dec 16 nicklas 51     Utility method for setting the 'umask' depending on the
4281 20 Dec 16 nicklas 52     consent on the case. The method will search for a
4281 20 Dec 16 nicklas 53     parent "Case" item and check if the "Consent" annotation
4281 20 Dec 16 nicklas 54     is set to yes. The "umask" is then set:
4281 20 Dec 16 nicklas 55     
4281 20 Dec 16 nicklas 56      * Yes: umask -S u=rwx,g=rx,o= 
4281 20 Dec 16 nicklas 57      * No: umask -S u=rwx,g=,o= 
4281 20 Dec 16 nicklas 58   
5595 11 Sep 19 nicklas 59     If an external group is given, the consent is not checked, but it is assumed that the permission
5595 11 Sep 19 nicklas 60     should be set to {@link FilePermission#EXTERNAL_GROUP_READABLE}.
5595 11 Sep 19 nicklas 61   
4281 20 Dec 16 nicklas 62     @return TRUE if there is a consent, FALSE if not
4281 20 Dec 16 nicklas 63   */
5595 11 Sep 19 nicklas 64   public static FilePermission setUmaskForItem(DbControl dc, ReggieItem<?> item, String externalGroup, ScriptBuilder script)
4281 20 Dec 16 nicklas 65   {
5595 11 Sep 19 nicklas 66     FilePermission permission = FilePermission.NO_CONSENT;
5595 11 Sep 19 nicklas 67     if (externalGroup != null)
5595 11 Sep 19 nicklas 68     {
5595 11 Sep 19 nicklas 69       permission = FilePermission.EXTERNAL_GROUP_READABLE;
5595 11 Sep 19 nicklas 70     }
5595 11 Sep 19 nicklas 71     else
5595 11 Sep 19 nicklas 72     {
5595 11 Sep 19 nicklas 73       Sample theCase = (Sample)item.findSingleParent(dc, Subtype.CASE);
5595 11 Sep 19 nicklas 74       if ("Yes".equals(Annotationtype.CONSENT.getAnnotationValue(dc, theCase)))
5595 11 Sep 19 nicklas 75       {
5595 11 Sep 19 nicklas 76         permission = FilePermission.CONSENT;
5595 11 Sep 19 nicklas 77       }
5595 11 Sep 19 nicklas 78     }
5595 11 Sep 19 nicklas 79     script.cmd("umask -S "+permission.getUmask());
5595 11 Sep 19 nicklas 80     return permission;
4281 20 Dec 16 nicklas 81   }
4281 20 Dec 16 nicklas 82
4287 09 Jan 17 nicklas 83   /**
5930 06 May 20 nicklas 84     Add a 'chgrp' command to set the group of the given 'folder' and all files and subdirectories in it.
5930 06 May 20 nicklas 85     If a 'parentFolder' is given and is a parent folder of the 'folder' a 'chgrp' command is added
5930 06 May 20 nicklas 86     to parent folder up to (but not including) the parent folder. Errors are logged to ${WD}/chgrp.out.
5930 06 May 20 nicklas 87     'itemName' is used for logging errors only
5930 06 May 20 nicklas 88     @since 4.27
5930 06 May 20 nicklas 89   */
5930 06 May 20 nicklas 90   public static void addChgrp(String groupName, String folder, String itemName, String parentFolder, ScriptBuilder script)
5930 06 May 20 nicklas 91   {
5930 06 May 20 nicklas 92     // Make sure parent folder ends with '/' and folder doesn't so that the while loop breaks correctly
5930 06 May 20 nicklas 93     if (folder.endsWith("/")) folder = folder.substring(0, folder.length()-1);
5930 06 May 20 nicklas 94     if (parentFolder != null && !parentFolder.endsWith("/")) parentFolder += "/";
5930 06 May 20 nicklas 95
5930 06 May 20 nicklas 96     script.cmd("chgrp -R " + groupName + " " + folder + " 2>> ${WD}/chgrp.out || echo [" + itemName +"] >> ${WD}/chgrp.out");
5930 06 May 20 nicklas 97
5930 06 May 20 nicklas 98     while (parentFolder != null)
5930 06 May 20 nicklas 99     {
5930 06 May 20 nicklas 100       int last = folder.lastIndexOf('/');
5930 06 May 20 nicklas 101       if (last == -1) break;
5930 06 May 20 nicklas 102       folder = folder.substring(0, last); // Move up one directory: /foo/bar/x --> /foo/bar
5930 06 May 20 nicklas 103       // Check if we have reached the parent folder
5930 06 May 20 nicklas 104       if (folder.indexOf(parentFolder) == -1) break;
5930 06 May 20 nicklas 105       
5930 06 May 20 nicklas 106       script.cmd("chgrp " + groupName + " " + folder + " 2>> ${WD}/chgrp.out || echo [" + itemName +"] >> ${WD}/chgrp.out");
5930 06 May 20 nicklas 107     }
5930 06 May 20 nicklas 108   }
5930 06 May 20 nicklas 109   
5930 06 May 20 nicklas 110   /**
4287 09 Jan 17 nicklas 111     We only allow letters, numbers, dot, underscore and forward slash in path names.
4287 09 Jan 17 nicklas 112     The path must be at least two levels and not contain a double dot (..).
4287 09 Jan 17 nicklas 113     @since 4.2
4287 09 Jan 17 nicklas 114   */
4287 09 Jan 17 nicklas 115   public static String checkValidPath(String path, boolean isDirectory, boolean twoLevels)
4287 09 Jan 17 nicklas 116   {
4287 09 Jan 17 nicklas 117     if (path == null)
4287 09 Jan 17 nicklas 118     {
4287 09 Jan 17 nicklas 119       throw new NullPointerException("path");
4287 09 Jan 17 nicklas 120     }
4287 09 Jan 17 nicklas 121     // Get rid of trailing '/'
4287 09 Jan 17 nicklas 122     if (path.endsWith("/")) path = path.substring(0, path.length()-1);
4287 09 Jan 17 nicklas 123     // Check valid characters
4287 09 Jan 17 nicklas 124     if (!path.matches("[a-zA-Z0-9._\\-/]+"))
4287 09 Jan 17 nicklas 125     {
4287 09 Jan 17 nicklas 126       throw new InvalidDataException("Path may only contain [a-zA-Z0-9._-/]: " + path);
4287 09 Jan 17 nicklas 127     }
4287 09 Jan 17 nicklas 128     if (twoLevels)
4287 09 Jan 17 nicklas 129     {
4287 09 Jan 17 nicklas 130       // Path must be at least two levels
4287 09 Jan 17 nicklas 131       if (!path.matches("/[a-zA-Z0-9._\\-]+/[a-zA-Z0-9._\\-].*"))
4287 09 Jan 17 nicklas 132       {
4287 09 Jan 17 nicklas 133         throw new InvalidDataException("Path must be at least two levels deep: " + path);
4287 09 Jan 17 nicklas 134       }
4287 09 Jan 17 nicklas 135     }
4287 09 Jan 17 nicklas 136     else
4287 09 Jan 17 nicklas 137     {
4287 09 Jan 17 nicklas 138       // Path must start with '/'
4287 09 Jan 17 nicklas 139       if (!path.startsWith("/")) 
4287 09 Jan 17 nicklas 140       {
4287 09 Jan 17 nicklas 141         throw new InvalidDataException("Path must start with '/': " + path);
4287 09 Jan 17 nicklas 142       }
4287 09 Jan 17 nicklas 143     }
4287 09 Jan 17 nicklas 144     // Not allowed to move up to parent directory
4287 09 Jan 17 nicklas 145     if (path.contains(".."))
4287 09 Jan 17 nicklas 146     {
4287 09 Jan 17 nicklas 147       throw new InvalidDataException("Path may not contain '..': " + path);
4287 09 Jan 17 nicklas 148     }
4287 09 Jan 17 nicklas 149     return path;
4287 09 Jan 17 nicklas 150   }
4287 09 Jan 17 nicklas 151   
4287 09 Jan 17 nicklas 152   /**
4287 09 Jan 17 nicklas 153     Checks that a value is allowed as a file/directory name.
4287 09 Jan 17 nicklas 154     We only allow letters, numbers, dot and underscore names.
4287 09 Jan 17 nicklas 155     It may not contain a double dot (..).
4287 09 Jan 17 nicklas 156     @since 4.2
4287 09 Jan 17 nicklas 157   */
4287 09 Jan 17 nicklas 158   public static String checkValidFilename(String filename)
4287 09 Jan 17 nicklas 159   {
4287 09 Jan 17 nicklas 160     if (filename == null)
4287 09 Jan 17 nicklas 161     {
4287 09 Jan 17 nicklas 162       throw new NullPointerException("filename");
4287 09 Jan 17 nicklas 163     }
4287 09 Jan 17 nicklas 164     if (!filename.matches("[a-zA-Z0-9._\\-]+"))
4287 09 Jan 17 nicklas 165     {
4287 09 Jan 17 nicklas 166       throw new InvalidDataException("File name may only contain [a-zA-Z0-9._-]: " + filename);
4287 09 Jan 17 nicklas 167     }
4287 09 Jan 17 nicklas 168     // Not allowed to move up to parent directory
4287 09 Jan 17 nicklas 169     if (filename.contains(".."))
4287 09 Jan 17 nicklas 170     {
4287 09 Jan 17 nicklas 171       throw new InvalidDataException("File name may not contain '..': " + filename);
4287 09 Jan 17 nicklas 172     }
4287 09 Jan 17 nicklas 173     return filename;
4287 09 Jan 17 nicklas 174   }
4287 09 Jan 17 nicklas 175   
4287 09 Jan 17 nicklas 176   /**
4287 09 Jan 17 nicklas 177     Script parameters may only contain letters, numbers, dot, underscore and hyphen.
4287 09 Jan 17 nicklas 178     @since 4.2
4287 09 Jan 17 nicklas 179   */
4287 09 Jan 17 nicklas 180   public static String checkValidScriptParameter(String param)
4287 09 Jan 17 nicklas 181   {
4287 09 Jan 17 nicklas 182     if (param != null && !param.matches("[a-zA-Z0-9._\\-]+"))
4287 09 Jan 17 nicklas 183     {
4287 09 Jan 17 nicklas 184       throw new InvalidDataException("Parameter may only contain [a-zA-Z0-9._-]: " + param);
4287 09 Jan 17 nicklas 185     }
4287 09 Jan 17 nicklas 186     return param;
4287 09 Jan 17 nicklas 187   }
4599 28 Sep 17 nicklas 188   
4599 28 Sep 17 nicklas 189   /**
7372 06 Oct 23 nicklas 190     Parse the submitOptions string and add as options to the job configuration.
7372 06 Oct 23 nicklas 191     The submitOptions must be a string with one option per line that contains a key 
7372 06 Oct 23 nicklas 192     and value or only a key in the format: key=value | key
7372 06 Oct 23 nicklas 193     Lines starting with '#' are ignored.
7372 06 Oct 23 nicklas 194     
7372 06 Oct 23 nicklas 195      @since 4.49.3
7372 06 Oct 23 nicklas 196   */
7372 06 Oct 23 nicklas 197   public static void addSubmitOptions(JobConfig job, String submitOptions, ClusterType clusterType)
7372 06 Oct 23 nicklas 198   {
7372 06 Oct 23 nicklas 199     if (submitOptions == null || submitOptions.length() == 0) return;
7372 06 Oct 23 nicklas 200     for (String keyVal : submitOptions.split("\\n"))
7372 06 Oct 23 nicklas 201     {
7372 06 Oct 23 nicklas 202       String[] kv = keyVal.strip().split("\\=", 2);
7372 06 Oct 23 nicklas 203       String key = kv[0];
7372 06 Oct 23 nicklas 204       // ignore empty lines and lines starting with #
7372 06 Oct 23 nicklas 205       if (key.length() == 0 || key.startsWith("#")) continue;
7372 06 Oct 23 nicklas 206       
7372 06 Oct 23 nicklas 207       String val = kv.length == 2 ? kv[1] : "";
7372 06 Oct 23 nicklas 208       if (clusterType == ClusterType.SLURM)
7372 06 Oct 23 nicklas 209       {
7372 06 Oct 23 nicklas 210         job.setSbatchOption(key, val);
7372 06 Oct 23 nicklas 211       }
7372 06 Oct 23 nicklas 212       else if (clusterType == ClusterType.OPENGRID)
7372 06 Oct 23 nicklas 213       {
7372 06 Oct 23 nicklas 214         job.setQsubOption(key, val);
7372 06 Oct 23 nicklas 215       }
7372 06 Oct 23 nicklas 216     }
7372 06 Oct 23 nicklas 217   }
7372 06 Oct 23 nicklas 218   
7372 06 Oct 23 nicklas 219   /**
4599 28 Sep 17 nicklas 220     Submit the list of job definitions to the specified cluster.
4599 28 Sep 17 nicklas 221     @since 4.12
4599 28 Sep 17 nicklas 222   */
4599 28 Sep 17 nicklas 223   public static List<Job> submitJobs(DbControl dc, OpenGridCluster cluster, List<JobDefinition> jobDefs)
4599 28 Sep 17 nicklas 224   {
4599 28 Sep 17 nicklas 225     OpenGridSession ogSession = null;
4599 28 Sep 17 nicklas 226     List<Job> jobs = new ArrayList<>(jobDefs.size());
4664 30 Jan 18 nicklas 227     if (jobDefs.size() > 0)
4599 28 Sep 17 nicklas 228     {
4661 29 Jan 18 nicklas 229       try
4599 28 Sep 17 nicklas 230       {
4661 29 Jan 18 nicklas 231         ogSession = cluster.connect(5);
4661 29 Jan 18 nicklas 232         // Submit jobs to cluster
4661 29 Jan 18 nicklas 233         CmdResult<List<JobStatus>> qsub = ogSession.qsub(dc, jobDefs);
4661 29 Jan 18 nicklas 234         qsub.throwExceptionIfNonZeroExitStatus();
4599 28 Sep 17 nicklas 235         
4661 29 Jan 18 nicklas 236         StringParameterType sType = new StringParameterType();
4661 29 Jan 18 nicklas 237         BooleanParameterType bType = new BooleanParameterType();
4661 29 Jan 18 nicklas 238         IntegerParameterType iType = new IntegerParameterType();
4661 29 Jan 18 nicklas 239         for (int jobNo = 0; jobNo < jobDefs.size(); jobNo++)
4599 28 Sep 17 nicklas 240         {
4661 29 Jan 18 nicklas 241           JobStatus status = qsub.getResult().get(jobNo);
4661 29 Jan 18 nicklas 242           JobDefinition jobDef = jobDefs.get(jobNo);
4661 29 Jan 18 nicklas 243           Job job = jobDef.getBaseJob();
4661 29 Jan 18 nicklas 244           jobs.add(job);
4661 29 Jan 18 nicklas 245           
4661 29 Jan 18 nicklas 246           job.setParameterValue("jobName", sType, status.getName());
4661 29 Jan 18 nicklas 247           if (jobDef.getDebug())
4661 29 Jan 18 nicklas 248           {
4661 29 Jan 18 nicklas 249             job.setParameterValue("debug", bType, jobDef.getDebug());
4661 29 Jan 18 nicklas 250           }
4661 29 Jan 18 nicklas 251           if (jobDef.getConfig().getPriority() != null)
4661 29 Jan 18 nicklas 252           {
4661 29 Jan 18 nicklas 253             job.setParameterValue("priority", iType, jobDef.getConfig().getPriority().intValue());
4661 29 Jan 18 nicklas 254           }
4661 29 Jan 18 nicklas 255     
4661 29 Jan 18 nicklas 256           String jobId = status.getJobIdentifier().getClusterJobId();
4661 29 Jan 18 nicklas 257           if (jobId == null || jobId.equals(""))
4661 29 Jan 18 nicklas 258           {
4661 29 Jan 18 nicklas 259             job.doneError("Cluster returned no job-id for this job");
4661 29 Jan 18 nicklas 260             continue;
4661 29 Jan 18 nicklas 261           }
4599 28 Sep 17 nicklas 262         }
4599 28 Sep 17 nicklas 263       }
4661 29 Jan 18 nicklas 264       finally
4661 29 Jan 18 nicklas 265       {
4661 29 Jan 18 nicklas 266         OpenGrid.close(ogSession);
4661 29 Jan 18 nicklas 267       }
4599 28 Sep 17 nicklas 268     }
4599 28 Sep 17 nicklas 269     return jobs;
4599 28 Sep 17 nicklas 270   }
4287 09 Jan 17 nicklas 271
5619 20 Sep 19 nicklas 272   /**
5619 20 Sep 19 nicklas 273     Parse errors from 'chgrp' command that is used to share files for external samples.
5619 20 Sep 19 nicklas 274
5619 20 Sep 19 nicklas 275     The file is expected to contain output to stderr from 'chgrp' followed by the 
5619 20 Sep 19 nicklas 276     name of the item (inside brackets) the errors are related to:
5619 20 Sep 19 nicklas 277     
5619 20 Sep 19 nicklas 278     chgrp: changing group of `/...foobar/lib.g': Operation not permitted
5619 20 Sep 19 nicklas 279     [foobar.lib.g]
5619 20 Sep 19 nicklas 280     
5619 20 Sep 19 nicklas 281     This parser will pick up item names and use for keys in a map with the
5619 20 Sep 19 nicklas 282     preceeding line as value:
5619 20 Sep 19 nicklas 283     
5619 20 Sep 19 nicklas 284     foobar.lib.g --> chgrp: changing group of `/...foobar/lib.g': Operation not permitted
5619 20 Sep 19 nicklas 285     
5619 20 Sep 19 nicklas 286     @since 4.24
5619 20 Sep 19 nicklas 287   */
5619 20 Sep 19 nicklas 288   public static Map<String, String> parseChgrpErrors(String chgrp)
5619 20 Sep 19 nicklas 289   {
5619 20 Sep 19 nicklas 290     Map<String, String> errors = new HashMap<>();
5619 20 Sep 19 nicklas 291     if (chgrp != null)
5619 20 Sep 19 nicklas 292     {
5619 20 Sep 19 nicklas 293       String[] lines = chgrp.split("\n");
5619 20 Sep 19 nicklas 294       for (int lineNo = 1; lineNo < lines.length; lineNo++)
5619 20 Sep 19 nicklas 295       {
5619 20 Sep 19 nicklas 296         String line = lines[lineNo];
5619 20 Sep 19 nicklas 297         if (line.length() > 3 && line.startsWith("[") && line.endsWith("]"))
5619 20 Sep 19 nicklas 298         {
5619 20 Sep 19 nicklas 299           String name = line.substring(1, line.length()-1);
5619 20 Sep 19 nicklas 300           String msg = lines[lineNo-1];
5619 20 Sep 19 nicklas 301           errors.put(name, msg);
5619 20 Sep 19 nicklas 302         }
5619 20 Sep 19 nicklas 303       }
5619 20 Sep 19 nicklas 304     }
5619 20 Sep 19 nicklas 305     return errors;
5619 20 Sep 19 nicklas 306   }
4287 09 Jan 17 nicklas 307   
6628 07 Mar 22 nicklas 308   /**
6635 09 Mar 22 nicklas 309     Check if the current cluster has been configured with the specified configuration option.
6635 09 Mar 22 nicklas 310     If not, search among all configured clusters and return the first one that is connected
6635 09 Mar 22 nicklas 311     and configured. If no such cluster is found, the current one is returned in any case.
6635 09 Mar 22 nicklas 312     
6635 09 Mar 22 nicklas 313     This method is intended to be used by auto-confirm handlers to check if the current cluster
6635 09 Mar 22 nicklas 314     can continue with the next step or if the pipeline need to move to another cluster. Typically
6635 09 Mar 22 nicklas 315     a top-level configuration section is checked, for example, 'demux' or 'align-hisat'.
6635 09 Mar 22 nicklas 316     
6635 09 Mar 22 nicklas 317     @since 4.38
6635 09 Mar 22 nicklas 318   */
6635 09 Mar 22 nicklas 319   public static OpenGridCluster autoSelectClusterWithConfig(DbControl dc, String xpath, OpenGridCluster current)
6635 09 Mar 22 nicklas 320   {
6635 09 Mar 22 nicklas 321     List<OpenGridCluster> clusters = new ArrayList<>();
7228 01 Jun 23 nicklas 322     if (current != null) clusters.add(current);
6635 09 Mar 22 nicklas 323     clusters.addAll(OpenGridService.getInstance().getClusters(dc, Include.ALL));
6635 09 Mar 22 nicklas 324     for (OpenGridCluster cluster : clusters)
6635 09 Mar 22 nicklas 325     {
6635 09 Mar 22 nicklas 326       if (!cluster.getClusterInfo().isConnected()) continue;
6635 09 Mar 22 nicklas 327       XmlConfig cfg = Reggie.getConfig(cluster.getId());
6635 09 Mar 22 nicklas 328       if (cfg != null && cfg.getElement(xpath) != null) 
6635 09 Mar 22 nicklas 329       {
6635 09 Mar 22 nicklas 330         // This cluster is connected and configured
6635 09 Mar 22 nicklas 331         return cluster;
6635 09 Mar 22 nicklas 332       }
6635 09 Mar 22 nicklas 333     }
6635 09 Mar 22 nicklas 334     return current;
6635 09 Mar 22 nicklas 335   }
6635 09 Mar 22 nicklas 336   
6635 09 Mar 22 nicklas 337   /**
6628 07 Mar 22 nicklas 338     Create an upload source instance for uploading a static script
6628 07 Mar 22 nicklas 339     inside the reggie.jar file. The path is either a full reference
6628 07 Mar 22 nicklas 340     to file or a the name of a file in the net.sf.basedb.reggie.grid.scripts
6628 07 Mar 22 nicklas 341     package.
6628 07 Mar 22 nicklas 342     @since 4.38
6628 07 Mar 22 nicklas 343   */
6628 07 Mar 22 nicklas 344   public static UploadSource upload(String path)
6628 07 Mar 22 nicklas 345   {
6628 07 Mar 22 nicklas 346     int last = path.lastIndexOf('/');
6628 07 Mar 22 nicklas 347     String name = path;
6628 07 Mar 22 nicklas 348     if (last > 0)
6628 07 Mar 22 nicklas 349     {
6628 07 Mar 22 nicklas 350       name = path.substring(last+1);
6628 07 Mar 22 nicklas 351     }
6628 07 Mar 22 nicklas 352     else
6628 07 Mar 22 nicklas 353     {
6628 07 Mar 22 nicklas 354       path = "/net/sf/basedb/reggie/grid/scripts/"+name;
6628 07 Mar 22 nicklas 355     }
6656 29 Mar 22 nicklas 356     InputStream in = Reggie.class.getResourceAsStream(path);
6656 29 Mar 22 nicklas 357     if (in == null) throw new ItemNotFoundException(path);
6656 29 Mar 22 nicklas 358     return new InputStreamUploadSource(name, in);
6628 07 Mar 22 nicklas 359   }
6628 07 Mar 22 nicklas 360   
6653 23 Mar 22 nicklas 361   /**
6662 01 Apr 22 nicklas 362     Get a script file as a string. The path is either a full reference
6662 01 Apr 22 nicklas 363     to file or a the name of a file in the net.sf.basedb.reggie.grid.scripts
6662 01 Apr 22 nicklas 364     package.
6662 01 Apr 22 nicklas 365     @since 4.38
6662 01 Apr 22 nicklas 366   */
6662 01 Apr 22 nicklas 367   public static String getAsString(String path)
6662 01 Apr 22 nicklas 368   {
6662 01 Apr 22 nicklas 369     int last = path.lastIndexOf('/');
6662 01 Apr 22 nicklas 370     String name = path;
6662 01 Apr 22 nicklas 371     if (last > 0)
6662 01 Apr 22 nicklas 372     {
6662 01 Apr 22 nicklas 373       name = path.substring(last+1);
6662 01 Apr 22 nicklas 374     }
6662 01 Apr 22 nicklas 375     else
6662 01 Apr 22 nicklas 376     {
6662 01 Apr 22 nicklas 377       path = "/net/sf/basedb/reggie/grid/scripts/"+name;
6662 01 Apr 22 nicklas 378     }
6662 01 Apr 22 nicklas 379     return Reggie.getTemplateFile(path);
6662 01 Apr 22 nicklas 380   }
6662 01 Apr 22 nicklas 381   
6662 01 Apr 22 nicklas 382   /**
6653 23 Mar 22 nicklas 383     If the string is a multi-line string, remove white-space from the beginning of
6653 23 Mar 22 nicklas 384     each line so that they 
6653 23 Mar 22 nicklas 385     @since 4.38
6653 23 Mar 22 nicklas 386   */
6653 23 Mar 22 nicklas 387   public static String multilineIndent(String in)
6653 23 Mar 22 nicklas 388   {
6657 30 Mar 22 nicklas 389     if (in == null) return null;
6653 23 Mar 22 nicklas 390     StringBuilder out = new StringBuilder();
6653 23 Mar 22 nicklas 391     for (String l : in.split("\n"))
6653 23 Mar 22 nicklas 392     {
6653 23 Mar 22 nicklas 393       out.append(l.stripLeading()+"\n");
6653 23 Mar 22 nicklas 394     }
6653 23 Mar 22 nicklas 395     return out.toString();
6653 23 Mar 22 nicklas 396   }
4281 20 Dec 16 nicklas 397 }