ood_core 0.29.0 → 0.30.0
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 +4 -4
- data/lib/ood_core/cluster.rb +11 -1
- data/lib/ood_core/job/account_info.rb +0 -9
- data/lib/ood_core/job/adapter.rb +2 -1
- data/lib/ood_core/job/adapters/coder/batch.rb +46 -25
- data/lib/ood_core/job/adapters/coder/credentials.rb +16 -0
- data/lib/ood_core/job/adapters/coder/openstack_credentials.rb +118 -0
- data/lib/ood_core/job/adapters/coder.rb +10 -2
- data/lib/ood_core/job/adapters/linux_host/templates/script_wrapper.erb.sh +1 -1
- data/lib/ood_core/job/adapters/sge/batch.rb +13 -10
- data/lib/ood_core/job/adapters/slurm.rb +39 -11
- data/lib/ood_core/job/adapters/systemd/templates/script_wrapper.erb.sh +1 -1
- data/lib/ood_core/job/queue_info.rb +8 -2
- data/lib/ood_core/version.rb +1 -1
- data/ood_core.gemspec +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 14dedae6a9b9508f87bb9b950520fb4614f345a90d8841c23836c2650a5b5d2f
|
|
4
|
+
data.tar.gz: 357f6a0eec8b1029f6ad1d32983a55e071b6fda90b453953a8400c5e7f442c68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c7782f857836971b0f26207669c9ef21746b0490755a0e86ba0eea6d4b75069c61e7a5c349aea813b4c38f812e099725d97ba0b7d8e916a3cd00e859e12e4eb
|
|
7
|
+
data.tar.gz: 798c9a6709dbba8d704e51942e9677ca632df7c773689dafeaebbe2212f560c2a18643f15dfa8550bdc1bff810eda34b929ce9fd4d84fef10001a68d63533056
|
data/lib/ood_core/cluster.rb
CHANGED
|
@@ -77,6 +77,16 @@ module OodCore
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def title
|
|
81
|
+
if !metadata.title.nil?
|
|
82
|
+
metadata.title
|
|
83
|
+
elsif id.to_s.respond_to?(:titleize)
|
|
84
|
+
id.to_s.titleize
|
|
85
|
+
else
|
|
86
|
+
id.to_s
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
80
90
|
# Metadata that provides extra information about this cluster
|
|
81
91
|
# @return [OpenStruct] the metadata
|
|
82
92
|
def metadata
|
|
@@ -100,7 +110,7 @@ module OodCore
|
|
|
100
110
|
# Build a job adapter from the job configuration
|
|
101
111
|
# @return [Job::Adapter] the job adapter
|
|
102
112
|
def job_adapter
|
|
103
|
-
Job::Factory.build(job_config)
|
|
113
|
+
Job::Factory.build(job_config.merge({ id: id }))
|
|
104
114
|
end
|
|
105
115
|
|
|
106
116
|
# Whether the job feature is allowed based on the ACLs
|
|
@@ -12,19 +12,10 @@ module OodCore
|
|
|
12
12
|
# The QoS values this account can use.
|
|
13
13
|
attr_reader :qos
|
|
14
14
|
|
|
15
|
-
# The cluster this account is associated with.
|
|
16
|
-
attr_reader :cluster
|
|
17
|
-
|
|
18
|
-
# The queue this account can use. nil means there is no queue info
|
|
19
|
-
# for this account.
|
|
20
|
-
attr_reader :queue
|
|
21
|
-
|
|
22
15
|
def initialize(**opts)
|
|
23
16
|
orig_name = opts.fetch(:name, 'unknown')
|
|
24
17
|
@name = upcase_accounts? ? orig_name.upcase : orig_name
|
|
25
18
|
@qos = opts.fetch(:qos, [])
|
|
26
|
-
@cluster = opts.fetch(:cluster, nil)
|
|
27
|
-
@queue = opts.fetch(:queue, nil)
|
|
28
19
|
end
|
|
29
20
|
|
|
30
21
|
def to_h
|
data/lib/ood_core/job/adapter.rb
CHANGED
|
@@ -210,7 +210,8 @@ module OodCore
|
|
|
210
210
|
ENV["OOD_JOB_NAME_ILLEGAL_CHARS"].to_s
|
|
211
211
|
end
|
|
212
212
|
|
|
213
|
-
# Retrieve the accounts available to use
|
|
213
|
+
# Retrieve the accounts available to use for the current user.
|
|
214
|
+
# The same account might appear muiltiple times if it has access to multiple clusters.
|
|
214
215
|
#
|
|
215
216
|
# Subclasses that do not implement this will return empty arrays.
|
|
216
217
|
# @return [Array<AccountInfo>] the accounts available to the user.
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
require "ood_core/refinements/hash_extensions"
|
|
2
2
|
require "json"
|
|
3
|
+
|
|
3
4
|
|
|
4
5
|
# Utility class for the Coder adapter to interact with the Coders API.
|
|
5
6
|
class OodCore::Job::Adapters::Coder::Batch
|
|
6
7
|
require_relative "coder_job_info"
|
|
7
8
|
class Error < StandardError; end
|
|
8
|
-
def initialize(config)
|
|
9
|
+
def initialize(config, credentials)
|
|
9
10
|
@host = config[:host]
|
|
10
11
|
@token = config[:token]
|
|
12
|
+
@service_user = config[:service_user]
|
|
13
|
+
@credential_deletion_max_attempts = config[:credential_deletion_max_attempts] || 5
|
|
14
|
+
@credential_deletion_timeout_interval = config[:credential_deletion_timeout_interval] || 10
|
|
15
|
+
@credentials = credentials
|
|
11
16
|
end
|
|
12
17
|
|
|
13
|
-
def
|
|
14
|
-
credentials_file = File.read("/home/#{username}/application_credentials.json")
|
|
15
|
-
credentials = JSON.parse(credentials_file)
|
|
16
|
-
credentials.find { |cred| cred["project_id"] == project_id }
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def get_rich_parameters(coder_parameters, project_id, os_app_credentials)
|
|
18
|
+
def get_rich_parameters(coder_parameters, project_id, app_credentials)
|
|
20
19
|
rich_parameter_values = [
|
|
21
|
-
{ name: "application_credential_name", value:
|
|
22
|
-
{ name: "application_credential_id", value:
|
|
23
|
-
{ name: "application_credential_secret", value:
|
|
20
|
+
{ name: "application_credential_name", value: app_credentials[:name] },
|
|
21
|
+
{ name: "application_credential_id", value: app_credentials[:id] },
|
|
22
|
+
{ name: "application_credential_secret", value: app_credentials[:secret] },
|
|
24
23
|
{name: "project_id", value: project_id }
|
|
25
24
|
]
|
|
26
25
|
if coder_parameters
|
|
@@ -43,34 +42,59 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
43
42
|
org_id = script.native[:org_id]
|
|
44
43
|
project_id = script.native[:project_id]
|
|
45
44
|
coder_parameters = script.native[:coder_parameters]
|
|
46
|
-
endpoint = "
|
|
47
|
-
|
|
45
|
+
endpoint = "#{@host}/api/v2/organizations/#{org_id}/members/#{@service_user}/workspaces"
|
|
46
|
+
app_credentials = @credentials.generate_credentials(project_id, username)
|
|
48
47
|
headers = get_headers(@token)
|
|
48
|
+
workspace_name = "#{username}-#{script.native[:workspace_name]}-#{rand(2_821_109_907_456).to_s(36)}"
|
|
49
49
|
body = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
rich_parameter_values: get_rich_parameters(coder_parameters, project_id, os_app_credentials),
|
|
50
|
+
template_version_id: script.native[:template_version_id],
|
|
51
|
+
name: workspace_name,
|
|
52
|
+
rich_parameter_values: get_rich_parameters(coder_parameters, project_id, app_credentials),
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
resp = api_call('post', endpoint, headers, body)
|
|
56
|
+
@credentials.save_credentials(resp["id"], username, app_credentials)
|
|
57
57
|
resp["id"]
|
|
58
|
+
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def delete(id)
|
|
61
|
-
endpoint = "
|
|
62
|
+
endpoint = "#{@host}/api/v2/workspaces/#{id}/builds"
|
|
62
63
|
headers = get_headers(@token)
|
|
63
64
|
body = {
|
|
64
65
|
'orphan' => false,
|
|
65
66
|
'transition' => 'delete'
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
+
api_call('post', endpoint, headers, body)
|
|
69
|
+
|
|
70
|
+
credentials = @credentials.load_credentials(id, username)
|
|
71
|
+
|
|
72
|
+
wait_for_workspace_deletion(id) do |attempt|
|
|
73
|
+
puts "#{Time.now.inspect} Deleting workspace (attempt #{attempt + 1}/#{5})"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@credentials.destroy_credentials(credentials, workspace_json(id).dig("latest_build", "status"), id, username)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def wait_for_workspace_deletion(id)
|
|
80
|
+
max_attempts = @credential_deletion_max_attempts
|
|
81
|
+
timeout_interval = @credential_deletion_timeout_interval
|
|
82
|
+
|
|
83
|
+
max_attempts.times do |attempt|
|
|
84
|
+
break unless workspace_json(id) && workspace_json(id).dig("latest_build", "status") == "deleting"
|
|
85
|
+
yield(attempt + 1)
|
|
86
|
+
sleep(timeout_interval)
|
|
87
|
+
end
|
|
68
88
|
end
|
|
69
89
|
|
|
70
|
-
def
|
|
71
|
-
endpoint = "
|
|
90
|
+
def workspace_json(id)
|
|
91
|
+
endpoint = "#{@host}/api/v2/workspaces/#{id}?include_deleted=true"
|
|
72
92
|
headers = get_headers(@token)
|
|
73
|
-
|
|
93
|
+
api_call('get', endpoint, headers)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def info(id)
|
|
97
|
+
workspace_info_from_json(workspace_json(id))
|
|
74
98
|
end
|
|
75
99
|
|
|
76
100
|
def coder_state_to_ood_status(coder_state)
|
|
@@ -137,7 +161,6 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
137
161
|
|
|
138
162
|
def api_call(method, endpoint, headers, body = nil)
|
|
139
163
|
uri = URI(endpoint)
|
|
140
|
-
|
|
141
164
|
case method.downcase
|
|
142
165
|
when 'get'
|
|
143
166
|
request = Net::HTTP::Get.new(uri, headers)
|
|
@@ -150,11 +173,9 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
150
173
|
end
|
|
151
174
|
|
|
152
175
|
request.body = body.to_json if body
|
|
153
|
-
|
|
154
176
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
155
177
|
http.request(request)
|
|
156
178
|
end
|
|
157
|
-
|
|
158
179
|
case response
|
|
159
180
|
when Net::HTTPSuccess
|
|
160
181
|
JSON.parse(response.body)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CredentialsInterface
|
|
2
|
+
def load_credentials
|
|
3
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def generate_credentials
|
|
7
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def destroy_credentials
|
|
11
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
12
|
+
end
|
|
13
|
+
def save_credentials
|
|
14
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require "fog/openstack"
|
|
2
|
+
require "json"
|
|
3
|
+
require "ood_core/job/adapters/coder/credentials"
|
|
4
|
+
|
|
5
|
+
class OpenStackCredentials < CredentialsInterface
|
|
6
|
+
def initialize(auth_url)
|
|
7
|
+
@auth_url = auth_url
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def load_credentials(id, username)
|
|
11
|
+
file_path = "/home/#{username}/#{id}_credentials.json"
|
|
12
|
+
JSON.parse(File.read(file_path))
|
|
13
|
+
rescue Errno::ENOENT => e
|
|
14
|
+
puts "Error loading credentials: #{e}"
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate_credentials(project_id, username)
|
|
19
|
+
token_json = JSON.parse(File.read("/home/#{username}/token.json"))
|
|
20
|
+
access_token = token_json["id"]
|
|
21
|
+
user_id = token_json["user_id"]
|
|
22
|
+
connection = Fog::OpenStack::Identity.new({
|
|
23
|
+
openstack_auth_url: @auth_url,
|
|
24
|
+
openstack_management_url: @auth_url,
|
|
25
|
+
openstack_auth_token: access_token,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
auth = {
|
|
29
|
+
"auth": {
|
|
30
|
+
"identity": {
|
|
31
|
+
"methods": [
|
|
32
|
+
"token"
|
|
33
|
+
],
|
|
34
|
+
"token": {
|
|
35
|
+
"id": access_token
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"scope": {
|
|
39
|
+
"project": {
|
|
40
|
+
"id": project_id
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
scoped_token = connection.tokens.authenticate(auth)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
connection = Fog::OpenStack::Identity.new({
|
|
50
|
+
openstack_auth_url: @auth_url,
|
|
51
|
+
openstack_management_url: @auth_url,
|
|
52
|
+
openstack_auth_token: scoped_token,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
app_credentials = {
|
|
57
|
+
"name": "OOD_generated_#{rand(16**8).to_s(16)}" ,
|
|
58
|
+
"description": "Application credential generated via OOD for Coder.",
|
|
59
|
+
"roles": [
|
|
60
|
+
],
|
|
61
|
+
"unrestricted": true,
|
|
62
|
+
"user_id": user_id
|
|
63
|
+
}
|
|
64
|
+
res = connection.application_credentials.create app_credentials
|
|
65
|
+
|
|
66
|
+
credential_data = {
|
|
67
|
+
id: res.id,
|
|
68
|
+
name: res.name,
|
|
69
|
+
user_id: user_id,
|
|
70
|
+
secret: res.secret
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
credential_data
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def save_credentials(id, username, app_credentials)
|
|
78
|
+
file_path = "/home/#{username}/#{id}_credentials.json"
|
|
79
|
+
File.write(file_path, JSON.generate(app_credentials))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def destroy_credentials(os_app_credentials, deletion_status, id, username)
|
|
84
|
+
return if os_app_credentials.nil?
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
connection = create_fog_connection(os_app_credentials)
|
|
88
|
+
credentials_to_destroy = find_os_application_credentials(connection, os_app_credentials)
|
|
89
|
+
|
|
90
|
+
if deletion_status != "deleted"
|
|
91
|
+
File.delete("/home/#{username}/#{id}_credentials.json")
|
|
92
|
+
puts "Workspace deletion timed out, credentials with id #{os_app_credentials['id']} of user #{os_app_credentials['user_id']} were not destroyed"
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
credentials_to_destroy.destroy
|
|
98
|
+
rescue Excon::Error::Forbidden => e
|
|
99
|
+
puts "Error destroying application credentials with id #{os_app_credentials['id']} #{e}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
def create_fog_connection(os_app_credentials)
|
|
106
|
+
Fog::OpenStack::Identity.new({
|
|
107
|
+
openstack_auth_url: @auth_url,
|
|
108
|
+
openstack_management_url: @auth_url,
|
|
109
|
+
openstack_application_credential_id: os_app_credentials['id'],
|
|
110
|
+
openstack_application_credential_secret: os_app_credentials['secret']
|
|
111
|
+
})
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
def find_os_application_credentials(connection, os_app_credentials)
|
|
116
|
+
connection.application_credentials.find_by_id(os_app_credentials['id'], os_app_credentials['user_id'])
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -9,8 +9,16 @@ module OodCore
|
|
|
9
9
|
class Factory
|
|
10
10
|
using Refinements::HashExtensions
|
|
11
11
|
|
|
12
|
+
require "ood_core/job/adapters/coder/openstack_credentials"
|
|
13
|
+
|
|
12
14
|
def self.build_coder(config)
|
|
13
|
-
|
|
15
|
+
config = config.to_h.symbolize_keys
|
|
16
|
+
if config[:auth]["cloud"] == "openstack"
|
|
17
|
+
credentials = OpenStackCredentials.new(config[:auth]["url"])
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "Unsupported credentials for cloud type: #{config[:auth]['cloud']}"
|
|
20
|
+
end
|
|
21
|
+
batch = Adapters::Coder::Batch.new(config.to_h.symbolize_keys, credentials)
|
|
14
22
|
Adapters::Coder.new(batch)
|
|
15
23
|
end
|
|
16
24
|
end
|
|
@@ -18,7 +26,7 @@ module OodCore
|
|
|
18
26
|
module Adapters
|
|
19
27
|
attr_reader :host, :token
|
|
20
28
|
|
|
21
|
-
# The adapter class for
|
|
29
|
+
# The adapter class for Coder.
|
|
22
30
|
class Coder < Adapter
|
|
23
31
|
|
|
24
32
|
using Refinements::ArrayExtensions
|
|
@@ -62,16 +62,19 @@ class OodCore::Job::Adapters::Sge::Batch
|
|
|
62
62
|
# @param owner [#to_s] the owner or owner list
|
|
63
63
|
# @return [Array<OodCore::Job::Info>]
|
|
64
64
|
def get_all(owner: nil)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|job_hash|
|
|
72
|
-
**post_process_qstat_job_hash(job_hash)
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
begin
|
|
66
|
+
listener = QstatXmlRListener.new
|
|
67
|
+
argv = ['qstat', '-r', '-xml']
|
|
68
|
+
argv.concat ['-u', owner] unless owner.nil?
|
|
69
|
+
REXML::Parsers::StreamParser.new(call(*argv), listener).parse
|
|
70
|
+
|
|
71
|
+
listener.parsed_jobs.map do |job_hash|
|
|
72
|
+
OodCore::Job::Info.new(**post_process_qstat_job_hash(job_hash))
|
|
73
|
+
end
|
|
74
|
+
rescue REXML::ParseException => e
|
|
75
|
+
warn("Error parsing response: #{e}")
|
|
76
|
+
[]
|
|
77
|
+
end
|
|
75
78
|
end
|
|
76
79
|
|
|
77
80
|
# Get OodCore::Job::Info for a job_id that may still be in the queue
|
|
@@ -25,7 +25,13 @@ module OodCore
|
|
|
25
25
|
bin_overrides = c.fetch(:bin_overrides, {})
|
|
26
26
|
submit_host = c.fetch(:submit_host, "")
|
|
27
27
|
strict_host_checking = c.fetch(:strict_host_checking, true)
|
|
28
|
-
|
|
28
|
+
id = c.fetch(:id, 'unknown')
|
|
29
|
+
|
|
30
|
+
slurm = Adapters::Slurm::Batch.new(
|
|
31
|
+
cluster: cluster, conf: conf, bin: bin, bin_overrides: bin_overrides,
|
|
32
|
+
submit_host: submit_host, strict_host_checking: strict_host_checking,
|
|
33
|
+
id: id
|
|
34
|
+
)
|
|
29
35
|
Adapters::Slurm.new(slurm: slurm)
|
|
30
36
|
end
|
|
31
37
|
end
|
|
@@ -84,6 +90,11 @@ module OodCore
|
|
|
84
90
|
# @return [Bool]; true if empty
|
|
85
91
|
attr_reader :strict_host_checking
|
|
86
92
|
|
|
93
|
+
# The ID of the cluster.
|
|
94
|
+
# @example oakley
|
|
95
|
+
# @return [String]; The ID of the cluster.
|
|
96
|
+
attr_reader :id
|
|
97
|
+
|
|
87
98
|
# The root exception class that all Slurm-specific exceptions inherit
|
|
88
99
|
# from
|
|
89
100
|
class Error < StandardError; end
|
|
@@ -97,13 +108,14 @@ module OodCore
|
|
|
97
108
|
# @param bin_overrides [#to_h] a hash of bin ovverides to be used in job
|
|
98
109
|
# @param submit_host [#to_s] Submits the job on a login node via ssh
|
|
99
110
|
# @param strict_host_checking [Bool] Whether to use strict host checking when ssh to submit_host
|
|
100
|
-
def initialize(cluster: nil, bin: nil, conf: nil, bin_overrides: {}, submit_host: "", strict_host_checking: true)
|
|
111
|
+
def initialize(cluster: nil, bin: nil, conf: nil, bin_overrides: {}, submit_host: "", strict_host_checking: true, id: 'unknown')
|
|
101
112
|
@cluster = cluster && cluster.to_s
|
|
102
113
|
@conf = conf && Pathname.new(conf.to_s)
|
|
103
114
|
@bin = Pathname.new(bin.to_s)
|
|
104
115
|
@bin_overrides = bin_overrides
|
|
105
116
|
@submit_host = submit_host.to_s
|
|
106
117
|
@strict_host_checking = strict_host_checking
|
|
118
|
+
@id = id.to_s
|
|
107
119
|
end
|
|
108
120
|
|
|
109
121
|
# Get a ClusterInfo object containing information about the given cluster
|
|
@@ -182,22 +194,29 @@ module OodCore
|
|
|
182
194
|
|
|
183
195
|
def accounts
|
|
184
196
|
user = Etc.getlogin
|
|
185
|
-
args = [
|
|
197
|
+
args = [
|
|
198
|
+
'-nP', 'show', 'users', 'withassoc', 'format=account,qos',
|
|
199
|
+
'where', "user=#{user}", "cluster=#{id}"
|
|
200
|
+
]
|
|
186
201
|
|
|
187
|
-
[].tap do |
|
|
202
|
+
[].tap do |associations|
|
|
188
203
|
call('sacctmgr', *args).each_line do |line|
|
|
189
|
-
acct,
|
|
204
|
+
acct, qos = line.split('|')
|
|
190
205
|
next if acct.nil? || acct.chomp.empty?
|
|
191
206
|
|
|
192
|
-
|
|
207
|
+
associations << {
|
|
193
208
|
name: acct,
|
|
194
209
|
qos: qos.to_s.chomp.split(','),
|
|
195
|
-
cluster: cluster,
|
|
196
|
-
queue: queue.to_s.empty? ? nil : queue
|
|
197
210
|
}
|
|
198
|
-
info = OodCore::Job::AccountInfo.new(**args) unless acct.nil?
|
|
199
|
-
accts << info unless acct.nil?
|
|
200
211
|
end
|
|
212
|
+
end.group_by do |x|
|
|
213
|
+
[x[:name], x[:cluster]]
|
|
214
|
+
end.map do |(name, cluster), assocs|
|
|
215
|
+
OodCore::Job::AccountInfo.new(
|
|
216
|
+
name: name,
|
|
217
|
+
cluster: cluster,
|
|
218
|
+
qos: (assocs.flat_map { |x| x[:qos] }).uniq,
|
|
219
|
+
)
|
|
201
220
|
end
|
|
202
221
|
end
|
|
203
222
|
|
|
@@ -430,7 +449,6 @@ module OodCore
|
|
|
430
449
|
end.to_h.symbolize_keys
|
|
431
450
|
|
|
432
451
|
hsh[:name] = hsh[:PartitionName]
|
|
433
|
-
hsh[:qos] = hsh[:QoS].to_s == 'N/A' ? [] : hsh[:QoS].to_s.split(',')
|
|
434
452
|
hsh[:allow_accounts] = if hsh[:AllowAccounts].nil? || hsh[:AllowAccounts].to_s == 'ALL'
|
|
435
453
|
nil
|
|
436
454
|
else
|
|
@@ -443,6 +461,16 @@ module OodCore
|
|
|
443
461
|
hsh[:DenyAccounts].nil? ? [] : hsh[:DenyAccounts].to_s.split(',')
|
|
444
462
|
end
|
|
445
463
|
|
|
464
|
+
hsh[:allow_qos] = if hsh[:AllowQos].nil? || hsh[:AllowQos].to_s == 'ALL'
|
|
465
|
+
[]
|
|
466
|
+
else
|
|
467
|
+
hsh[:AllowQos].to_s.split(',')
|
|
468
|
+
end
|
|
469
|
+
hsh[:deny_qos] = if !hsh[:allow_qos].empty?
|
|
470
|
+
[] # manpage says that AllowQos negates DenyQos
|
|
471
|
+
else
|
|
472
|
+
hsh[:DenyQos].nil? ? [] : hsh[:DenyQos].to_s.split(',')
|
|
473
|
+
end
|
|
446
474
|
hsh[:tres] = case hsh[:TRES]
|
|
447
475
|
when nil, '(null)', ''
|
|
448
476
|
{}
|
|
@@ -10,7 +10,8 @@ class OodCore::Job::QueueInfo
|
|
|
10
10
|
alias to_s name
|
|
11
11
|
|
|
12
12
|
# The QoSes associated with this queue
|
|
13
|
-
attr_reader :
|
|
13
|
+
attr_reader :allow_qos
|
|
14
|
+
attr_reader :deny_qos
|
|
14
15
|
|
|
15
16
|
# The accounts that are allowed to use this queue.
|
|
16
17
|
#
|
|
@@ -25,7 +26,8 @@ class OodCore::Job::QueueInfo
|
|
|
25
26
|
|
|
26
27
|
def initialize(**opts)
|
|
27
28
|
@name = opts.fetch(:name, 'unknown')
|
|
28
|
-
@
|
|
29
|
+
@allow_qos = opts.fetch(:allow_qos, [])
|
|
30
|
+
@deny_qos = opts.fetch(:deny_qos, [])
|
|
29
31
|
@tres = opts.fetch(:tres, {})
|
|
30
32
|
|
|
31
33
|
allow_accounts = opts.fetch(:allow_accounts, nil)
|
|
@@ -50,4 +52,8 @@ class OodCore::Job::QueueInfo
|
|
|
50
52
|
def gpu?
|
|
51
53
|
tres.keys.any? { |name| name.to_s.match?(%r{^gres/gpu($|:)}i) }
|
|
52
54
|
end
|
|
55
|
+
|
|
56
|
+
def allow_all_qos?
|
|
57
|
+
allow_qos.empty? && deny_qos.empty?
|
|
58
|
+
end
|
|
53
59
|
end
|
data/lib/ood_core/version.rb
CHANGED
data/ood_core.gemspec
CHANGED
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
|
|
25
25
|
spec.add_runtime_dependency "ood_support", "~> 0.0.2"
|
|
26
26
|
spec.add_runtime_dependency "ffi", "~> 1.16.3"
|
|
27
|
+
spec.add_runtime_dependency "fog-openstack", "~> 1.1.5"
|
|
27
28
|
spec.add_runtime_dependency "rexml", "~> 3.2"
|
|
28
29
|
spec.add_development_dependency "bundler", "~> 2.1"
|
|
29
30
|
spec.add_development_dependency "rake", "~> 13.3.0"
|
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.
|
|
4
|
+
version: 0.30.0
|
|
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: 2025-
|
|
13
|
+
date: 2025-11-25 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: ood_support
|
|
@@ -40,6 +40,20 @@ dependencies:
|
|
|
40
40
|
- - "~>"
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
42
|
version: 1.16.3
|
|
43
|
+
- !ruby/object:Gem::Dependency
|
|
44
|
+
name: fog-openstack
|
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - "~>"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 1.1.5
|
|
50
|
+
type: :runtime
|
|
51
|
+
prerelease: false
|
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - "~>"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 1.1.5
|
|
43
57
|
- !ruby/object:Gem::Dependency
|
|
44
58
|
name: rexml
|
|
45
59
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -207,6 +221,8 @@ files:
|
|
|
207
221
|
- lib/ood_core/job/adapters/coder.rb
|
|
208
222
|
- lib/ood_core/job/adapters/coder/batch.rb
|
|
209
223
|
- lib/ood_core/job/adapters/coder/coder_job_info.rb
|
|
224
|
+
- lib/ood_core/job/adapters/coder/credentials.rb
|
|
225
|
+
- lib/ood_core/job/adapters/coder/openstack_credentials.rb
|
|
210
226
|
- lib/ood_core/job/adapters/drmaa.rb
|
|
211
227
|
- lib/ood_core/job/adapters/fujitsu_tcs.rb
|
|
212
228
|
- lib/ood_core/job/adapters/helper.rb
|