ood_core 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c668d456d8773bfa4a3af714f73a57d2f9396ee8
4
- data.tar.gz: 14cdd12014850a9464505d7e34e5e4397826c6d9
2
+ SHA256:
3
+ metadata.gz: bf5cfe29bd0770daa8404169e04fcc8fcdc9a89b88f83bbfbc8675040b119ccf
4
+ data.tar.gz: c6082f2c7b751c0b7f247dcc790ae35bc1a372830b6f7c529d1b5574eed714ee
5
5
  SHA512:
6
- metadata.gz: ebb2ea211dc272f03884faf0c2245e2f2b945851023ef5f5f26c1862514a0626a57c35c3be636d9287d1581d56dd18a3e6ff014e41837d63b7d05559605acd79
7
- data.tar.gz: beb0f071c17c7632e9aae32ee243e3fd47480ff385d1d82cfe42e48652a6bbaaee3db7c224c7c2b4c8f91b11b11b8e5d9170b231f846e60ff9e6e1d2f03a0260
6
+ metadata.gz: 30c82f37cf6c974c04a3d8c9bc9da21e47014b7aecfa03424575196e42aa0ebebb89aba6717e73073a1ed3d963d8391fa76da263a4e3a7e4dc4250a2cb32f830
7
+ data.tar.gz: 9c7be268d29f4dd6c9cec57ce783e7eb0777c76e3df7025062e77868985a61e410cd24ec7d39c2a4e91a039d85a85c41fad75e28a408c8e00940597e5a1fb1ff
data/.travis.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.1
5
- before_install: gem install bundler -v 1.7.8
4
+ - 2.5.5
5
+ before_install: gem install bundler -v '~> 1.17'
6
6
  notifications:
7
7
  email:
8
8
  on_success: never
