ood_core 0.30.2 → 0.31.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/helpers/openstack.rb +107 -0
- data/lib/ood_core/job/adapters/pbspro.rb +56 -0
- data/lib/ood_core/job/adapters/slurm.rb +8 -8
- data/lib/ood_core/version.rb +1 -1
- data/lib/ood_core.rb +5 -0
- data/ood_core.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1ad113f64d7ee39779802e7386bf753a2323969302963c58456b51a05f132b5
|
|
4
|
+
data.tar.gz: 472bcdea0fbd7096171a8338507e0e9c6554d6a4ceea3fb12f46a4a59f48cf7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6436bdfb152bd592627f6fb710d44fc08e10d240a398199081ffd0ba1e3fd3a6f4fbaf8fb56adb88656bb58557eb6630702baea2e07ab1e5072f9180e02da967
|
|
7
|
+
data.tar.gz: 12eb878a31d953d7fb4c8bb6a0e67febf42b5c04aa397aa9fb17329d9aae33601e2d7358c3799ede4be5ef8af84fc9cb8d44e022847a4d42f3162788e5db828d
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require "fog/openstack"
|
|
2
|
+
require "json"
|
|
3
|
+
require "etc"
|
|
4
|
+
|
|
5
|
+
module OodCore
|
|
6
|
+
class OpenStackHelper
|
|
7
|
+
attr_reader :auth_url, :openstack_instance
|
|
8
|
+
|
|
9
|
+
def initialize(token_file:, openstack_instance:)
|
|
10
|
+
@token_file = token_file
|
|
11
|
+
@openstack_instance = openstack_instance
|
|
12
|
+
@auth_url = "https://identity.#{openstack_instance}/v3"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Load token data from the token file
|
|
16
|
+
# @return [Hash] Parsed token JSON or nil if file does not exist
|
|
17
|
+
def load_token_data
|
|
18
|
+
return nil unless File.exist?(@token_file)
|
|
19
|
+
JSON.parse(File.read(@token_file))
|
|
20
|
+
rescue Errno::ENOENT => e
|
|
21
|
+
puts "Error loading token: #{e}"
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get access token from loaded credentials
|
|
26
|
+
# @return [String] The token ID
|
|
27
|
+
def access_token
|
|
28
|
+
load_token_data&.[]("id")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get user ID from loaded credentials
|
|
32
|
+
# @return [String] The user ID
|
|
33
|
+
def user_id
|
|
34
|
+
load_token_data&.[]("user_id")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Fetch all projects for the authenticated user
|
|
38
|
+
# @return [Array<Hash>] Array of project hashes with id and name
|
|
39
|
+
def fetch_user_projects
|
|
40
|
+
connection_params = {
|
|
41
|
+
openstack_auth_url: auth_url,
|
|
42
|
+
openstack_management_url: auth_url,
|
|
43
|
+
openstack_auth_token: access_token,
|
|
44
|
+
}
|
|
45
|
+
identity = Fog::OpenStack::Identity.new(connection_params)
|
|
46
|
+
identity.list_user_projects(user_id).body["projects"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Fetch all flavors across all projects for a user
|
|
50
|
+
# @return [Array<Array>] Sorted array of [display_string, flavor_name, project_id]
|
|
51
|
+
def fetch_all_flavors
|
|
52
|
+
flavors = []
|
|
53
|
+
|
|
54
|
+
fetch_user_projects.each do |project|
|
|
55
|
+
scoped_token = scope_token_to_project(access_token, project['id'])
|
|
56
|
+
|
|
57
|
+
compute_connection_params = {
|
|
58
|
+
openstack_auth_url: auth_url,
|
|
59
|
+
openstack_project_name: project['name'],
|
|
60
|
+
openstack_management_url: "https://compute.#{openstack_instance}/v2.1/#{project['id']}",
|
|
61
|
+
openstack_auth_token: scoped_token,
|
|
62
|
+
}
|
|
63
|
+
compute = Fog::OpenStack::Compute.new(compute_connection_params)
|
|
64
|
+
|
|
65
|
+
compute.flavors.each do |flavor|
|
|
66
|
+
flavors << [
|
|
67
|
+
"#{flavor.name} - #{flavor.vcpus}VCPUS, #{flavor.ram/1024}GB RAM, #{flavor.disk}GB disk",
|
|
68
|
+
flavor.name,
|
|
69
|
+
project['id']
|
|
70
|
+
]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
flavors.sort
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Convenience method that returns both projects and flavors
|
|
78
|
+
# @return [Array] Array containing [projects, flavors]
|
|
79
|
+
def load_projects_and_flavors
|
|
80
|
+
[fetch_user_projects, fetch_all_flavors]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Scope token to a specific project
|
|
84
|
+
# @param access_token [String] The unscoped token ID
|
|
85
|
+
# @param project_id [String] The project ID to scope to
|
|
86
|
+
# @return [String] The scoped token ID
|
|
87
|
+
def scope_token_to_project(access_token, project_id)
|
|
88
|
+
auth = {
|
|
89
|
+
"auth": {
|
|
90
|
+
"identity": {
|
|
91
|
+
"methods": ["token"],
|
|
92
|
+
"token": { "id": access_token }
|
|
93
|
+
},
|
|
94
|
+
"scope": { "project": { "id": project_id } }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
connection_params = {
|
|
99
|
+
openstack_auth_url: auth_url,
|
|
100
|
+
openstack_management_url: auth_url,
|
|
101
|
+
openstack_auth_token: access_token,
|
|
102
|
+
}
|
|
103
|
+
identity = Fog::OpenStack::Identity.new(connection_params)
|
|
104
|
+
identity.tokens.authenticate(auth)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "time"
|
|
2
|
+
require "json"
|
|
2
3
|
require "ood_core/refinements/hash_extensions"
|
|
3
4
|
require "ood_core/job/adapters/helper"
|
|
4
5
|
|
|
@@ -85,6 +86,50 @@ module OodCore
|
|
|
85
86
|
@bin_overrides = bin_overrides
|
|
86
87
|
end
|
|
87
88
|
|
|
89
|
+
# Get a ClusterInfo object containing information about the given cluster
|
|
90
|
+
# @return [ClusterInfo] object containing cluster details
|
|
91
|
+
def get_cluster_info
|
|
92
|
+
args = ["-a", "-F", "json"]
|
|
93
|
+
stdout = call("pbsnodes", *args)
|
|
94
|
+
node_info = JSON.parse(stdout)
|
|
95
|
+
|
|
96
|
+
# Initialize cluster info values
|
|
97
|
+
total_nodes = 0
|
|
98
|
+
allocated_nodes = 0
|
|
99
|
+
total_cpus = 0
|
|
100
|
+
allocated_cpus = 0
|
|
101
|
+
total_gpus = 0
|
|
102
|
+
allocated_gpus = 0
|
|
103
|
+
|
|
104
|
+
nodes = node_info.fetch('nodes', {})
|
|
105
|
+
|
|
106
|
+
nodes.each do |_node_name, node|
|
|
107
|
+
total_nodes += 1
|
|
108
|
+
resources_avail = node.fetch('resources_available', {})
|
|
109
|
+
total_cpus += get_node_resource(resources_avail, 'ncpus')
|
|
110
|
+
total_gpus += get_node_resource(resources_avail, 'ngpus')
|
|
111
|
+
|
|
112
|
+
# Resources assigned (currently allocated to jobs)
|
|
113
|
+
resources_assigned = node.fetch('resources_assigned', {})
|
|
114
|
+
ncpus_assigned = get_node_resource(resources_assigned, 'ncpus')
|
|
115
|
+
ngpus_assigned = get_node_resource(resources_assigned, 'ngpus')
|
|
116
|
+
|
|
117
|
+
allocated_cpus += ncpus_assigned
|
|
118
|
+
allocated_gpus += ngpus_assigned
|
|
119
|
+
|
|
120
|
+
# A node is allocated if at least one CPU has been assigned to a job
|
|
121
|
+
allocated_nodes += 1 if ncpus_assigned > 0
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
ClusterInfo.new(active_nodes: allocated_nodes,
|
|
125
|
+
total_nodes: total_nodes,
|
|
126
|
+
active_processors: allocated_cpus,
|
|
127
|
+
total_processors: total_cpus,
|
|
128
|
+
active_gpus: allocated_gpus,
|
|
129
|
+
total_gpus: total_gpus
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
88
133
|
# Get a list of hashes detailing each of the jobs on the batch server
|
|
89
134
|
# @example Status info for all jobs
|
|
90
135
|
# my_batch.get_jobs
|
|
@@ -174,6 +219,13 @@ module OodCore
|
|
|
174
219
|
end
|
|
175
220
|
|
|
176
221
|
private
|
|
222
|
+
# Get a resource value from a node's resources hash, returning 0 if the
|
|
223
|
+
# resource is not present
|
|
224
|
+
def get_node_resource(resources, key)
|
|
225
|
+
val = resources.fetch(key, 0)
|
|
226
|
+
val.to_i
|
|
227
|
+
end
|
|
228
|
+
|
|
177
229
|
# Call a forked PBS Pro command for a given batch server
|
|
178
230
|
def call(cmd, *args, env: {}, stdin: "", chdir: nil)
|
|
179
231
|
cmd = cmd.to_s
|
|
@@ -299,6 +351,10 @@ module OodCore
|
|
|
299
351
|
raise JobAdapterError, e.message
|
|
300
352
|
end
|
|
301
353
|
|
|
354
|
+
def cluster_info
|
|
355
|
+
@pbspro.get_cluster_info
|
|
356
|
+
end
|
|
357
|
+
|
|
302
358
|
# Retrieve info for all jobs from the resource manager
|
|
303
359
|
# @raise [JobAdapterError] if something goes wrong getting job info
|
|
304
360
|
# @return [Array<Info>] information describing submitted jobs
|
|
@@ -47,7 +47,7 @@ module OodCore
|
|
|
47
47
|
# calculated from gres string
|
|
48
48
|
# @return [Integer] the number of gpus in gres
|
|
49
49
|
def self.gpus_from_gres(gres)
|
|
50
|
-
gres.to_s.scan(/gpu[
|
|
50
|
+
gres.to_s.scan(/gpu[s:]*[\w()-]*[=:]?(\d+)(?:[(,]|$)/).flatten.map(&:to_i).sum
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Object used for simplified communication with a Slurm batch server
|
|
@@ -577,7 +577,7 @@ module OodCore
|
|
|
577
577
|
'OOM' => :completed, # OUT_OF_MEMORY
|
|
578
578
|
|
|
579
579
|
'BOOT_FAIL' => :completed,
|
|
580
|
-
'
|
|
580
|
+
'CANCELLED' => :completed,
|
|
581
581
|
'COMPLETED' => :completed,
|
|
582
582
|
'DEADLINE' => :completed,
|
|
583
583
|
'FAILED' => :completed,
|
|
@@ -879,11 +879,11 @@ module OodCore
|
|
|
879
879
|
"%02d:%02d:%02d" % [time/3600, time/60%60, time%60]
|
|
880
880
|
end
|
|
881
881
|
|
|
882
|
-
#
|
|
882
|
+
# safely parse date time string, return nil when there are errors.
|
|
883
883
|
def parse_time(date_time)
|
|
884
|
-
return nil if date_time.empty? || %w[N/A NONE UNKNOWN].include?(date_time.to_s.upcase)
|
|
885
|
-
|
|
886
884
|
Time.parse(date_time)
|
|
885
|
+
rescue ArgumentError
|
|
886
|
+
nil
|
|
887
887
|
end
|
|
888
888
|
|
|
889
889
|
# Convert host list string to individual nodes
|
|
@@ -910,7 +910,7 @@ module OodCore
|
|
|
910
910
|
|
|
911
911
|
# Determine state from Slurm state code
|
|
912
912
|
def get_state(st)
|
|
913
|
-
STATE_MAP.fetch(st, :undetermined)
|
|
913
|
+
STATE_MAP.fetch(st.split.first, :undetermined)
|
|
914
914
|
end
|
|
915
915
|
|
|
916
916
|
# Parse hash describing Slurm job status
|
|
@@ -937,8 +937,8 @@ module OodCore
|
|
|
937
937
|
wallclock_time: duration_in_seconds(v[:time_used]),
|
|
938
938
|
wallclock_limit: duration_in_seconds(v[:time_limit]),
|
|
939
939
|
cpu_time: nil,
|
|
940
|
-
submission_time:
|
|
941
|
-
dispatch_time: (v[:start_time]
|
|
940
|
+
submission_time: parse_time(v[:submit_time]),
|
|
941
|
+
dispatch_time: parse_time(v[:start_time]),
|
|
942
942
|
native: v,
|
|
943
943
|
gpus: self.class.gpus_from_gres(v[:gres])
|
|
944
944
|
)
|
data/lib/ood_core/version.rb
CHANGED
data/lib/ood_core.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "ood_core/cluster"
|
|
|
4
4
|
require "ood_core/clusters"
|
|
5
5
|
require "ood_core/invalid_cluster"
|
|
6
6
|
require "ood_core/data_formatter"
|
|
7
|
+
require "ood_core/helpers/openstack"
|
|
7
8
|
|
|
8
9
|
# The main namespace for ood_core
|
|
9
10
|
module OodCore
|
|
@@ -42,4 +43,8 @@ module OodCore
|
|
|
42
43
|
require "ood_core/batch_connect/template"
|
|
43
44
|
require "ood_core/batch_connect/factory"
|
|
44
45
|
end
|
|
46
|
+
|
|
47
|
+
module Helpers
|
|
48
|
+
require "ood_core/helpers/openstack"
|
|
49
|
+
end
|
|
45
50
|
end
|
data/ood_core.gemspec
CHANGED
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_runtime_dependency "fog-openstack", "~> 1.1.5"
|
|
28
28
|
spec.add_runtime_dependency "rexml", "~> 3.2"
|
|
29
29
|
spec.add_development_dependency "bundler", "~> 2.1"
|
|
30
|
-
spec.add_development_dependency "rake", "~> 13.
|
|
30
|
+
spec.add_development_dependency "rake", "~> 13.4.1"
|
|
31
31
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
32
32
|
spec.add_development_dependency "pry", "~> 0.10"
|
|
33
33
|
spec.add_development_dependency "timecop", "~> 0.8"
|
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.31.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: 2026-
|
|
13
|
+
date: 2026-04-16 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: ood_support
|
|
@@ -88,14 +88,14 @@ dependencies:
|
|
|
88
88
|
requirements:
|
|
89
89
|
- - "~>"
|
|
90
90
|
- !ruby/object:Gem::Version
|
|
91
|
-
version: 13.
|
|
91
|
+
version: 13.4.1
|
|
92
92
|
type: :development
|
|
93
93
|
prerelease: false
|
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
|
95
95
|
requirements:
|
|
96
96
|
- - "~>"
|
|
97
97
|
- !ruby/object:Gem::Version
|
|
98
|
-
version: 13.
|
|
98
|
+
version: 13.4.1
|
|
99
99
|
- !ruby/object:Gem::Dependency
|
|
100
100
|
name: rspec
|
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -214,6 +214,7 @@ files:
|
|
|
214
214
|
- lib/ood_core/clusters.rb
|
|
215
215
|
- lib/ood_core/data_formatter.rb
|
|
216
216
|
- lib/ood_core/errors.rb
|
|
217
|
+
- lib/ood_core/helpers/openstack.rb
|
|
217
218
|
- lib/ood_core/invalid_cluster.rb
|
|
218
219
|
- lib/ood_core/job/account_info.rb
|
|
219
220
|
- lib/ood_core/job/adapter.rb
|