extensions/net.sf.basedb.opengrid/trunk/src/net/sf/basedb/opengrid/config/JobConfig.java

Code
Comments
Other
Rev Date Author Line
4254 25 Nov 16 nicklas 1 package net.sf.basedb.opengrid.config;
4234 11 Nov 16 nicklas 2
4254 25 Nov 16 nicklas 3 import java.util.Collections;
4234 11 Nov 16 nicklas 4 import java.util.LinkedHashMap;
4234 11 Nov 16 nicklas 5 import java.util.Map;
4297 13 Jan 17 nicklas 6 import java.util.Set;
5991 20 Aug 20 nicklas 7 import java.util.regex.Matcher;
5991 20 Aug 20 nicklas 8 import java.util.regex.Pattern;
4234 11 Nov 16 nicklas 9
4254 25 Nov 16 nicklas 10 import net.sf.basedb.opengrid.JobDefinition;
6629 07 Mar 22 nicklas 11 import net.sf.basedb.opengrid.ScriptBuilder;
4254 25 Nov 16 nicklas 12
4234 11 Nov 16 nicklas 13 /**
4234 11 Nov 16 nicklas 14   Configuration settings related to a job definition that is
5991 20 Aug 20 nicklas 15   about to be run on an Open Grid or Slurm cluster. The information 
5991 20 Aug 20 nicklas 16   becomes readonly when a {@link JobDefinition} instance has been created.
4234 11 Nov 16 nicklas 17   A single configuration instance can be used with multiple jobs.
5991 20 Aug 20 nicklas 18   <p>
5991 20 Aug 20 nicklas 19   Note that options for the Open Grid Engine and Slurm can be very
5991 20 Aug 20 nicklas 20   different and it is best if the client uses either {@link #setQsubOption(String, String)}
5991 20 Aug 20 nicklas 21   or {@link #setSbatchOption(String, String)} depending on the type of the 
5991 20 Aug 20 nicklas 22   cluster.
5991 20 Aug 20 nicklas 23   <p>
5991 20 Aug 20 nicklas 24   Some options can be automatically converted between the two systems. This will 
5991 20 Aug 20 nicklas 25   only happen if options for one system has been set, but no options for the other.
5991 20 Aug 20 nicklas 26   It is also possible to force conversion by directly calling
5991 20 Aug 20 nicklas 27   {@link #convertQsubToSbatchOptions()} or {@link #convertSbatchToQsubOptions()}.
4234 11 Nov 16 nicklas 28   
4234 11 Nov 16 nicklas 29   @author nicklas
4234 11 Nov 16 nicklas 30   @since 1.0
4234 11 Nov 16 nicklas 31 */
4234 11 Nov 16 nicklas 32 public class JobConfig 
4234 11 Nov 16 nicklas 33   extends AbstractLockable<JobConfig>
4234 11 Nov 16 nicklas 34 {
4234 11 Nov 16 nicklas 35   /**
4234 11 Nov 16 nicklas 36     A (locked) configuration instance with default
6672 11 Apr 22 nicklas 37     settings and no batch configuration.
4234 11 Nov 16 nicklas 38   */
6672 11 Apr 22 nicklas 39   public static final JobConfig DEFAULT = new JobConfig(true, null).lock();
4234 11 Nov 16 nicklas 40   
4234 11 Nov 16 nicklas 41   /**
4234 11 Nov 16 nicklas 42     Convert an Open Grid job priority value to a BASE job
4234 11 Nov 16 nicklas 43     priority value.
4301 13 Jan 17 nicklas 44      * 1024 -- 1 → 1 -- 4
4301 13 Jan 17 nicklas 45     * 0 or missing → 5
4301 13 Jan 17 nicklas 46     * -1 -- -1023 → 6 -- 10
4234 11 Nov 16 nicklas 47   */
4234 11 Nov 16 nicklas 48   public static int openGridPriorityToBasePriority(Integer priority)
4234 11 Nov 16 nicklas 49   {
4234 11 Nov 16 nicklas 50     if (priority == null || priority == 0) return 5;
4234 11 Nov 16 nicklas 51     for (Priority p : Priority.values())
4234 11 Nov 16 nicklas 52     {
4234 11 Nov 16 nicklas 53       if (priority >= p.inOpenGrid) return p.inBase;
4234 11 Nov 16 nicklas 54     }
4234 11 Nov 16 nicklas 55     return 5;
4234 11 Nov 16 nicklas 56   }
4234 11 Nov 16 nicklas 57   
4234 11 Nov 16 nicklas 58   /**
5987 18 Aug 20 nicklas 59     Convert an Open Grid job priority value to a 'nice' value
5987 18 Aug 20 nicklas 60     used in Slurm. The main difference is that the scale is
5987 18 Aug 20 nicklas 61     reversed. Eg. a positive priority value mean a negative
5987 18 Aug 20 nicklas 62     nice value. Note that in Slurm the range for 'nice' is between
5987 18 Aug 20 nicklas 63     +/- 2147483645, but in order to keep things simple we
5987 18 Aug 20 nicklas 64     only reverse the sign. Null values are returned as null.
5987 18 Aug 20 nicklas 65     @since 1.4
5987 18 Aug 20 nicklas 66   */
5987 18 Aug 20 nicklas 67   public static Integer openGridPriorityToSlurmNice(Integer priority)
5987 18 Aug 20 nicklas 68   {
5987 18 Aug 20 nicklas 69     return priority == null ? null : -priority;
5987 18 Aug 20 nicklas 70   }
5987 18 Aug 20 nicklas 71   
5987 18 Aug 20 nicklas 72   /**
5987 18 Aug 20 nicklas 73     Convert a Slurm nice value to Open Grid priority. To keep
5987 18 Aug 20 nicklas 74     things simple we only reverse the sign. If the resulting
5987 18 Aug 20 nicklas 75     priority is outside the allowed range (-1023..+1024), 
5987 18 Aug 20 nicklas 76     the value is automatically changed to the nearest limit.
5987 18 Aug 20 nicklas 77     @see #openGridPriorityToSlurmNice(Integer)
5987 18 Aug 20 nicklas 78     @since 1.4
5987 18 Aug 20 nicklas 79   */
5987 18 Aug 20 nicklas 80   public static Integer slurmNiceToOpenGridPriority(Integer nice)
5987 18 Aug 20 nicklas 81   {
5987 18 Aug 20 nicklas 82     return nice == null ? null : Math.min(1024, Math.max(-1023, -nice));
5987 18 Aug 20 nicklas 83   }
5987 18 Aug 20 nicklas 84   
5987 18 Aug 20 nicklas 85   /**
4234 11 Nov 16 nicklas 86     Convert a BASE job priority value to an Open Grid job priority value.
4301 13 Jan 17 nicklas 87      * 1-4 → 1024 -- 1
4301 13 Jan 17 nicklas 88     * 5 or missing → 0
4301 13 Jan 17 nicklas 89     * 6-10 → -1 -- -1023
4234 11 Nov 16 nicklas 90   */
4234 11 Nov 16 nicklas 91   public static int basePriorityToOpenGridPriority(Integer priority)
4234 11 Nov 16 nicklas 92   {
4234 11 Nov 16 nicklas 93     if (priority == null || priority == 5) return 0;
4234 11 Nov 16 nicklas 94     for (Priority p : Priority.values())
4234 11 Nov 16 nicklas 95     {
4234 11 Nov 16 nicklas 96       if (p.inBase == priority) return p.inOpenGrid;
4234 11 Nov 16 nicklas 97     }
4234 11 Nov 16 nicklas 98     return 0;
4234 11 Nov 16 nicklas 99   }
4234 11 Nov 16 nicklas 100   
4234 11 Nov 16 nicklas 101   private Integer priority;
5987 18 Aug 20 nicklas 102   private Integer nice;
4234 11 Nov 16 nicklas 103   
4234 11 Nov 16 nicklas 104   private Map<String, String> qsubOptions;
5989 19 Aug 20 nicklas 105   private Map<String, String> sbatchOptions;
5991 20 Aug 20 nicklas 106   private boolean autoConvertOptions;
4234 11 Nov 16 nicklas 107   
6672 11 Apr 22 nicklas 108   private BatchConfig batchConfig;
6672 11 Apr 22 nicklas 109   
4234 11 Nov 16 nicklas 110   /**
4234 11 Nov 16 nicklas 111     Create a new job configuration instance with default settings.
4234 11 Nov 16 nicklas 112   */
4234 11 Nov 16 nicklas 113   public JobConfig()
4234 11 Nov 16 nicklas 114   {
5991 20 Aug 20 nicklas 115     this(true);
5991 20 Aug 20 nicklas 116   }
5991 20 Aug 20 nicklas 117   
5991 20 Aug 20 nicklas 118   /**
5991 20 Aug 20 nicklas 119     Create a new job configuration instance.
5991 20 Aug 20 nicklas 120     @param autoConvertOptions If TRUE, options for qsub or sbatch are automatically
5991 20 Aug 20 nicklas 121       converted
5991 20 Aug 20 nicklas 122     @since 1.4
5991 20 Aug 20 nicklas 123   */
5991 20 Aug 20 nicklas 124   public JobConfig(boolean autoConvertOptions)
5991 20 Aug 20 nicklas 125   {
6672 11 Apr 22 nicklas 126     this(autoConvertOptions, new BatchConfig());
6672 11 Apr 22 nicklas 127   }
6672 11 Apr 22 nicklas 128
6672 11 Apr 22 nicklas 129   private JobConfig(boolean autoConvertOptions, BatchConfig batchConfig)
6672 11 Apr 22 nicklas 130   {
4234 11 Nov 16 nicklas 131     this.priority = null;
5987 18 Aug 20 nicklas 132     this.nice = null;
4234 11 Nov 16 nicklas 133     this.qsubOptions = new LinkedHashMap<>();
5989 19 Aug 20 nicklas 134     this.sbatchOptions = new LinkedHashMap<>();
5991 20 Aug 20 nicklas 135     this.autoConvertOptions = autoConvertOptions;
6672 11 Apr 22 nicklas 136     this.batchConfig = batchConfig;
4234 11 Nov 16 nicklas 137   }
6672 11 Apr 22 nicklas 138   
4234 11 Nov 16 nicklas 139   /**
6672 11 Apr 22 nicklas 140     Set the batch configuration to use with this job configuration.
6672 11 Apr 22 nicklas 141     @since 1.5
6672 11 Apr 22 nicklas 142   */
6672 11 Apr 22 nicklas 143   public void setBatchConfig(BatchConfig batchConfig)
6672 11 Apr 22 nicklas 144   {
6672 11 Apr 22 nicklas 145     checkLocked("setBatchConfig()");
6672 11 Apr 22 nicklas 146     this.batchConfig = batchConfig;
6672 11 Apr 22 nicklas 147   }
6672 11 Apr 22 nicklas 148   public BatchConfig getBatchConfig()
6672 11 Apr 22 nicklas 149   {
6672 11 Apr 22 nicklas 150     return batchConfig;
6672 11 Apr 22 nicklas 151   }
6672 11 Apr 22 nicklas 152   
6672 11 Apr 22 nicklas 153   /**
4234 11 Nov 16 nicklas 154     Set this option if the job script should exit as soon as an
4234 11 Nov 16 nicklas 155     error is encountered. This is on by default.
6624 04 Mar 22 nicklas 156     @deprecated In 1.5, "set -e' is no longer included in the generated scripts automatically
4234 11 Nov 16 nicklas 157   */
6624 04 Mar 22 nicklas 158   @Deprecated
4234 11 Nov 16 nicklas 159   public void setFailImmediately(boolean failImmediately)
6624 04 Mar 22 nicklas 160   {}
6624 04 Mar 22 nicklas 161   @Deprecated
4234 11 Nov 16 nicklas 162   public boolean getFailImmediately()
4234 11 Nov 16 nicklas 163   {
6624 04 Mar 22 nicklas 164     return false;
4234 11 Nov 16 nicklas 165   }
4234 11 Nov 16 nicklas 166
4234 11 Nov 16 nicklas 167   /**
4267 15 Dec 16 nicklas 168     Set this option to automatically include
4267 15 Dec 16 nicklas 169     a "umask" command that makes all files created
4267 15 Dec 16 nicklas 170     by the job script private (eg, umask u=rwx,g=,o=)
4267 15 Dec 16 nicklas 171     This is on by default.
6624 04 Mar 22 nicklas 172     @deprecated In 1.5, "umask" is no longer included in the generated scripts
4267 15 Dec 16 nicklas 173   */
6624 04 Mar 22 nicklas 174   @Deprecated
4267 15 Dec 16 nicklas 175   public void setCreatePrivateFiles(boolean privateFiles)
6624 04 Mar 22 nicklas 176   {}
6624 04 Mar 22 nicklas 177   @Deprecated
4267 15 Dec 16 nicklas 178   public boolean getCreatePrivateFiles()
4267 15 Dec 16 nicklas 179   {
6624 04 Mar 22 nicklas 180     return false;
4267 15 Dec 16 nicklas 181   }
4267 15 Dec 16 nicklas 182   
4267 15 Dec 16 nicklas 183   /**
4234 11 Nov 16 nicklas 184     Set the priority of the job. Valid range is from -1023 
4234 11 Nov 16 nicklas 185     (low priority) to +1024 (high priority). Priorities above 0 
4234 11 Nov 16 nicklas 186     require special privileges. The default priority is unspecified 
4234 11 Nov 16 nicklas 187     (which typically means 0 on the Open Grid Cluster but this may 
4234 11 Nov 16 nicklas 188     depend on the user that is logged in).
5987 18 Aug 20 nicklas 189     Also updates the 'nice' value.
4234 11 Nov 16 nicklas 190   */
4234 11 Nov 16 nicklas 191   public void setPriority(Integer priority)
4234 11 Nov 16 nicklas 192   {
4234 11 Nov 16 nicklas 193     checkLocked("setPriority()");
4234 11 Nov 16 nicklas 194     this.priority = priority;
5987 18 Aug 20 nicklas 195     this.nice = openGridPriorityToSlurmNice(priority);
4234 11 Nov 16 nicklas 196   }
4234 11 Nov 16 nicklas 197   public Integer getPriority()
4234 11 Nov 16 nicklas 198   {
4234 11 Nov 16 nicklas 199     return priority;
4234 11 Nov 16 nicklas 200   }
4234 11 Nov 16 nicklas 201   
4234 11 Nov 16 nicklas 202   /**
5987 18 Aug 20 nicklas 203     Get the 'nice' value (used in Slurm) that corresponds to 
5987 18 Aug 20 nicklas 204     the priority value used in Open Grid.
5987 18 Aug 20 nicklas 205     @see #setPriority(Integer)
5987 18 Aug 20 nicklas 206     @see #openGridPriorityToSlurmNice(Integer)
5987 18 Aug 20 nicklas 207     @since 1.4
5987 18 Aug 20 nicklas 208   */
5987 18 Aug 20 nicklas 209   public Integer getSlurmNice()
5987 18 Aug 20 nicklas 210   {
5987 18 Aug 20 nicklas 211     return nice;
5987 18 Aug 20 nicklas 212   }
5987 18 Aug 20 nicklas 213   
5987 18 Aug 20 nicklas 214   /**
5987 18 Aug 20 nicklas 215     Set the priority of the job using a Slurm 'nice' value. 
5987 18 Aug 20 nicklas 216     Valid range is from -2147483645 (high priority) to +2147483645
5987 18 Aug 20 nicklas 217     (low priority). Values below typically requires special 
5987 18 Aug 20 nicklas 218     privileges. The defaul 'nice' value is unspecified (which typically
5987 18 Aug 20 nicklas 219     means 0 but this may depend on other options).
5987 18 Aug 20 nicklas 220     Also updates the 'priority' value.
5987 18 Aug 20 nicklas 221     @since 1.4
5987 18 Aug 20 nicklas 222   */
5987 18 Aug 20 nicklas 223   public void setSlurmNice(Integer nice)
5987 18 Aug 20 nicklas 224   {
5987 18 Aug 20 nicklas 225     checkLocked("setSlurmNice()");
5987 18 Aug 20 nicklas 226     this.nice = nice;
5987 18 Aug 20 nicklas 227     this.priority = slurmNiceToOpenGridPriority(nice);
5987 18 Aug 20 nicklas 228   }
5987 18 Aug 20 nicklas 229   
5987 18 Aug 20 nicklas 230   /**
4234 11 Nov 16 nicklas 231     Shortcut for calling {@link #openGridPriorityToBasePriority(Integer)}.
4234 11 Nov 16 nicklas 232   */
4234 11 Nov 16 nicklas 233   public int getBASEPriority()
4234 11 Nov 16 nicklas 234   {
4234 11 Nov 16 nicklas 235     return openGridPriorityToBasePriority(getPriority());
4234 11 Nov 16 nicklas 236   }
4234 11 Nov 16 nicklas 237
4234 11 Nov 16 nicklas 238   /**
4234 11 Nov 16 nicklas 239     Set an option for the 'qsub' command. See 
4234 11 Nov 16 nicklas 240     http://gridscheduler.sourceforge.net/htmlman/htmlman1/qsub.html for
4234 11 Nov 16 nicklas 241     a lot more information. The options will go into the
7352 18 Sep 23 nicklas 242     script file using the #$ prefix: #$ -option value
7352 18 Sep 23 nicklas 243     Use an empty string as value for flag-like options: #$ -option
7352 18 Sep 23 nicklas 244     It is not recommended to override options that are automatically
7352 18 Sep 23 nicklas 245     set when submitting the job. This is currently: -terse, -S, -N, -wd,
7352 18 Sep 23 nicklas 246     -o, -e and -p.
4234 11 Nov 16 nicklas 247     
4234 11 Nov 16 nicklas 248     @param option The name of the option (with or without the hyphen)
4234 11 Nov 16 nicklas 249     @param value The value of the option (null will remove it)
4234 11 Nov 16 nicklas 250   */
4234 11 Nov 16 nicklas 251   public void setQsubOption(String option, String value)
4234 11 Nov 16 nicklas 252   {
4234 11 Nov 16 nicklas 253     checkLocked("setQsubOption(" + option + ")");
4234 11 Nov 16 nicklas 254     if (option == null) return;
4234 11 Nov 16 nicklas 255     if (option.startsWith("-")) option = option.substring(1);
4234 11 Nov 16 nicklas 256     if (value == null)
4234 11 Nov 16 nicklas 257     {
4234 11 Nov 16 nicklas 258       qsubOptions.remove(option);
4234 11 Nov 16 nicklas 259     }
4234 11 Nov 16 nicklas 260     else
4234 11 Nov 16 nicklas 261     {
4234 11 Nov 16 nicklas 262       qsubOptions.put(option, value);
4234 11 Nov 16 nicklas 263     }
4234 11 Nov 16 nicklas 264   }
4234 11 Nov 16 nicklas 265
4234 11 Nov 16 nicklas 266   /**
4234 11 Nov 16 nicklas 267     Get the value of a qsub option.
4234 11 Nov 16 nicklas 268     @param option The name of the option (with or without the hyphen)
4234 11 Nov 16 nicklas 269   */
4234 11 Nov 16 nicklas 270   public String getQsubOption(String option)
4234 11 Nov 16 nicklas 271   {
4234 11 Nov 16 nicklas 272     if (option == null) return null;
4234 11 Nov 16 nicklas 273     if (option.startsWith("-")) option = option.substring(1);
4234 11 Nov 16 nicklas 274     return qsubOptions.get(option);
4234 11 Nov 16 nicklas 275   }
4234 11 Nov 16 nicklas 276   
4234 11 Nov 16 nicklas 277   /**
4254 25 Nov 16 nicklas 278     Get all options that has been specified for qsub so far.
4254 25 Nov 16 nicklas 279     @return A read-only map with the qsub options
4254 25 Nov 16 nicklas 280   */
4254 25 Nov 16 nicklas 281   public Map<String, String> getQsubOptions()
4254 25 Nov 16 nicklas 282   {
4254 25 Nov 16 nicklas 283     return Collections.unmodifiableMap(qsubOptions);
4254 25 Nov 16 nicklas 284   }
4254 25 Nov 16 nicklas 285   
4254 25 Nov 16 nicklas 286   /**
4234 11 Nov 16 nicklas 287     Add all qsub options in this configuration to the script.
4254 25 Nov 16 nicklas 288     Each option will create a line:
4301 13 Jan 17 nicklas 289     #$ -&lt;option&gt; &lt;value&gt;
4234 11 Nov 16 nicklas 290   */
4297 13 Jan 17 nicklas 291   public void appendQsubOptionsToScript(StringBuilder script, Set<String> ignore)
4234 11 Nov 16 nicklas 292   {
4234 11 Nov 16 nicklas 293     for (Map.Entry<String, String> entry : qsubOptions.entrySet())
4234 11 Nov 16 nicklas 294     {
4297 13 Jan 17 nicklas 295       if (ignore == null || !ignore.contains(entry.getKey()))
4297 13 Jan 17 nicklas 296       {
7352 18 Sep 23 nicklas 297         if (entry.getValue().length()==0)
7352 18 Sep 23 nicklas 298         {
7352 18 Sep 23 nicklas 299           script.append("#$ -" + entry.getKey() + "\n");
7352 18 Sep 23 nicklas 300         }
7352 18 Sep 23 nicklas 301         else
7352 18 Sep 23 nicklas 302         {
7352 18 Sep 23 nicklas 303           script.append("#$ -" + entry.getKey() + " " + entry.getValue() + "\n");
7352 18 Sep 23 nicklas 304         }
4297 13 Jan 17 nicklas 305       }
4234 11 Nov 16 nicklas 306     }
4234 11 Nov 16 nicklas 307   }
4234 11 Nov 16 nicklas 308   
6629 07 Mar 22 nicklas 309   /**
6629 07 Mar 22 nicklas 310     Add all qsub options in this configuration to the script.
6629 07 Mar 22 nicklas 311     Each option will create a line:
6629 07 Mar 22 nicklas 312     #$ -&lt;option&gt; &lt;value&gt;
6629 07 Mar 22 nicklas 313     @since 1.5
6629 07 Mar 22 nicklas 314   */
6629 07 Mar 22 nicklas 315   public void appendQsubOptionsToScript(ScriptBuilder script, Set<String> ignore)
6629 07 Mar 22 nicklas 316   {
6629 07 Mar 22 nicklas 317     for (Map.Entry<String, String> entry : qsubOptions.entrySet())
6629 07 Mar 22 nicklas 318     {
6629 07 Mar 22 nicklas 319       if (ignore == null || !ignore.contains(entry.getKey()))
6629 07 Mar 22 nicklas 320       {
7352 18 Sep 23 nicklas 321         if (entry.getValue().length()==0)
7352 18 Sep 23 nicklas 322         {
7352 18 Sep 23 nicklas 323           script.cmd("#$ -" + entry.getKey());
7352 18 Sep 23 nicklas 324         }
7352 18 Sep 23 nicklas 325         else
7352 18 Sep 23 nicklas 326         {
7352 18 Sep 23 nicklas 327           script.cmd("#$ -" + entry.getKey() + " " + entry.getValue());
7352 18 Sep 23 nicklas 328         }
6629 07 Mar 22 nicklas 329       }
6629 07 Mar 22 nicklas 330     }
6629 07 Mar 22 nicklas 331   }
6629 07 Mar 22 nicklas 332
5989 19 Aug 20 nicklas 333   
4234 11 Nov 16 nicklas 334   /**
5989 19 Aug 20 nicklas 335     Set an option for the 'sbatch' command (Slurm). See 
5989 19 Aug 20 nicklas 336     https://slurm.schedmd.com/sbatch.html for
5989 19 Aug 20 nicklas 337     a lot more information. The options will go into the
7352 18 Sep 23 nicklas 338     script file using the #SBATCH prefix: #SBATCH --option=value
7352 18 Sep 23 nicklas 339     Use an empty string as value for flag-like options: #SBATCH --option
5989 19 Aug 20 nicklas 340     
7352 18 Sep 23 nicklas 341     It is not recommended to override options that are automatically set
7352 18 Sep 23 nicklas 342     when submitting the job. This is currently: (--parsable, --job-name, 
7352 18 Sep 23 nicklas 343     -J, --chdir, -D, --output, -o, --error, -e).
7352 18 Sep 23 nicklas 344     
5989 19 Aug 20 nicklas 345     @param option The name of the option (with or without the hyphen)
5989 19 Aug 20 nicklas 346     @param value The value of the option (null will remove it)
5989 19 Aug 20 nicklas 347     @since 1.4
5989 19 Aug 20 nicklas 348   */
5989 19 Aug 20 nicklas 349   public void setSbatchOption(String option, String value)
5989 19 Aug 20 nicklas 350   {
5989 19 Aug 20 nicklas 351     checkLocked("setSbatchOption(" + option + ")");
5989 19 Aug 20 nicklas 352     if (option == null) return;
5989 19 Aug 20 nicklas 353     if (option.startsWith("-")) option = option.substring(1); // Remove '-'
5989 19 Aug 20 nicklas 354     if (option.startsWith("-")) option = option.substring(1); // Remove '--'
5989 19 Aug 20 nicklas 355     if (value == null)
5989 19 Aug 20 nicklas 356     {
5989 19 Aug 20 nicklas 357       sbatchOptions.remove(option);
5989 19 Aug 20 nicklas 358     }
5989 19 Aug 20 nicklas 359     else
5989 19 Aug 20 nicklas 360     {
5989 19 Aug 20 nicklas 361       sbatchOptions.put(option, value);
5989 19 Aug 20 nicklas 362     }
5989 19 Aug 20 nicklas 363   }
5989 19 Aug 20 nicklas 364
5989 19 Aug 20 nicklas 365   /**
5989 19 Aug 20 nicklas 366     Get the value of a sbatch option.
5989 19 Aug 20 nicklas 367     @param option The name of the option (with or without the hyphen)
5989 19 Aug 20 nicklas 368     @since 1.4
5989 19 Aug 20 nicklas 369   */
5989 19 Aug 20 nicklas 370   public String getSbatchOption(String option)
5989 19 Aug 20 nicklas 371   {
5989 19 Aug 20 nicklas 372     if (option == null) return null;
5989 19 Aug 20 nicklas 373     if (option.startsWith("-")) option = option.substring(1); // Remove '-'
5989 19 Aug 20 nicklas 374     if (option.startsWith("-")) option = option.substring(1); // Remove '--'
5989 19 Aug 20 nicklas 375     return sbatchOptions.get(option);
5989 19 Aug 20 nicklas 376   }
5989 19 Aug 20 nicklas 377   
5989 19 Aug 20 nicklas 378   /**
5989 19 Aug 20 nicklas 379     Get all options that has been specified for sbatch so far.
5989 19 Aug 20 nicklas 380     @return A read-only map with the sbatch options
5989 19 Aug 20 nicklas 381     @since 1.4
5989 19 Aug 20 nicklas 382   */
5989 19 Aug 20 nicklas 383   public Map<String, String> getSbatchOptions()
5989 19 Aug 20 nicklas 384   {
5989 19 Aug 20 nicklas 385     return Collections.unmodifiableMap(sbatchOptions);
5989 19 Aug 20 nicklas 386   }
5989 19 Aug 20 nicklas 387
5989 19 Aug 20 nicklas 388   /**
5989 19 Aug 20 nicklas 389     Add all sbatch options in this configuration to the script.
5989 19 Aug 20 nicklas 390     Each option will create a line:
5989 19 Aug 20 nicklas 391     #SBATCH --&lt;option&gt;=&lt;value&gt;
5989 19 Aug 20 nicklas 392     or:
5989 19 Aug 20 nicklas 393     #SBATCH -&lt;option&gt;=&lt;value&gt;
5989 19 Aug 20 nicklas 394   */
5989 19 Aug 20 nicklas 395   public void appendSbatchOptionsToScript(StringBuilder script, Set<String> ignore)
5989 19 Aug 20 nicklas 396   {
5989 19 Aug 20 nicklas 397     for (Map.Entry<String, String> entry : sbatchOptions.entrySet())
5989 19 Aug 20 nicklas 398     {
5989 19 Aug 20 nicklas 399       String option = entry.getKey();
5989 19 Aug 20 nicklas 400       if (ignore == null || !ignore.contains(option))
5989 19 Aug 20 nicklas 401       {
5989 19 Aug 20 nicklas 402         String prefix = option.length() == 1 ? "-" : "--";
7352 18 Sep 23 nicklas 403         if (entry.getValue().length()==0)
7352 18 Sep 23 nicklas 404         {
7352 18 Sep 23 nicklas 405           // Flag option
7352 18 Sep 23 nicklas 406           script.append("#SBATCH " + prefix + option + "\n");
7352 18 Sep 23 nicklas 407         }
7352 18 Sep 23 nicklas 408         else
7352 18 Sep 23 nicklas 409         {
7352 18 Sep 23 nicklas 410           script.append("#SBATCH " + prefix + option + "=" + entry.getValue() + "\n");
7352 18 Sep 23 nicklas 411         }
5989 19 Aug 20 nicklas 412       }
5989 19 Aug 20 nicklas 413     }
5989 19 Aug 20 nicklas 414   }
5989 19 Aug 20 nicklas 415   
5989 19 Aug 20 nicklas 416   /**
6629 07 Mar 22 nicklas 417     Add all sbatch options in this configuration to the script.
6629 07 Mar 22 nicklas 418     Each option will create a line:
6629 07 Mar 22 nicklas 419     #SBATCH --&lt;option&gt;=&lt;value&gt;
6629 07 Mar 22 nicklas 420     or:
6629 07 Mar 22 nicklas 421     #SBATCH -&lt;option&gt;=&lt;value&gt;
6629 07 Mar 22 nicklas 422   */
6629 07 Mar 22 nicklas 423   public void appendSbatchOptionsToScript(ScriptBuilder script, Set<String> ignore)
6629 07 Mar 22 nicklas 424   {
6629 07 Mar 22 nicklas 425     for (Map.Entry<String, String> entry : sbatchOptions.entrySet())
6629 07 Mar 22 nicklas 426     {
6629 07 Mar 22 nicklas 427       String option = entry.getKey();
6629 07 Mar 22 nicklas 428       if (ignore == null || !ignore.contains(option))
6629 07 Mar 22 nicklas 429       {
6629 07 Mar 22 nicklas 430         String prefix = option.length() == 1 ? "-" : "--";
7352 18 Sep 23 nicklas 431         if (entry.getValue().length()==0)
7352 18 Sep 23 nicklas 432         {
7352 18 Sep 23 nicklas 433           // Flag option
7352 18 Sep 23 nicklas 434           script.cmd("#SBATCH " + prefix + option);
7352 18 Sep 23 nicklas 435         }
7352 18 Sep 23 nicklas 436         else
7352 18 Sep 23 nicklas 437         {
7352 18 Sep 23 nicklas 438           script.cmd("#SBATCH " + prefix + option + "=" + entry.getValue());
7352 18 Sep 23 nicklas 439         }
6629 07 Mar 22 nicklas 440       }
6629 07 Mar 22 nicklas 441     }
6629 07 Mar 22 nicklas 442   }
6629 07 Mar 22 nicklas 443
6629 07 Mar 22 nicklas 444   
6629 07 Mar 22 nicklas 445   /**
5989 19 Aug 20 nicklas 446     The priority must be between -1023 and +1024 and 'nice' must be
5989 19 Aug 20 nicklas 447     between -2147483645 and +2147483645. Other generic qsub or sbatch
4254 25 Nov 16 nicklas 448     options are not checked.
4254 25 Nov 16 nicklas 449   */
4254 25 Nov 16 nicklas 450   @Override
4257 30 Nov 16 nicklas 451   protected void checkValid(boolean forLock) 
4254 25 Nov 16 nicklas 452   {
4257 30 Nov 16 nicklas 453     super.checkValid(forLock);
4254 25 Nov 16 nicklas 454     if (priority != null && (priority < -1023 || priority > 1024))
4254 25 Nov 16 nicklas 455     {
4254 25 Nov 16 nicklas 456       throw new IllegalArgumentException("Priority must be in the range -1023..+1024: " + priority);
4254 25 Nov 16 nicklas 457     }
5991 20 Aug 20 nicklas 458     if (nice != null && (nice < -2147483645 || nice > 2147483645))
5987 18 Aug 20 nicklas 459     {
5987 18 Aug 20 nicklas 460       throw new IllegalArgumentException("Nice must be in the range +/- 2147483645: " + nice);
5987 18 Aug 20 nicklas 461     }
6672 11 Apr 22 nicklas 462     if (forLock)
6672 11 Apr 22 nicklas 463     {
6672 11 Apr 22 nicklas 464       if (autoConvertOptions) autoConvertOptions();
6672 11 Apr 22 nicklas 465       if (batchConfig != null) batchConfig.lock();
6672 11 Apr 22 nicklas 466     }
4254 25 Nov 16 nicklas 467   }
4254 25 Nov 16 nicklas 468
5991 20 Aug 20 nicklas 469   private void autoConvertOptions()
5991 20 Aug 20 nicklas 470   {
5991 20 Aug 20 nicklas 471     if (sbatchOptions.size() == 0 && qsubOptions.size() > 0)
5991 20 Aug 20 nicklas 472     {
5991 20 Aug 20 nicklas 473       convertQsubToSbatchOptions();
5991 20 Aug 20 nicklas 474     }
5991 20 Aug 20 nicklas 475     else if (sbatchOptions.size() > 0 && qsubOptions.size() == 0)
5991 20 Aug 20 nicklas 476     {
5991 20 Aug 20 nicklas 477       convertSbatchToQsubOptions();
5991 20 Aug 20 nicklas 478     }
5991 20 Aug 20 nicklas 479   }
4254 25 Nov 16 nicklas 480   
4254 25 Nov 16 nicklas 481   /**
6978 16 Jan 23 nicklas 482     Convert options to the variant used for the given cluster type.
6978 16 Jan 23 nicklas 483     Note that the list of supported options that can be converted are
6978 16 Jan 23 nicklas 484     very limited.
6978 16 Jan 23 nicklas 485     @since 1.8
6978 16 Jan 23 nicklas 486     @see #convertQsubToSbatchOptions()
6978 16 Jan 23 nicklas 487     @see #convertSbatchToQsubOptions()
6978 16 Jan 23 nicklas 488   */
6978 16 Jan 23 nicklas 489   public void convertOptionsTo(ClusterType type)
6978 16 Jan 23 nicklas 490   {
6978 16 Jan 23 nicklas 491     if (type == ClusterType.OPENGRID)
6978 16 Jan 23 nicklas 492     {
6978 16 Jan 23 nicklas 493       convertSbatchToQsubOptions();
6978 16 Jan 23 nicklas 494     }
6978 16 Jan 23 nicklas 495     else if (type == ClusterType.SLURM)
6978 16 Jan 23 nicklas 496     {
6978 16 Jan 23 nicklas 497       convertQsubToSbatchOptions();
6978 16 Jan 23 nicklas 498     }
6978 16 Jan 23 nicklas 499   }
6978 16 Jan 23 nicklas 500   
6978 16 Jan 23 nicklas 501   /**
5991 20 Aug 20 nicklas 502     Convert options set for 'qsub' (Open Grid) to options for 'sbatch' (Slurm).
5991 20 Aug 20 nicklas 503     Supported options are:
5991 20 Aug 20 nicklas 504     
5991 20 Aug 20 nicklas 505     * -pe smp X-Y: --nodes=1, --ntasks=1, --cpus-per-task=max(X, Y)
6978 16 Jan 23 nicklas 506     * -q NAME: --partition=NAME (since 1.8)
5991 20 Aug 20 nicklas 507
5991 20 Aug 20 nicklas 508     @since 1.4 
5991 20 Aug 20 nicklas 509   */
5991 20 Aug 20 nicklas 510   public void convertQsubToSbatchOptions()
5991 20 Aug 20 nicklas 511   {
5991 20 Aug 20 nicklas 512     checkLocked("convertQsubToSbatchOptions()");
5991 20 Aug 20 nicklas 513     for (Map.Entry<String, String> entry : qsubOptions.entrySet())
5991 20 Aug 20 nicklas 514     {
5991 20 Aug 20 nicklas 515       String option = entry.getKey();
5991 20 Aug 20 nicklas 516       if ("pe".equals(option))
5991 20 Aug 20 nicklas 517       {
5991 20 Aug 20 nicklas 518         sbatchOptions.put("nodes", "1");
5991 20 Aug 20 nicklas 519         sbatchOptions.put("ntasks", "1");
5991 20 Aug 20 nicklas 520         sbatchOptions.put("cpus-per-task", getMaxNumberInString(entry.getValue()));
5991 20 Aug 20 nicklas 521       }
6978 16 Jan 23 nicklas 522       else if ("q".equals(option))
6978 16 Jan 23 nicklas 523       {
6978 16 Jan 23 nicklas 524         sbatchOptions.put("partition", entry.getValue());
6978 16 Jan 23 nicklas 525       }
5991 20 Aug 20 nicklas 526     }
5991 20 Aug 20 nicklas 527   }
5991 20 Aug 20 nicklas 528   
5991 20 Aug 20 nicklas 529   /**
5991 20 Aug 20 nicklas 530     Convert options set for 'sbatch' (Slurm) to options for 'qsub' (Open Grid).
5991 20 Aug 20 nicklas 531     Supported options are:
5991 20 Aug 20 nicklas 532     
5991 20 Aug 20 nicklas 533     * --cpus-per-task=N: -pe smp N
6978 16 Jan 23 nicklas 534     * -p, --partition=NAME: -q NAME (since 1.8)
5991 20 Aug 20 nicklas 535   
5991 20 Aug 20 nicklas 536     @since 1.4 
5991 20 Aug 20 nicklas 537   */
5991 20 Aug 20 nicklas 538   public void convertSbatchToQsubOptions()
5991 20 Aug 20 nicklas 539   {
5991 20 Aug 20 nicklas 540     checkLocked("convertSbatchToQsubOptions()");
5991 20 Aug 20 nicklas 541     for (Map.Entry<String, String> entry : sbatchOptions.entrySet())
5991 20 Aug 20 nicklas 542     {
5991 20 Aug 20 nicklas 543       String option = entry.getKey();
5991 20 Aug 20 nicklas 544       if ("cpus-per-task".equals(option) || "c".equals(option))
5991 20 Aug 20 nicklas 545       {
5991 20 Aug 20 nicklas 546         // '--cpu-per-task=N' is converted to '-pe smp N'
5991 20 Aug 20 nicklas 547         qsubOptions.put("pe", "smp " + entry.getValue());
5991 20 Aug 20 nicklas 548       }
6978 16 Jan 23 nicklas 549       else if ("partition".equals(option) || "p".equals(option))
6978 16 Jan 23 nicklas 550       {
6978 16 Jan 23 nicklas 551         // '--partition=N' is converted to '-q N'
6978 16 Jan 23 nicklas 552         qsubOptions.put("q", entry.getValue());
6978 16 Jan 23 nicklas 553       }
5991 20 Aug 20 nicklas 554     }
5991 20 Aug 20 nicklas 555   }
5991 20 Aug 20 nicklas 556   
5991 20 Aug 20 nicklas 557   /**
5991 20 Aug 20 nicklas 558     Get the highest number that can be found in the given string.
5991 20 Aug 20 nicklas 559     Use to extract the number of requested slots in, for example,
5991 20 Aug 20 nicklas 560     -pe smp 8-16.
5991 20 Aug 20 nicklas 561   */
5991 20 Aug 20 nicklas 562   private String getMaxNumberInString(String s)
5991 20 Aug 20 nicklas 563   {
5991 20 Aug 20 nicklas 564     Pattern p = Pattern.compile("\\d+");
5991 20 Aug 20 nicklas 565     Matcher m = p.matcher(s);
5991 20 Aug 20 nicklas 566     int max = 1;
5991 20 Aug 20 nicklas 567     while (m.find())
5991 20 Aug 20 nicklas 568     {
5991 20 Aug 20 nicklas 569       int val = Integer.valueOf(m.group());
5991 20 Aug 20 nicklas 570       if (val > max) max = val;
5991 20 Aug 20 nicklas 571     }
5991 20 Aug 20 nicklas 572     return Integer.toString(max);
5991 20 Aug 20 nicklas 573   }
5991 20 Aug 20 nicklas 574   
5991 20 Aug 20 nicklas 575   /**
4234 11 Nov 16 nicklas 576     Used for converting between Open Grid priority values and
4234 11 Nov 16 nicklas 577     BASE priority values.
4234 11 Nov 16 nicklas 578   */
4234 11 Nov 16 nicklas 579   static enum Priority
4234 11 Nov 16 nicklas 580   {
4234 11 Nov 16 nicklas 581     ONE(1, 1024),
4234 11 Nov 16 nicklas 582     TWO(2, 768),
4234 11 Nov 16 nicklas 583     THREE(3, 512),
4234 11 Nov 16 nicklas 584     FOUR(4, 256),
4234 11 Nov 16 nicklas 585     FIVE(5, 0),
4234 11 Nov 16 nicklas 586     SIX(6, -220),
4234 11 Nov 16 nicklas 587     SEVEN(7, -440),
4234 11 Nov 16 nicklas 588     EIGHT(8, -660),
4234 11 Nov 16 nicklas 589     NINE(9, -880),
4234 11 Nov 16 nicklas 590     TEN(10, -1023);
4234 11 Nov 16 nicklas 591     
4234 11 Nov 16 nicklas 592     final int inBase;
4234 11 Nov 16 nicklas 593     final int inOpenGrid;
4234 11 Nov 16 nicklas 594     
4234 11 Nov 16 nicklas 595     Priority(int inBase, int inOpenGrid)
4234 11 Nov 16 nicklas 596     {
4234 11 Nov 16 nicklas 597       this.inBase = inBase;
4234 11 Nov 16 nicklas 598       this.inOpenGrid = inOpenGrid;
4234 11 Nov 16 nicklas 599     }
4234 11 Nov 16 nicklas 600   }
4234 11 Nov 16 nicklas 601 }