data/CHANGELOG.md CHANGED
@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
+
10
+ ## [0.11.1] - 2012-03-18
11
+ ### Changed
12
+ - Only the version changed. Had to republish to rubygems.org
13
+
14
+ ## [0.11.0] - 2012-03-18
15
+ ### Added
16
+ - Added directive prefixes to each adapter (e.g. `#QSUB`) ([#161](https://github.com/OSC/ood_core/issues/161))
17
+ - LHA supports `submit_host` field in native ([#164](https://github.com/OSC/ood_core/issues/164))
18
+ - Cluster files can be yaml or yml extensions ([#171](https://github.com/OSC/ood_core/issues/171))
19
+ - Users can add a flag `OOD_JOB_NAME_ILLEGAL_CHARS` to sanitize job names ([#183](https://github.com/OSC/ood_core/issues/183)
20
+
21
+ ### Changed
22
+ - Simplified job array parsing ([#144](https://github.com/OSC/ood_core/issues/144))
23
+
24
+ ### Fixed
25
+ - Issue where environment variables were not properly exported to the job ([#158](https://github.com/OSC/ood_core/issues/158))
26
+ - Parsing bad cluster files ([#150](https://github.com/OSC/ood_core/issues/150) and [#178](https://github.com/OSC/ood_core/issues/178))
27
+ - netcat is no longer a hard dependency. Now lsof, python and bash can be used ([153](https://github.com/OSC/ood_core/issues/153))
28
+ - GE crash when nil config file was given ([#175](https://github.com/OSC/ood_core/issues/175))
29
+ - GE sometimes reported incorrect core count ([#168](https://github.com/OSC/ood_core/issues/168))
30
+
31
+
9
32
  ## [0.10.0] - 2019-11-05
10
33
  ### Added
11
34
  - Added an adapter for submitting work on Linux hosted systems without using a scheduler
@@ -196,7 +219,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
196
219
  ### Added
197
220
  - Initial release!
198
221
 
199
- [Unreleased]: https://github.com/OSC/ood_core/compare/v0.10.0...HEAD
222
+ [Unreleased]: https://github.com/OSC/ood_core/compare/v0.11.1...HEAD
223
+ [0.11.1]: https://github.com/OSC/ood_core/compare/v0.11.0...v0.11.1
224
+ [0.11.0]: https://github.com/OSC/ood_core/compare/v0.10.0...v0.11.0
200
225
  [0.10.0]: https://github.com/OSC/ood_core/compare/v0.9.3...v0.10.0
201
226
  [0.9.3]: https://github.com/OSC/ood_core/compare/v0.9.2...v0.9.3
202
227
  [0.9.2]: https://github.com/OSC/ood_core/compare/v0.9.1...v0.9.2
data/lib/ood_core.rb CHANGED
@@ -2,6 +2,7 @@ require "ood_core/version"
2
2
  require "ood_core/errors"
3
3
  require "ood_core/cluster"
4
4
  require "ood_core/clusters"
5
+ require "ood_core/invalid_cluster"
5
6
 
6
7
  # The main namespace for ood_core
7
8
  module OodCore
@@ -117,11 +117,47 @@ module OodCore
117
117
  }
118
118
  export -f random_number
119
119
 
120
+ port_used_python() {
121
+ python -c "import socket; socket.socket().connect(('$1',$2))" >/dev/null 2>&1
122
+ }
123
+
124
+ port_used_python3() {
125
+ python3 -c "import socket; socket.socket().connect(('$1',$2))" >/dev/null 2>&1
126
+ }
127
+
128
+ port_used_nc(){
129
+ nc -w 2 "$1" "$2" < /dev/null > /dev/null 2>&1
130
+ }
131
+
132
+ port_used_lsof(){
133
+ lsof -i :"$2" >/dev/null 2>&1
134
+ }
135
+
136
+ port_used_bash(){
137
+ local bash_supported=$(strings /bin/bash 2>/dev/null | grep tcp)
138
+ if [ "$bash_supported" == "/dev/tcp/*/*" ]; then
139
+ (: < /dev/tcp/$1/$2) >/dev/null 2>&1
140
+ else
141
+ return 127
142
+ fi
143
+ }
144
+
120
145
  # Check if port $1 is in use
121
146
  port_used () {
122
147
  local port="${1#*:}"
123
148
  local host=$((expr "${1}" : '\\(.*\\):' || echo "localhost") | awk 'END{print $NF}')
124
- nc -w 2 "${host}" "${port}" < /dev/null &> /dev/null
149
+ local port_strategies=(port_used_nc port_used_lsof port_used_bash port_used_python port_used_python3)
150
+
151
+ for strategy in ${port_strategies[@]};
152
+ do
153
+ $strategy $host $port
154
+ status=$?
155
+ if [[ "$status" == "0" ]] || [[ "$status" == "1" ]]; then
156
+ return $status
157
+ fi
158
+ done
159
+
160
+ return 127
125
161
  }
126
162
  export -f port_used
127
163
 
@@ -143,8 +179,14 @@ module OodCore
143
179
  local port="${1}"
144
180
  local time="${2:-30}"
145
181
  for ((i=1; i<=time*2; i++)); do
146
- if port_used "${port}"; then
182
+ port_used "${port}"
183
+ port_status=$?
184
+ if [ "$port_status" == "0" ]; then
147
185
  return 0
186
+ elif [ "$port_status" == "127" ]; then
187
+ echo "commands to find port were either not found or inaccessible."
188
+ echo "command options are lsof, nc, bash's /dev/tcp, or python (or python3) with socket lib."
189
+ return 127
148
190
  fi
149
191
  sleep 0.5
150
192
  done
@@ -28,6 +28,10 @@ module OodCore
28
28
  # @return [Hash] the acls configuration
29
29
  attr_reader :acls_config
30
30
 
31
+ # The errors encountered with configuring this cluster
32
+ # @return Array<String> the errors
33
+ attr_reader :errors
34
+
31
35
  # @param cluster [#to_h] the cluster object
32
36
  # @option cluster [#to_sym] :id The cluster id
33
37
  # @option cluster [#to_h] :metadata ({}) The cluster's metadata
@@ -39,6 +43,8 @@ module OodCore
39
43
  # against
40
44
  # @option cluster [#to_h] :batch_connect ({}) Configuration for batch
41
45
  # connect templates
46
+ # @option cluster [#to_a] :errors ([]) List of configuration errors
47
+ #
42
48
  def initialize(cluster)
43
49
  c = cluster.to_h.symbolize_keys
44
50
 
@@ -52,6 +58,9 @@ module OodCore
52
58
  @custom_config = c.fetch(:custom, {}) .to_h.symbolize_keys
53
59
  @acls_config = c.fetch(:acls, []) .map(&:to_h)
54
60
  @batch_connect_config = c.fetch(:batch_connect, {}).to_h.symbolize_keys
61
+
62
+ # side affects from object creation and validation
63
+ @errors = c.fetch(:errors, []) .to_a
55
64
  end
56
65
 
57
66
  # Metadata that provides extra information about this cluster
@@ -159,6 +168,12 @@ module OodCore
159
168
  }
160
169
  end
161
170
 
171
+ # This cluster is always valid
172
+ # @return true
173
+ def valid?
174
+ return true
175
+ end
176
+
162
177
  private
163
178
  # Build acl adapter objects from array
164
179
  def build_acls(ary)
@@ -21,16 +21,30 @@ module OodCore
21
21
  if config.file?
22
22
  if config.readable?
23
23
  CONFIG_VERSION.any? do |version|
24
- YAML.safe_load(config.read).fetch(version, {}).each do |k, v|
25
- clusters << Cluster.new(send("parse_#{version}", id: k, cluster: v))
24
+ begin
25
+ YAML.safe_load(config.read)&.fetch(version, {}).each do |k, v|
26
+ clusters << Cluster.new(send("parse_#{version}", id: k, cluster: v))
27
+ end
28
+ rescue Psych::SyntaxError => e
29
+ clusters << InvalidCluster.new(
30
+ id: config.basename(config.extname).to_s,
31
+ errors: [ e.message.to_s ]
32
+ )
26
33
  end
27
34
  end
28
35
  end
29
36
  elsif config.directory?
30
- Pathname.glob(config.join("*.yml")).select(&:file?).select(&:readable?).each do |p|
37
+ Pathname.glob([config.join("*.yml"), config.join("*.yaml")]).select(&:file?).select(&:readable?).each do |p|
31
38
  CONFIG_VERSION.any? do |version|
32
- if cluster = YAML.safe_load(p.read).fetch(version, nil)
33
- clusters << Cluster.new(send("parse_#{version}", id: p.basename(".yml").to_s, cluster: cluster))
39
+ begin
40
+ if cluster = YAML.safe_load(p.read)&.fetch(version, nil)
41
+ clusters << Cluster.new(send("parse_#{version}", id: p.basename(p.extname()).to_s, cluster: cluster))
42
+ end
43
+ rescue Psych::SyntaxError => e
44
+ clusters << InvalidCluster.new(
45
+ id: p.basename(p.extname).to_s,
46
+ errors: [ e.message.to_s ]
47
+ )
34
48
  end
35
49
  end
36
50
  end
@@ -0,0 +1,37 @@
1
+ module OodCore
2
+ # A special case of an OodCore::Cluster where something went awry in the
3
+ # creation and it's invalid for some reason. Users should only be able
4
+ # to rely on id and metadata.error_msg. All *allow? related functions
5
+ # false, meaning nothing is allowed.
6
+ class InvalidCluster < Cluster
7
+ # Jobs are not allowed
8
+ # @return false
9
+ def login_allow?
10
+ false
11
+ end
12
+
13
+ # Jobs are not allowed
14
+ # @return false
15
+ def job_allow?
16
+ false
17
+ end
18
+
19
+ # Custom features are not allowed
20
+ # @return false
21
+ def custom_allow?(_)
22
+ false
23
+ end
24
+
25
+ # This cluster is not allowed to be used
26
+ # @return false
27
+ def allow?
28
+ false
29
+ end
30
+
31
+ # This cluster is never valid
32
+ # @return false
33
+ def valid?
34
+ return false
35
+ end
36
+ end
37
+ end
@@ -36,7 +36,7 @@ module OodCore
36
36
  # Retrieve info for all jobs from the resource manager
37
37
  # @abstract Subclass is expected to implement {#info_all}
38
38
  # @raise [NotImplementedError] if subclass did not define {#info_all}
39
- # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
39
+ # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
40
40
  # This array specifies only attrs you want, in addition to id and status.
41
41
  # If an array, the Info object that is returned to you is not guarenteed
42
42
  # to have a value for any attr besides the ones specified and id and status.
@@ -51,7 +51,7 @@ module OodCore
51
51
  # Retrieve info for all jobs for a given owner or owners from the
52
52
  # resource manager
53
53
  # @param owner [#to_s, Array<#to_s>] the owner(s) of the jobs
54
- # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
54
+ # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
55
55
  # This array specifies only attrs you want, in addition to id and status.
56
56
  # If an array, the Info object that is returned to you is not guarenteed
57
57
  # to have a value for any attr besides the ones specified and id and status.
@@ -69,7 +69,7 @@ module OodCore
69
69
  end
70
70
 
71
71
  # Iterate over each job Info object
72
- # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
72
+ # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
73
73
  # This array specifies only attrs you want, in addition to id and status.
74
74
  # If an array, the Info object that is returned to you is not guarenteed
75
75
  # to have a value for any attr besides the ones specified and id and status.
@@ -88,7 +88,7 @@ module OodCore
88
88
 
89
89
  # Iterate over each job Info object
90
90
  # @param owner [#to_s, Array<#to_s>] the owner(s) of the jobs
91
- # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
91
+ # @param attrs [Array<symbol>] defaults to nil (and all attrs are provided)
92
92
  # This array specifies only attrs you want, in addition to id and status.
93
93
  # If an array, the Info object that is returned to you is not guarenteed
94
94
  # to have a value for any attr besides the ones specified and id and status.
@@ -157,6 +157,37 @@ module OodCore
157
157
  def delete(id)
158
158
  raise NotImplementedError, "subclass did not define #delete"
159
159
  end
160
+
161
+ # Return the scheduler-specific directive prefix
162
+ #
163
+ # Examples of directive prefixes include #QSUB, #BSUB and allow placing what would
164
+ # otherwise be command line options inside the job launch script.
165
+ #
166
+ # The method should return nil if the adapter does not support prefixes
167
+ #
168
+ # @abstract Subclass is expected to implement {#directive_prefix}
169
+ # @raise [NotImplementedError] if subclass did not defined {#directive_prefix}
170
+ # @return [String]
171
+ def directive_prefix
172
+ raise NotImplementedError, "subclass did not define #directive_prefix"
173
+ end
174
+
175
+ # Replace illegal chars in job name with a dash
176
+ #
177
+ # @return [String] job name with dashes replacing illegal chars
178
+ def sanitize_job_name(job_name)
179
+ # escape ^ and omit -
180
+ chars = job_name_illegal_chars.to_s.gsub("^", "\\^").gsub("-", "")
181
+ job_name.tr(chars, "-")
182
+ end
183
+
184
+ # Illegal chars that should not be used in a job name
185
+ # A dash is assumed to be legal in job names in all batch schedulers
186
+ #
187
+ # @return [String] string of chars
188
+ def job_name_illegal_chars
189
+ ENV["OOD_JOB_NAME_ILLEGAL_CHARS"].to_s
190
+ end
160
191
  end
161
192
  end
162
193
  end
@@ -13,7 +13,7 @@
13
13
  # The contents of this file are subject to the Sun Industry Standards
14
14
  # Source License Version 1.2 (the "License"); You may not use this file
15
15
  # except in compliance with the License. You may obtain a copy of the
16
- # License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
16
+ # License at http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html
17
17
  #
18
18
  # Software provided under this License is provided on an "AS IS" basis,
19
19
  # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
@@ -201,6 +201,10 @@ module OodCore
201
201
  raise JobAdapterError, e.message
202
202
  end
203
203
 
204
+ def directive_prefix
205
+ nil
206
+ end
207
+
204
208
  private
205
209
 
206
210
  def host_permitted?(destination_host)
@@ -11,7 +11,7 @@ require 'time'
11
11
  class OodCore::Job::Adapters::LinuxHost::Launcher
12
12
  attr_reader :contain, :debug, :site_timeout, :session_name_label, :singularity_bin,
13
13
  :site_singularity_bindpath, :default_singularity_image, :ssh_hosts,
14
- :strict_host_checking, :submit_host, :tmux_bin, :username
14
+ :strict_host_checking, :tmux_bin, :username
15
15
  # The root exception class that all LinuxHost adapter-specific exceptions inherit
16
16
  # from
17
17
  class Error < StandardError; end
@@ -57,7 +57,7 @@ class OodCore::Job::Adapters::LinuxHost::Launcher
57
57
  # @param hostname [#to_s] The hostname to submit the work to
58
58
  # @param script [OodCore::Job::Script] The script object defining the work
59
59
  def start_remote_session(script)
60
- cmd = ssh_cmd(submit_host)
60
+ cmd = ssh_cmd(submit_host(script))
61
61
 
62
62
  session_name = unique_session_name
63
63
  output = call(*cmd, stdin: wrapped_script(script, session_name))
@@ -98,6 +98,14 @@ class OodCore::Job::Adapters::LinuxHost::Launcher
98
98
  }
99
99
  end
100
100
 
101
+ def submit_host(script = nil)
102
+ if script && script.native && script.native['submit_host_override']
103
+ script.native['submit_host_override']
104
+ else
105
+ @submit_host
106
+ end
107
+ end
108
+
101
109
  private
102
110
 
103
111
  # Call a forked Slurm command for a given cluster
@@ -167,6 +167,10 @@ module OodCore
167
167
  raise JobAdapterError, e.message
168
168
  end
169
169
 
170
+ def directive_prefix
171
+ '#BSUB'
172
+ end
173
+
170
174
  private
171
175
  # Determine state from LSF state code
172
176
  def get_state(st)
@@ -92,6 +92,15 @@ class OodCore::Job::Adapters::Lsf::Helper
92
92
  args += ["-W", (script.wall_time / 60).to_i] unless script.wall_time.nil?
93
93
  args += ["-L", script.shell_path.to_s] unless script.shell_path.nil?
94
94
 
95
+ # environment
96
+ env = script.job_environment || {}
97
+ # To preserve pre-existing behavior we only act when true or false, when nil we do nothing
98
+ if script.copy_environment?
99
+ args += ["-env", (["all"] + env.keys).join(",")]
100
+ elsif script.copy_environment? == false
101
+ args += ["-env", (["none"] + env.keys).join(",")]
102
+ end
103
+
95
104
  # input and output files
96
105
  args += ["-i", script.input_path] unless script.input_path.nil?
97
106
  args += ["-o", script.output_path] unless script.output_path.nil?
@@ -104,9 +113,6 @@ class OodCore::Job::Adapters::Lsf::Helper
104
113
 
105
114
  args += script.native unless script.native.nil?
106
115
 
107
- # environment
108
- env = script.job_environment || {}
109
-
110
116
  {args: args, env: env}
111
117
  end
112
118
  end
@@ -261,6 +261,7 @@ module OodCore
261
261
  # Set environment variables
262
262
  envvars = script.job_environment.to_h
263
263
  args += ["-v", envvars.map{|k,v| "#{k}=#{v}"}.join(",")] unless envvars.empty?
264
+ args += ["-V"] if script.copy_environment?
264
265
 
265
266
  # If error_path is not specified we join stdout & stderr (as this
266
267
  # mimics what the other resource managers do)
@@ -397,6 +398,10 @@ module OodCore
397
398
  raise JobAdapterError, e.message unless /Unknown Job Id/ =~ e.message || /Job has finished/ =~ e.message
398
399
  end
399
400
 
401
+ def directive_prefix
402
+ '#PBS'
403
+ end
404
+
400
405
  private
401
406
  # Convert duration to seconds
402
407
  def duration_in_seconds(time)
@@ -157,6 +157,10 @@ module OodCore
157
157
  rescue Batch::Error => e
158
158
  raise JobAdapterError, e.message
159
159
  end
160
+
161
+ def directive_prefix
162
+ '#$'
163
+ end
160
164
  end
161
165
  end
162
166
  end
@@ -33,8 +33,7 @@ class OodCore::Job::Adapters::Sge::Batch
33
33
  # @see Factory.build_sge
34
34
  def initialize(config)
35
35
  @cluster = config.fetch(:cluster, nil)
36
- @conf = Pathname.new(config.fetch(:conf, nil))
37
- @bin = Pathname.new(config.fetch(:bin, nil))
36
+ @bin = Pathname.new(config.fetch(:bin, nil).to_s)
38
37
  @sge_root = Pathname.new(config[:sge_root] || ENV['SGE_ROOT'] || "/var/lib/gridengine")
39
38
  @bin_overrides = config.fetch(:bin_overrides, {})
40
39
 
@@ -20,6 +20,7 @@ class OodCore::Job::Adapters::Sge::Helper
20
20
  args += ['-h'] if script.submit_as_hold
21
21
  args += ['-r', 'yes'] if script.rerunnable
22
22
  script.job_environment.each_pair {|k, v| args += ['-v', "#{k.to_s}=#{v.to_s}"]} unless script.job_environment.nil?
23
+ args += ["-V"] if script.copy_environment?
23
24
 
24
25
  if script.workdir
25
26
  args += ['-wd', script.workdir]
@@ -27,13 +27,15 @@ class QstatXmlJRListener
27
27
  @parsed_job = {
28
28
  :tasks => [],
29
29
  :status => :queued,
30
- :procs => 1, # un-knowable from SGE qstat output
30
+ :procs => 1,
31
31
  :native => {} # TODO: improve native attribute reporting
32
32
  }
33
33
  @current_text = nil
34
34
  @current_request = nil
35
35
 
36
36
  @processing_job_array_spec = false
37
+ @adding_slots = false
38
+
37
39
  @job_array_spec = {
38
40
  start: nil,
39
41
  stop: nil,
@@ -46,6 +48,8 @@ class QstatXmlJRListener
46
48
  case name
47
49
  when 'task_id_range'
48
50
  toggle_processing_array_spec
51
+ when 'JB_pe_range'
52
+ toggle_adding_slots
49
53
  end
50
54
  end
51
55
 
@@ -78,13 +82,16 @@ class QstatXmlJRListener
78
82
  when 'djob_info'
79
83
  finalize_parsed_job
80
84
  when 'RN_min'
81
- set_job_array_piece(:start)
85
+ set_job_array_piece(:start) if @processing_job_array_spec
86
+ set_slots if @adding_slots
82
87
  when 'RN_max'
83
- set_job_array_piece(:stop)
88
+ set_job_array_piece(:stop) if @processing_job_array_spec
84
89
  when 'RN_step'
85
- set_job_array_piece(:step)
90
+ set_job_array_piece(:step) if @processing_job_array_spec
86
91
  when 'task_id_range'
87
92
  toggle_processing_array_spec
93
+ when 'JB_pe_range'
94
+ toggle_adding_slots
88
95
  end
89
96
  end
90
97
 
@@ -186,5 +193,13 @@ class QstatXmlJRListener
186
193
  def toggle_processing_array_spec
187
194
  @processing_job_array_spec = ! @processing_job_array_spec
188
195
  end
196
+
197
+ def toggle_adding_slots
198
+ @adding_slots = ! @adding_slots
199
+ end
200
+
201
+ def set_slots
202
+ @parsed_job[:procs] = @current_text.to_i
203
+ end
189
204
  end
190
205
 
@@ -192,7 +192,7 @@ module OodCore
192
192
  # @return [String] the id of the job that was created
193
193
  def submit_string(str, args: [], env: {})
194
194
  args = args.map(&:to_s) + ["--parsable"]
195
- env = {"SBATCH_EXPORT" => "NONE"}.merge env.each_with_object({}) { |(k, v), h| h[k.to_s] = v.to_s }
195
+ env = env.to_h.each_with_object({}) { |(k, v), h| h[k.to_s] = v.to_s }
196
196
  call("sbatch", *args, env: env, stdin: str.to_s).strip.split(";").first
197
197
  end
198
198
 
@@ -394,7 +394,10 @@ module OodCore
394
394
 
395
395
  # Set environment variables
396
396
  env = script.job_environment || {}
397
- args += ["--export", script.job_environment.keys.join(",")] unless script.job_environment.nil? || script.job_environment.empty?
397
+ unless (script.job_environment.nil? || script.job_environment.empty?)
398
+ prefix = script.copy_environment? ? "ALL," : "NONE," # NONE if false or nil
399
+ args += ["--export", prefix + script.job_environment.keys.join(",")]
400
+ end
398
401
 
399
402
  # Set native options
400
403
  args += script.native if script.native
@@ -530,6 +533,10 @@ module OodCore
530
533
  raise JobAdapterError, e.message unless /Invalid job id specified/ =~ e.message
531
534
  end
532
535
 
536
+ def directive_prefix
537
+ '#SBATCH'
538
+ end
539
+
533
540
  private
534
541
  # Convert duration to seconds
535
542
  def duration_in_seconds(time)
@@ -155,6 +155,7 @@ module OodCore
155
155
  # Set environment variables
156
156
  env = script.job_environment.to_h
157
157
  args += ["-v", env.keys.join(",")] unless env.empty?
158
+ args += ["-V"] if script.copy_environment?
158
159
 
159
160
  # If error_path is not specified we join stdout & stderr (as this
160
161
  # mimics what the other resource managers do)
@@ -288,6 +289,10 @@ module OodCore
288
289
  raise JobAdapterError, e.message
289
290
  end
290
291
 
292
+ def directive_prefix
293
+ '#QSUB'
294
+ end
295
+
291
296
  private
292
297
  # Convert duration to seconds
293
298
  def duration_in_seconds(time)
@@ -10,68 +10,33 @@
10
10
  module OodCore
11
11
  module Job
12
12
  class ArrayIds
13
- class Error < StandardError ; end
13
+ attr_reader :spec_string
14
14
 
15
- attr_reader :ids
16
15
  def initialize(spec_string)
17
- @ids = []
18
- begin
19
- parse_spec_string(spec_string) if spec_string
20
- rescue Error
21
- end
22
- end
23
-
24
- protected
25
- def parse_spec_string(spec_string)
26
- @ids = get_components(spec_string).map{
27
- |component| process_component(component)
28
- }.reduce(:+).sort
16
+ @spec_string = spec_string
29
17
  end
30
18
 
31
- def get_components(spec_string)
32
- base = discard_percent_modifier(spec_string)
33
- raise Error unless base
34
- base.split(',')
19
+ def ids
20
+ @ids ||= parse_spec_string(spec_string)
35
21
  end
36
22
 
37
- # A few adapters use percent to define an arrays maximum number of
38
- # simultaneous tasks. The percent is expected to come at the end.
39
- def discard_percent_modifier(spec_string)
40
- spec_string.split('%').first
41
- end
23
+ protected
42
24
 
43
- def process_component(component)
44
- if is_range?(component)
45
- get_range(component)
46
- elsif numbers_valid?([component])
47
- [ component.to_i ]
48
- else
49
- raise Error
25
+ def parse_spec_string(spec_string)
26
+ return [] unless spec_string
27
+
28
+ rx = /^(\d+)-?(\d+)?:?(\d+)?%?\d*$/
29
+ spec_string.split(',').reduce([]) do |ids, spec|
30
+ if rx =~ spec
31
+ start = ($1 || 1).to_i
32
+ finish = ($2 || start).to_i
33
+ step = ($3 || 1).to_i
34
+ ids.concat (start..finish).step(step).to_a
35
+ end
36
+
37
+ ids
50
38
  end
51
39
  end
52
-
53
- def get_range(component)
54
- raw_range, raw_step = component.split(':')
55
- start, stop = raw_range.split('-')
56
- raise Error unless numbers_valid?(
57
- # Only include Step if it is not nil
58
- [start, stop].tap { |a| a << raw_step if raw_step }
59
- )
60
- range = Range.new(start.to_i, stop.to_i)
61
- step = raw_step.to_i
62
- step = 1 if step == 0
63
-
64
- range.step(step).to_a
65
- end
66
-
67
- def is_range?(component)
68
- component.include?('-')
69
- end
70
-
71
- # Protect against Ruby's String#to_i returning 0 for arbitrary strings
72
- def numbers_valid?(numbers)
73
- numbers.all? { |str| /^[0-9]+$/ =~ str }
74
- end
75
40
  end
76
41
  end
77
42
  end
@@ -104,6 +104,11 @@ module OodCore
104
104
  # @return [Object, nil] native specifications
105
105
  attr_reader :native
106
106
 
107
+ # Flag whether the job should contain a copy of its calling environment
108
+ # @return [Boolean] copy environment
109
+ attr_reader :copy_environment
110
+ alias_method :copy_environment?, :copy_environment
111
+
107
112
  # @param content [#to_s] the script content
108
113
  # @param args [Array<#to_s>, nil] arguments supplied to script
109
114
  # @param submit_as_hold [Boolean, nil] whether job is held after submit
@@ -132,7 +137,8 @@ module OodCore
132
137
  job_name: nil, shell_path: nil, input_path: nil,
133
138
  output_path: nil, error_path: nil, reservation_id: nil,
134
139
  queue_name: nil, priority: nil, start_time: nil,
135
- wall_time: nil, accounting_id: nil, job_array_request: nil, native: nil, **_)
140
+ wall_time: nil, accounting_id: nil, job_array_request: nil,
141
+ native: nil, copy_environment: nil, **_)
136
142
  @content = content.to_s
137
143
 
138
144
  @submit_as_hold = submit_as_hold
@@ -157,6 +163,7 @@ module OodCore
157
163
  @accounting_id = accounting_id && accounting_id.to_s
158
164
  @job_array_request = job_array_request && job_array_request.to_s
159
165
  @native = native
166
+ @copy_environment = (copy_environment.nil?) ? nil : !! copy_environment
160
167
  end
161
168
 
162
169
  # Convert object to hash
@@ -184,7 +191,8 @@ module OodCore
184
191
  wall_time: wall_time,
185
192
  accounting_id: accounting_id,
186
193
  job_array_request: job_array_request,
187
- native: native
194
+ native: native,
195
+ copy_environment: copy_environment
188
196
  }
189
197
  end
190
198
 
@@ -1,4 +1,4 @@
1
1
  module OodCore
2
2
  # The current version of {OodCore}
3
- VERSION = "0.10.0"
3
+ VERSION = "0.11.1"
4
4
  end
data/ood_core.gemspec CHANGED
@@ -25,8 +25,9 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency "ood_support", "~> 0.0.2"
26
26
  spec.add_runtime_dependency "ffi", "~> 1.9", ">= 1.9.6"
27
27
  spec.add_development_dependency "bundler", "~> 1.7"
28
- spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rake", "~> 13.0.1"
29
29
  spec.add_development_dependency "rspec", "~> 3.0"
30
30
  spec.add_development_dependency "pry", "~> 0.10"
31
31
  spec.add_development_dependency "timecop", "~> 0.8"
32
+ spec.add_development_dependency "climate_control", "~> 0.2.0"
32
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ood_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Franz
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2019-11-05 00:00:00.000000000 Z
13
+ date: 2020-03-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: ood_support
@@ -66,14 +66,14 @@ dependencies:
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '10.0'
69
+ version: 13.0.1
70
70
  type: :development
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '10.0'
76
+ version: 13.0.1
77
77
  - !ruby/object:Gem::Dependency
78
78
  name: rspec
79
79
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,20 @@ dependencies:
116
116
  - - "~>"
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0.8'
119
+ - !ruby/object:Gem::Dependency
120
+ name: climate_control
121
+ requirement: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 0.2.0
126
+ type: :development
127
+ prerelease: false
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: 0.2.0
119
133
  description: Open OnDemand core library that provides support for an HPC Center to
120
134
  globally define HPC services that web applications can then take advantage of.
121
135
  email:
@@ -147,6 +161,7 @@ files:
147
161
  - lib/ood_core/cluster.rb
148
162
  - lib/ood_core/clusters.rb
149
163
  - lib/ood_core/errors.rb
164
+ - lib/ood_core/invalid_cluster.rb
150
165
  - lib/ood_core/job/adapter.rb
151
166
  - lib/ood_core/job/adapters/drmaa.rb
152
167
  - lib/ood_core/job/adapters/helper.rb
@@ -201,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
216
  version: '0'
202
217
  requirements: []
203
218
  rubyforge_project:
204
- rubygems_version: 2.6.11
219
+ rubygems_version: 2.7.6.2
205
220
  signing_key:
206
221
  specification_version: 4
207
222
  summary: Open OnDemand core library