redfish_tools 0.2.6 → 0.2.7
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 +5 -5
- data/lib/redfish_tools/exceptions.rb +7 -0
- data/lib/redfish_tools/recorder.rb +34 -15
- data/lib/redfish_tools/server.rb +28 -3
- data/lib/redfish_tools/servlet.rb +84 -17
- data/lib/redfish_tools/sse_server.rb +10 -2
- data/lib/redfish_tools/utils.rb +24 -0
- data/lib/redfish_tools/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d6eb47b4ce78b595e4465c9efcda2ca2a8700b22257bd039cd4651411b0b5e9d
|
4
|
+
data.tar.gz: d6fb33b3c654c4edd44279bfb107ecbc941d0d456bac7c0652062a88449d9fcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6a62c917f853e1812c9ce2a2266aeebfdcff73d313a92a7b5d1434e596084d8d50eea6a0acf4c05d8935c44326aac2dd75705ff7f05a5fd62ceb2c9cd43d234
|
7
|
+
data.tar.gz: c88535b973f5010dcdd50b9b218882fc45240b63b2a22fb93cffe868bf4e6c9fc3b6601668680cf9c4407ccdb662ab1bff474813e7674319b86746440aaa857e
|
@@ -36,13 +36,30 @@ module RedfishTools
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def process_oid(oid)
|
39
|
-
|
40
|
-
resource
|
41
|
-
|
42
|
-
|
39
|
+
resource, duration = fetch_resource(oid)
|
40
|
+
if resource
|
41
|
+
persist_to_disk(oid, resource.raw, resource.headers, duration)
|
42
|
+
extract_oids(resource.raw)
|
43
|
+
else
|
44
|
+
puts("!!!! FAILED TO FETCH #{oid} !!!!")
|
45
|
+
Set.new
|
46
|
+
end
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
49
|
+
def fetch_resource(oid)
|
50
|
+
3.times do
|
51
|
+
begin
|
52
|
+
start = Time.now
|
53
|
+
resource = @client.find(oid)
|
54
|
+
duration = Time.now - start
|
55
|
+
return [resource, duration] if resource
|
56
|
+
rescue JSON::ParserError => e
|
57
|
+
return [nil, nil]
|
58
|
+
end
|
59
|
+
|
60
|
+
sleep(2)
|
61
|
+
end
|
62
|
+
[nil, nil]
|
46
63
|
end
|
47
64
|
|
48
65
|
def persist_to_disk(oid, body, headers, duration)
|
@@ -81,18 +98,20 @@ module RedfishTools
|
|
81
98
|
File.open(path, "w") { |f| f.write(data) }
|
82
99
|
end
|
83
100
|
|
84
|
-
def extract_oids_from_hash(data)
|
85
|
-
ids = Set.new
|
86
|
-
ids.add(data["@odata.id"]) if data["@odata.id"]
|
87
|
-
data.values.reduce(ids) { |a, v| a.merge(extract_oids(v)) }
|
88
|
-
end
|
89
|
-
|
90
101
|
def extract_oids(data)
|
102
|
+
result = Set.new
|
103
|
+
|
91
104
|
case data
|
92
|
-
when Hash then
|
93
|
-
when Array then data.
|
94
|
-
|
105
|
+
when Hash then data.values.each { |v| result.merge(extract_oids(v)) }
|
106
|
+
when Array then data.each { |v| result.merge(extract_oids(v)) }
|
107
|
+
when String then result.add(data) if path?(data)
|
95
108
|
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
113
|
+
def path?(data)
|
114
|
+
/^\/redfish\/v1\/[^# ]*$/.match(data)
|
96
115
|
end
|
97
116
|
end
|
98
117
|
end
|
data/lib/redfish_tools/server.rb
CHANGED
@@ -28,13 +28,26 @@ module RedfishTools
|
|
28
28
|
Base64.strict_encode64("#{username}:#{password}")
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def actions
|
32
32
|
# Hash of action_id => system_id
|
33
|
-
@
|
33
|
+
@actions ||= load_all_actions
|
34
|
+
end
|
35
|
+
|
36
|
+
def systems
|
37
|
+
@systems ||= load_all_system_ids
|
34
38
|
end
|
35
39
|
|
36
40
|
private
|
37
41
|
|
42
|
+
def load_all_system_ids
|
43
|
+
systems_id = datastore.get("/redfish/v1").body["Systems"]["@odata.id"]
|
44
|
+
datastore.get(systems_id).body["Members"].map { |l| l["@odata.id"] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_all_actions
|
48
|
+
load_all_system_actions.merge(load_update_actions)
|
49
|
+
end
|
50
|
+
|
38
51
|
def load_all_system_actions
|
39
52
|
systems_id = datastore.get("/redfish/v1").body["Systems"]["@odata.id"]
|
40
53
|
datastore.get(systems_id).body["Members"].reduce({}) do |acc, link|
|
@@ -45,7 +58,19 @@ module RedfishTools
|
|
45
58
|
def load_system_actions(id)
|
46
59
|
actions = datastore.get(id).body.dig("Actions") || {}
|
47
60
|
actions.each_with_object({}) do |(name, data), acc|
|
48
|
-
acc[data["target"]] = { name: name,
|
61
|
+
acc[data["target"]] = { name: name, id: id }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_update_actions
|
66
|
+
update_service_id = datastore.get("/redfish/v1").body.dig(
|
67
|
+
"UpdateService", "@odata.id",
|
68
|
+
)
|
69
|
+
return {} if update_service_id.nil?
|
70
|
+
|
71
|
+
actions = datastore.get(update_service_id).body["Actions"] || {}
|
72
|
+
actions.each_with_object({}) do |(name, data), acc|
|
73
|
+
acc[data["target"]] = { name: name, id: update_service_id }
|
49
74
|
end
|
50
75
|
end
|
51
76
|
end
|
@@ -5,6 +5,8 @@ require "json"
|
|
5
5
|
require "securerandom"
|
6
6
|
require "set"
|
7
7
|
require "webrick"
|
8
|
+
require "redfish_tools/utils"
|
9
|
+
require "redfish_tools/exceptions"
|
8
10
|
|
9
11
|
module RedfishTools
|
10
12
|
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
@@ -12,28 +14,36 @@ module RedfishTools
|
|
12
14
|
|
13
15
|
def_delegators :@server,
|
14
16
|
:datastore, :login_path, :username, :password,
|
15
|
-
:basic_auth_header, :
|
17
|
+
:basic_auth_header, :actions, :systems
|
16
18
|
|
17
19
|
BAD_HEADERS = Set.new(["connection", "content-length", "keep-alive"])
|
18
20
|
DEFAULT_HEADERS = {
|
19
21
|
"content-type" => "application/json"
|
20
22
|
}.freeze
|
21
23
|
TRANSITIONS = {
|
22
|
-
"
|
23
|
-
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
24
|
+
"PoweringOn" => {}.freeze,
|
25
|
+
"On" => {
|
26
|
+
"GracefulShutdown" => "Off",
|
27
|
+
"ForceOff" => "Off",
|
28
|
+
"PushPowerButton" => "Off",
|
29
|
+
"Nmi" => "Off",
|
30
|
+
"GracefulRestart" => "On",
|
31
|
+
"ForceRestart" => "On",
|
32
|
+
"PowerCycle" => "On",
|
30
33
|
}.freeze,
|
31
|
-
"
|
34
|
+
"PoweringOff" => {}.freeze,
|
35
|
+
"Off" => {
|
32
36
|
"On" => "On",
|
33
37
|
"ForceOn" => "On",
|
34
38
|
"PushPowerButton" => "On",
|
35
39
|
}.freeze,
|
36
40
|
}.freeze
|
41
|
+
TASK_TRANSITIONS = {
|
42
|
+
"New" => "Starting",
|
43
|
+
"Starting" => "Running",
|
44
|
+
"Running" => "Completed",
|
45
|
+
"Completed" => "Completed",
|
46
|
+
}
|
37
47
|
|
38
48
|
def service(request, response)
|
39
49
|
return response.status = 401 unless authorized?(request)
|
@@ -46,12 +56,22 @@ module RedfishTools
|
|
46
56
|
|
47
57
|
item = datastore.get(request.path)
|
48
58
|
response.status = 200
|
59
|
+
|
60
|
+
if request.path.chomp("/").end_with?("monitor") && item.body["TaskState"]
|
61
|
+
item.body["TaskState"] = TASK_TRANSITIONS[item.body["TaskState"]]
|
62
|
+
response.status = 202
|
63
|
+
if item.body["TaskState"] == "Completed"
|
64
|
+
response.status = 200
|
65
|
+
item.body["EndTime"] = Time.now.utc.iso8601
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
49
69
|
set_headers(response, item.headers)
|
50
70
|
response.body = item.body.to_json
|
51
71
|
end
|
52
72
|
|
53
73
|
def do_POST(request, response)
|
54
|
-
action =
|
74
|
+
action = actions[request.path]
|
55
75
|
item = datastore.get(request.path)
|
56
76
|
return response.status = 404 unless action || item.body
|
57
77
|
return response.status = 405 if action.nil? && item.body["Members"].nil?
|
@@ -78,8 +98,15 @@ module RedfishTools
|
|
78
98
|
response.status = 501
|
79
99
|
end
|
80
100
|
|
81
|
-
def do_PATCH(
|
82
|
-
|
101
|
+
def do_PATCH(request, response)
|
102
|
+
system = datastore.get(request.path)
|
103
|
+
return response.status = 404 unless system.body
|
104
|
+
system.body = Utils.combine_hashes(system.body, JSON.parse(request.body))
|
105
|
+
response.status = 200
|
106
|
+
rescue Exceptions::MergeConflict => error
|
107
|
+
response.status = 405
|
108
|
+
set_headers(response)
|
109
|
+
response.body = error_body(error).to_json
|
83
110
|
end
|
84
111
|
|
85
112
|
def do_DELETE(request, response)
|
@@ -97,13 +124,22 @@ module RedfishTools
|
|
97
124
|
end
|
98
125
|
|
99
126
|
def execute_action(action, data)
|
100
|
-
|
101
|
-
|
102
|
-
|
127
|
+
resource = datastore.get(action[:id]).body
|
128
|
+
case action[:name]
|
129
|
+
when "#ComputerSystem.Reset"
|
130
|
+
execute_computer_system_reset(resource, data)
|
131
|
+
when "#UpdateService.SimpleUpdate"
|
132
|
+
execute_update_service_simple_update(resource, data)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def execute_computer_system_reset(system, data)
|
137
|
+
action = system["Actions"]["#ComputerSystem.Reset"]
|
103
138
|
reset = data["ResetType"]
|
104
139
|
|
140
|
+
# TODO(@tadeboro): Handle ActionInfo case also.
|
105
141
|
unless action["ResetType@Redfish.AllowableValues"].include?(reset)
|
106
|
-
return error_body("Invalid reset type"),
|
142
|
+
return error_body("Invalid reset type"), nil, 400
|
107
143
|
end
|
108
144
|
|
109
145
|
unless TRANSITIONS[system["PowerState"]].key?(reset)
|
@@ -116,6 +152,37 @@ module RedfishTools
|
|
116
152
|
[error_body("Success"), nil, 200]
|
117
153
|
end
|
118
154
|
|
155
|
+
def execute_update_service_simple_update(service, data)
|
156
|
+
action = service["Actions"]["#UpdateService.SimpleUpdate"]
|
157
|
+
proto = data["TransferProtocol"]
|
158
|
+
targets = data["Targets"] || []
|
159
|
+
|
160
|
+
# TODO(@tadeboro): Handle ActionInfo case also.
|
161
|
+
unless action["TransferProtocol@Redfish.AllowableValues"].include?(proto)
|
162
|
+
return error_body("Invalid transfer protocol value"), nil, 400
|
163
|
+
end
|
164
|
+
|
165
|
+
unless (targets - systems).empty?
|
166
|
+
return error_body("Invalid targets: #{targets - systems}"), nil, 400
|
167
|
+
end
|
168
|
+
|
169
|
+
task_service_id = datastore.get("/redfish/v1").body["Tasks"]["@odata.id"]
|
170
|
+
task_col_id = datastore.get(task_service_id).body["Tasks"]["@odata.id"]
|
171
|
+
|
172
|
+
body, _, _ = new_item(datastore.get(task_col_id), {
|
173
|
+
"TaskState" => "New",
|
174
|
+
"StartTime" => Time.new.utc.iso8601,
|
175
|
+
})
|
176
|
+
|
177
|
+
task_monitor_path = body["@odata.id"] + "/monitor"
|
178
|
+
headers = DEFAULT_HEADERS.merge("location" => task_monitor_path)
|
179
|
+
|
180
|
+
body["TaskMonitor"] = task_monitor_path
|
181
|
+
datastore.set(task_monitor_path, body, headers: headers)
|
182
|
+
|
183
|
+
[body, headers, 202]
|
184
|
+
end
|
185
|
+
|
119
186
|
def login_path?(path)
|
120
187
|
login_path == path.chomp("/")
|
121
188
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "date"
|
4
4
|
require "json"
|
5
|
+
require "securerandom"
|
5
6
|
require "socket"
|
6
7
|
|
7
8
|
module RedfishTools
|
@@ -30,13 +31,20 @@ module RedfishTools
|
|
30
31
|
id = 0
|
31
32
|
loop do
|
32
33
|
event = @events.sample
|
33
|
-
event["Events"]
|
34
|
+
make_events_unique(event["Events"])
|
34
35
|
socket.print("id: #{id}\ndata: #{event.to_json}\n\n")
|
35
|
-
sleep(rand(1..
|
36
|
+
sleep(rand(1..60))
|
36
37
|
id += 1
|
37
38
|
end
|
38
39
|
ensure
|
39
40
|
socket.close
|
40
41
|
end
|
42
|
+
|
43
|
+
def make_events_unique(events)
|
44
|
+
events.each do |event|
|
45
|
+
event["EventTimestamp"] = Time.now.utc.to_s
|
46
|
+
event["EventId"] = SecureRandom.uuid
|
47
|
+
end
|
48
|
+
end
|
41
49
|
end
|
42
50
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "redfish_tools/exceptions"
|
2
|
+
|
3
|
+
module RedfishTools
|
4
|
+
module Utils
|
5
|
+
def self.combine_hashes(original, b, path: nil)
|
6
|
+
path ||= []
|
7
|
+
a = original.clone
|
8
|
+
b.each {|key, value|
|
9
|
+
if a.include?(key)
|
10
|
+
if (a[key].is_a? Hash) && (b[key].is_a? Hash)
|
11
|
+
a[key] = combine_hashes(a[key], b[key], path: path + [key.to_s])
|
12
|
+
elsif a[key].is_a? b[key].class
|
13
|
+
a[key] = b[key]
|
14
|
+
else
|
15
|
+
raise Exceptions::MergeConflict, "Conflict at '%s'" % (path + [key.to_s]).join(".")
|
16
|
+
end
|
17
|
+
else
|
18
|
+
raise Exceptions::MergeConflict, "Key '%s' does not exists in source hash" % (path + [key.to_s]).join(".")
|
19
|
+
end
|
20
|
+
}
|
21
|
+
a
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redfish_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tadej Borovšak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02
|
11
|
+
date: 2019-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redfish_client
|
@@ -148,11 +148,13 @@ files:
|
|
148
148
|
- lib/redfish_tools/cli/serve.rb
|
149
149
|
- lib/redfish_tools/cli/serve_sse.rb
|
150
150
|
- lib/redfish_tools/datastore.rb
|
151
|
+
- lib/redfish_tools/exceptions.rb
|
151
152
|
- lib/redfish_tools/recorder.rb
|
152
153
|
- lib/redfish_tools/server.rb
|
153
154
|
- lib/redfish_tools/servlet.rb
|
154
155
|
- lib/redfish_tools/sse_client.rb
|
155
156
|
- lib/redfish_tools/sse_server.rb
|
157
|
+
- lib/redfish_tools/utils.rb
|
156
158
|
- lib/redfish_tools/version.rb
|
157
159
|
- redfish_tools.gemspec
|
158
160
|
homepage: https://github.com/xlab-si/redfish-tools-ruby
|
@@ -176,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
178
|
version: '0'
|
177
179
|
requirements: []
|
178
180
|
rubyforge_project:
|
179
|
-
rubygems_version: 2.
|
181
|
+
rubygems_version: 2.7.7
|
180
182
|
signing_key:
|
181
183
|
specification_version: 4
|
182
184
|
summary: Collection of tools for working with Redfish services.
|