supermicro 0.1.7 → 0.1.8
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/supermicro/bios.rb +277 -0
- data/lib/supermicro/boot.rb +188 -55
- data/lib/supermicro/client.rb +1 -0
- data/lib/supermicro/jobs.rb +67 -39
- data/lib/supermicro/network.rb +13 -9
- data/lib/supermicro/storage.rb +173 -74
- data/lib/supermicro/system.rb +104 -14
- data/lib/supermicro/system_config.rb +2 -75
- data/lib/supermicro/tasks.rb +39 -1
- data/lib/supermicro/utility.rb +59 -23
- data/lib/supermicro/version.rb +1 -1
- data/lib/supermicro/virtual_media.rb +53 -10
- data/lib/supermicro.rb +1 -0
- metadata +3 -2
data/lib/supermicro/system.rb
CHANGED
@@ -12,7 +12,19 @@ module Supermicro
|
|
12
12
|
begin
|
13
13
|
data = JSON.parse(response.body)
|
14
14
|
|
15
|
-
memory = data["Members"].map do |
|
15
|
+
memory = data["Members"].map do |member|
|
16
|
+
# If member is just a reference, fetch the full data
|
17
|
+
if member["@odata.id"] && !member["CapacityMiB"]
|
18
|
+
mem_response = authenticated_request(:get, member["@odata.id"])
|
19
|
+
if mem_response.status == 200
|
20
|
+
m = JSON.parse(mem_response.body)
|
21
|
+
else
|
22
|
+
next nil
|
23
|
+
end
|
24
|
+
else
|
25
|
+
m = member
|
26
|
+
end
|
27
|
+
|
16
28
|
next if m["CapacityMiB"].nil? || m["CapacityMiB"] == 0
|
17
29
|
|
18
30
|
dimm_name = m["DeviceLocator"] || m["Name"]
|
@@ -26,16 +38,17 @@ module Supermicro
|
|
26
38
|
end
|
27
39
|
|
28
40
|
{
|
29
|
-
"
|
41
|
+
"manufacturer" => m["Manufacturer"],
|
30
42
|
"name" => dimm_name,
|
31
43
|
"capacity_bytes" => m["CapacityMiB"].to_i * 1024 * 1024,
|
32
44
|
"health" => m.dig("Status", "Health") || "N/A",
|
33
|
-
"speed_mhz" => m["
|
45
|
+
"speed_mhz" => m["OperatingSpeedMHz"],
|
34
46
|
"part_number" => m["PartNumber"],
|
35
47
|
"serial" => m["SerialNumber"],
|
36
48
|
"bank" => bank,
|
37
49
|
"index" => index.to_i,
|
38
|
-
"
|
50
|
+
"memory_device_type" => m["MemoryDeviceType"],
|
51
|
+
"base_module_type" => m["BaseModuleType"]
|
39
52
|
}
|
40
53
|
end.compact
|
41
54
|
|
@@ -99,6 +112,7 @@ module Supermicro
|
|
99
112
|
"name" => fan["Name"],
|
100
113
|
"rpm" => rpm,
|
101
114
|
"status" => health,
|
115
|
+
"state" => fan.dig("Status", "State"),
|
102
116
|
"min_rpm" => fan["MinReadingRange"],
|
103
117
|
"max_rpm" => fan["MaxReadingRange"]
|
104
118
|
}
|
@@ -167,24 +181,52 @@ module Supermicro
|
|
167
181
|
end
|
168
182
|
|
169
183
|
def nics
|
170
|
-
response = authenticated_request(:get, "/redfish/v1/Systems/1/EthernetInterfaces
|
184
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1/EthernetInterfaces")
|
171
185
|
|
172
186
|
if response.status == 200
|
173
187
|
begin
|
174
188
|
data = JSON.parse(response.body)
|
175
189
|
|
176
|
-
|
190
|
+
# If Members are not expanded, fetch each interface individually
|
191
|
+
nics = data["Members"].map do |member|
|
192
|
+
# Check if this is just a reference or full data
|
193
|
+
if member["@odata.id"] && !member["Id"]
|
194
|
+
# It's just a reference, fetch the full data
|
195
|
+
interface_response = authenticated_request(:get, member["@odata.id"])
|
196
|
+
if interface_response.status == 200
|
197
|
+
nic = JSON.parse(interface_response.body)
|
198
|
+
else
|
199
|
+
puts "Failed to fetch NIC #{member["@odata.id"]}: #{interface_response.status}".yellow
|
200
|
+
next
|
201
|
+
end
|
202
|
+
else
|
203
|
+
# We already have the full data
|
204
|
+
nic = member
|
205
|
+
end
|
206
|
+
|
207
|
+
# Create adapter structure to match iDRAC format
|
177
208
|
{
|
178
|
-
"id" => nic["Id"],
|
179
209
|
"name" => nic["Name"],
|
180
|
-
"
|
181
|
-
"
|
182
|
-
"
|
183
|
-
"
|
184
|
-
"
|
185
|
-
|
210
|
+
"manufacturer" => nic["Manufacturer"] || "Supermicro",
|
211
|
+
"model" => nil,
|
212
|
+
"part_number" => nil,
|
213
|
+
"serial" => nil,
|
214
|
+
"ports" => [
|
215
|
+
{
|
216
|
+
"name" => nic["Id"],
|
217
|
+
"status" => nic["LinkStatus"] == "LinkDown" ? "Down" : "Up",
|
218
|
+
"mac" => nic["MACAddress"],
|
219
|
+
"ipv4" => nic.dig("IPv4Addresses", 0, "Address"),
|
220
|
+
"mode" => nic.dig("IPv4Addresses", 0, "AddressOrigin"), # DHCP or Static
|
221
|
+
"mask" => nic.dig("IPv4Addresses", 0, "SubnetMask"),
|
222
|
+
"port" => 0,
|
223
|
+
"speed_mbps" => nic["SpeedMbps"] || 0,
|
224
|
+
"kind" => "ethernet",
|
225
|
+
"linux_device" => nil
|
226
|
+
}
|
227
|
+
]
|
186
228
|
}
|
187
|
-
end
|
229
|
+
end.compact # Remove any nil entries from failed fetches
|
188
230
|
|
189
231
|
return nics
|
190
232
|
rescue JSON::ParserError
|
@@ -202,12 +244,21 @@ module Supermicro
|
|
202
244
|
begin
|
203
245
|
data = JSON.parse(response.body)
|
204
246
|
|
247
|
+
# Get Manager UUID for service tag (BMC MAC address)
|
248
|
+
manager_uuid = nil
|
249
|
+
manager_response = authenticated_request(:get, "/redfish/v1/Managers/1")
|
250
|
+
if manager_response.status == 200
|
251
|
+
manager_data = JSON.parse(manager_response.body)
|
252
|
+
manager_uuid = manager_data["UUID"]
|
253
|
+
end
|
254
|
+
|
205
255
|
{
|
206
256
|
"name" => data["Name"],
|
207
257
|
"model" => data["Model"],
|
208
258
|
"manufacturer" => data["Manufacturer"],
|
209
259
|
"serial" => data["SerialNumber"],
|
210
260
|
"uuid" => data["UUID"],
|
261
|
+
"manager_uuid" => manager_uuid,
|
211
262
|
"bios_version" => data["BiosVersion"],
|
212
263
|
"power_state" => data["PowerState"],
|
213
264
|
"health" => data.dig("Status", "Health"),
|
@@ -254,6 +305,45 @@ module Supermicro
|
|
254
305
|
data = power_consumption
|
255
306
|
data["consumed_watts"] if data.is_a?(Hash)
|
256
307
|
end
|
308
|
+
|
309
|
+
# Get system health status
|
310
|
+
def system_health
|
311
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1")
|
312
|
+
|
313
|
+
if response.status == 200
|
314
|
+
begin
|
315
|
+
data = JSON.parse(response.body)
|
316
|
+
|
317
|
+
health = {
|
318
|
+
"rollup" => data.dig("Status", "HealthRollup") || data.dig("Status", "Health"),
|
319
|
+
"system" => data.dig("Status", "Health"),
|
320
|
+
"processor" => data.dig("ProcessorSummary", "Status", "Health"),
|
321
|
+
"memory" => data.dig("MemorySummary", "Status", "Health")
|
322
|
+
}
|
323
|
+
|
324
|
+
# Try to get storage health
|
325
|
+
storage_response = authenticated_request(:get, "/redfish/v1/Systems/1/Storage")
|
326
|
+
if storage_response.status == 200
|
327
|
+
storage_data = JSON.parse(storage_response.body)
|
328
|
+
if storage_data["Members"] && !storage_data["Members"].empty?
|
329
|
+
# Get first storage controller's health
|
330
|
+
storage_url = storage_data["Members"].first["@odata.id"]
|
331
|
+
storage_detail = authenticated_request(:get, storage_url)
|
332
|
+
if storage_detail.status == 200
|
333
|
+
storage_detail_data = JSON.parse(storage_detail.body)
|
334
|
+
health["storage"] = storage_detail_data.dig("Status", "Health")
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
health
|
340
|
+
rescue JSON::ParserError
|
341
|
+
raise Error, "Failed to parse system health information: #{response.body}"
|
342
|
+
end
|
343
|
+
else
|
344
|
+
raise Error, "Failed to get system health. Status code: #{response.status}"
|
345
|
+
end
|
346
|
+
end
|
257
347
|
|
258
348
|
def temperatures
|
259
349
|
response = authenticated_request(:get, "/redfish/v1/Chassis/1/Thermal")
|
@@ -5,81 +5,8 @@ require 'colorize'
|
|
5
5
|
|
6
6
|
module Supermicro
|
7
7
|
module SystemConfig
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
if response.status == 200
|
12
|
-
begin
|
13
|
-
data = JSON.parse(response.body)
|
14
|
-
|
15
|
-
{
|
16
|
-
"attributes" => data["Attributes"],
|
17
|
-
"attribute_registry" => data["AttributeRegistry"]
|
18
|
-
}
|
19
|
-
rescue JSON::ParserError
|
20
|
-
raise Error, "Failed to parse BIOS attributes response: #{response.body}"
|
21
|
-
end
|
22
|
-
else
|
23
|
-
raise Error, "Failed to get BIOS attributes. Status code: #{response.status}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def set_bios_attribute(attribute_name, value)
|
28
|
-
puts "Setting BIOS attribute #{attribute_name} to #{value}...".yellow
|
29
|
-
|
30
|
-
body = {
|
31
|
-
"Attributes" => {
|
32
|
-
attribute_name => value
|
33
|
-
}
|
34
|
-
}
|
35
|
-
|
36
|
-
response = authenticated_request(
|
37
|
-
:patch,
|
38
|
-
"/redfish/v1/Systems/1/Bios/Settings",
|
39
|
-
body: body.to_json,
|
40
|
-
headers: { 'Content-Type': 'application/json' }
|
41
|
-
)
|
42
|
-
|
43
|
-
if response.status.between?(200, 299)
|
44
|
-
puts "BIOS attribute set successfully. Changes will be applied on next reboot.".green
|
45
|
-
return true
|
46
|
-
else
|
47
|
-
raise Error, "Failed to set BIOS attribute: #{response.status} - #{response.body}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def pending_bios_settings
|
52
|
-
response = authenticated_request(:get, "/redfish/v1/Systems/1/Bios/Settings")
|
53
|
-
|
54
|
-
if response.status == 200
|
55
|
-
begin
|
56
|
-
data = JSON.parse(response.body)
|
57
|
-
data["Attributes"] || {}
|
58
|
-
rescue JSON::ParserError
|
59
|
-
raise Error, "Failed to parse pending BIOS settings response: #{response.body}"
|
60
|
-
end
|
61
|
-
else
|
62
|
-
{}
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def reset_bios_defaults
|
67
|
-
puts "Resetting BIOS to defaults...".yellow
|
68
|
-
|
69
|
-
response = authenticated_request(
|
70
|
-
:post,
|
71
|
-
"/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios",
|
72
|
-
body: {}.to_json,
|
73
|
-
headers: { 'Content-Type': 'application/json' }
|
74
|
-
)
|
75
|
-
|
76
|
-
if response.status.between?(200, 299)
|
77
|
-
puts "BIOS reset to defaults successfully. Changes will be applied on next reboot.".green
|
78
|
-
return true
|
79
|
-
else
|
80
|
-
raise Error, "Failed to reset BIOS: #{response.status} - #{response.body}"
|
81
|
-
end
|
82
|
-
end
|
8
|
+
# BIOS methods have been moved to the Bios module for better organization
|
9
|
+
# Use client.bios_attributes, client.set_bios_attribute, etc. instead
|
83
10
|
|
84
11
|
def manager_network_protocol
|
85
12
|
response = authenticated_request(:get, "/redfish/v1/Managers/1/NetworkProtocol")
|
data/lib/supermicro/tasks.rb
CHANGED
@@ -61,6 +61,37 @@ module Supermicro
|
|
61
61
|
sleep 1
|
62
62
|
next
|
63
63
|
end
|
64
|
+
elsif task_response.status == 404
|
65
|
+
# TaskMonitor endpoint not found - this is an error
|
66
|
+
spinner&.stop("Task endpoint not found", success: false)
|
67
|
+
debug "Task endpoint returned 404 - cannot monitor task", 1, :red
|
68
|
+
return { success: false, error: 'task_endpoint_not_found' }
|
69
|
+
elsif task_response.status == 400
|
70
|
+
# 400 can indicate task completion with error details in body
|
71
|
+
begin
|
72
|
+
if task_response.body && !task_response.body.empty?
|
73
|
+
task_info = JSON.parse(task_response.body)
|
74
|
+
|
75
|
+
# Check if this is a completed task with error
|
76
|
+
if task_info['TaskState'] == 'Exception'
|
77
|
+
spinner&.stop("Task failed: #{task_info['Message']}", success: false)
|
78
|
+
debug "✗ Task failed: #{task_info['Message']}", 1, :red
|
79
|
+
return { success: false, task: task_info, error: task_info['Message'] }
|
80
|
+
elsif task_info['TaskState'] == 'Completed'
|
81
|
+
spinner&.stop("Task completed", success: true)
|
82
|
+
debug "✓ Task completed", 2, :green
|
83
|
+
return { success: true, task: task_info }
|
84
|
+
else
|
85
|
+
# Unknown 400 response
|
86
|
+
debug "Unexpected 400 response with TaskState: #{task_info['TaskState']}", 2, :yellow
|
87
|
+
sleep 1
|
88
|
+
next
|
89
|
+
end
|
90
|
+
end
|
91
|
+
rescue JSON::ParserError
|
92
|
+
debug "400 response but couldn't parse body: #{task_response.body}", 1, :red
|
93
|
+
return { success: false, error: 'bad_request' }
|
94
|
+
end
|
64
95
|
else
|
65
96
|
debug "Unexpected task response: #{task_response.status}", 2, :yellow
|
66
97
|
sleep 1
|
@@ -121,7 +152,14 @@ module Supermicro
|
|
121
152
|
if !task_location && response.body && !response.body.empty?
|
122
153
|
begin
|
123
154
|
task_data = JSON.parse(response.body)
|
124
|
-
|
155
|
+
# Prefer @odata.id over TaskMonitor as TaskMonitor may return 404
|
156
|
+
task_location = task_data['@odata.id']
|
157
|
+
|
158
|
+
# Only use TaskMonitor if @odata.id is not available
|
159
|
+
if !task_location && task_data['TaskMonitor']
|
160
|
+
debug "Using TaskMonitor endpoint (may not be supported): #{task_data['TaskMonitor']}", 2, :yellow
|
161
|
+
task_location = task_data['TaskMonitor']
|
162
|
+
end
|
125
163
|
rescue JSON::ParserError
|
126
164
|
# No task info in body
|
127
165
|
end
|
data/lib/supermicro/utility.rb
CHANGED
@@ -6,44 +6,80 @@ require 'colorize'
|
|
6
6
|
module Supermicro
|
7
7
|
module Utility
|
8
8
|
def sel_log
|
9
|
-
|
9
|
+
# Supermicro uses Systems/1/LogServices/Log1/Entries for system health event logs
|
10
|
+
# Try Systems log first (health events)
|
11
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/1/LogServices/Log1/Entries?$expand=*($levels=1)")
|
10
12
|
|
11
13
|
if response.status == 200
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
}
|
26
|
-
end || []
|
27
|
-
|
28
|
-
return entries.sort_by { |e| e["created"] || "" }.reverse
|
29
|
-
rescue JSON::ParserError
|
30
|
-
raise Error, "Failed to parse SEL log response: #{response.body}"
|
14
|
+
debug "Retrieved system health event logs", 2, :green
|
15
|
+
return parse_log_entries(response)
|
16
|
+
elsif response.status == 404
|
17
|
+
# Fallback to Manager logs (maintenance events)
|
18
|
+
debug "Systems log not found, trying Manager logs", 2, :yellow
|
19
|
+
response = authenticated_request(:get, "/redfish/v1/Managers/1/LogServices/Log1/Entries?$expand=*($levels=1)")
|
20
|
+
|
21
|
+
if response.status == 200
|
22
|
+
debug "Retrieved manager maintenance logs", 2, :green
|
23
|
+
return parse_log_entries(response)
|
24
|
+
elsif response.status == 404
|
25
|
+
debug "No log services available on this system", 1, :yellow
|
26
|
+
return []
|
31
27
|
end
|
32
|
-
else
|
33
|
-
raise Error, "Failed to get SEL log. Status code: #{response.status}"
|
34
28
|
end
|
29
|
+
|
30
|
+
if response.status != 200
|
31
|
+
debug "Failed to get system logs. Status code: #{response.status}", 1, :yellow
|
32
|
+
return []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def parse_log_entries(response)
|
39
|
+
data = JSON.parse(response.body)
|
40
|
+
|
41
|
+
entries = data["Members"]&.map do |entry|
|
42
|
+
{
|
43
|
+
"id" => entry["Id"],
|
44
|
+
"name" => entry["Name"],
|
45
|
+
"created" => entry["Created"],
|
46
|
+
"severity" => entry["Severity"],
|
47
|
+
"message" => entry["Message"],
|
48
|
+
"message_id" => entry["MessageId"],
|
49
|
+
"sensor_type" => entry["SensorType"],
|
50
|
+
"sensor_number" => entry["SensorNumber"]
|
51
|
+
}
|
52
|
+
end || []
|
53
|
+
|
54
|
+
return entries.sort_by { |e| e["created"] || "" }.reverse
|
55
|
+
rescue JSON::ParserError
|
56
|
+
debug "Failed to parse log response", 1, :yellow
|
57
|
+
return []
|
35
58
|
end
|
59
|
+
|
60
|
+
public
|
36
61
|
|
37
62
|
def clear_sel_log
|
38
63
|
puts "Clearing System Event Log...".yellow
|
39
64
|
|
65
|
+
# Try to clear System health logs first
|
40
66
|
response = authenticated_request(
|
41
67
|
:post,
|
42
|
-
"/redfish/v1/
|
68
|
+
"/redfish/v1/Systems/1/LogServices/Log1/Actions/LogService.ClearLog",
|
43
69
|
body: {}.to_json,
|
44
70
|
headers: { 'Content-Type': 'application/json' }
|
45
71
|
)
|
46
72
|
|
73
|
+
if response.status == 404
|
74
|
+
# Fallback to Manager logs
|
75
|
+
response = authenticated_request(
|
76
|
+
:post,
|
77
|
+
"/redfish/v1/Managers/1/LogServices/Log1/Actions/LogService.ClearLog",
|
78
|
+
body: {}.to_json,
|
79
|
+
headers: { 'Content-Type': 'application/json' }
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
47
83
|
if response.status.between?(200, 299)
|
48
84
|
puts "SEL cleared successfully.".green
|
49
85
|
return true
|
data/lib/supermicro/version.rb
CHANGED
@@ -155,6 +155,7 @@ module Supermicro
|
|
155
155
|
|
156
156
|
tries = 0
|
157
157
|
max_tries = 3
|
158
|
+
last_error = nil
|
158
159
|
|
159
160
|
while tries < max_tries
|
160
161
|
begin
|
@@ -233,21 +234,26 @@ module Supermicro
|
|
233
234
|
inserted_media = current_media.find { |m| m[:device] == device }
|
234
235
|
if inserted_media && inserted_media[:image] == iso_url
|
235
236
|
if inserted_media[:connected_via] == "NotConnected"
|
236
|
-
|
237
|
-
debug "
|
238
|
-
|
237
|
+
error_msg = "Media mounted but NOT CONNECTED! The ISO will NOT boot! ConnectedVia must be 'URI', not 'NotConnected'."
|
238
|
+
debug "ERROR: #{error_msg}", 1, :red
|
239
|
+
raise Error, error_msg
|
239
240
|
else
|
240
241
|
debug "Media mounted with status: #{inserted_media[:connected_via]}", 1, :green
|
241
242
|
return true
|
242
243
|
end
|
243
244
|
else
|
244
|
-
|
245
|
-
|
245
|
+
error_msg = "Failed to verify media mount - media not found on device #{device}"
|
246
|
+
debug error_msg, 1, :red
|
247
|
+
raise Error, error_msg
|
246
248
|
end
|
247
249
|
end
|
248
250
|
else
|
249
251
|
debug "Task failed or timed out", 1, :red
|
250
|
-
|
252
|
+
last_error = task_result[:error] || "Task failed with unknown error"
|
253
|
+
debug "Task error details: #{last_error}", 1, :red
|
254
|
+
tries += 1
|
255
|
+
sleep 2
|
256
|
+
next
|
251
257
|
end
|
252
258
|
elsif response.status.between?(200, 299)
|
253
259
|
# Synchronous success (rare)
|
@@ -261,14 +267,16 @@ module Supermicro
|
|
261
267
|
debug "✓ Media connected via URI", 1, :green
|
262
268
|
return true
|
263
269
|
elsif inserted_media[:connected_via] == "NotConnected"
|
264
|
-
|
265
|
-
|
270
|
+
error_msg = "Media mounted but not connected! ConnectedVia is 'NotConnected' - ISO will not boot"
|
271
|
+
debug "ERROR: #{error_msg}", 1, :red
|
272
|
+
raise Error, error_msg
|
266
273
|
else
|
267
274
|
debug "Media mounted with status: #{inserted_media[:connected_via]}", 1, :yellow
|
268
275
|
return true
|
269
276
|
end
|
270
277
|
else
|
271
|
-
|
278
|
+
# If we can't verify, still treat as success but log warning
|
279
|
+
debug "WARNING: Could not verify media mount status", 1, :yellow
|
272
280
|
return true
|
273
281
|
end
|
274
282
|
elsif response.status == 400 && response.body.include?("already")
|
@@ -278,19 +286,54 @@ module Supermicro
|
|
278
286
|
tries += 1
|
279
287
|
else
|
280
288
|
debug "Failed to insert media: #{response.status} - #{response.body}", 1, :red
|
289
|
+
last_error = "HTTP #{response.status}: #{response.body}"
|
281
290
|
tries += 1
|
282
291
|
sleep 2
|
283
292
|
end
|
284
293
|
rescue => e
|
285
294
|
debug "Error inserting media: #{e.message}", 1, :red
|
295
|
+
last_error = e.message
|
286
296
|
tries += 1
|
287
297
|
sleep 2
|
288
298
|
end
|
289
299
|
end
|
290
300
|
|
291
|
-
|
301
|
+
error_msg = "Failed to insert virtual media after #{max_tries} attempts"
|
302
|
+
error_msg += ": #{last_error}" if last_error
|
303
|
+
raise Error, error_msg
|
292
304
|
end
|
293
305
|
|
306
|
+
def test_iso_accessibility(iso_url)
|
307
|
+
debug "Testing if BMC can reach ISO URL: #{iso_url}", 1, :yellow
|
308
|
+
|
309
|
+
# Try to use Redfish's built-in validation if available
|
310
|
+
path = "/redfish/v1/Managers/1/VirtualMedia/test"
|
311
|
+
body = {
|
312
|
+
"Image" => iso_url,
|
313
|
+
"TestOnly" => true
|
314
|
+
}
|
315
|
+
|
316
|
+
begin
|
317
|
+
response = authenticated_request(
|
318
|
+
:post,
|
319
|
+
path,
|
320
|
+
body: body.to_json,
|
321
|
+
headers: { 'Content-Type': 'application/json' }
|
322
|
+
)
|
323
|
+
|
324
|
+
if response.status == 200
|
325
|
+
debug "✓ BMC can reach ISO URL", 1, :green
|
326
|
+
return true
|
327
|
+
else
|
328
|
+
debug "✗ BMC cannot reach ISO URL: #{response.body}", 1, :red
|
329
|
+
return false
|
330
|
+
end
|
331
|
+
rescue => e
|
332
|
+
debug "Cannot test ISO accessibility: #{e.message}", 2, :yellow
|
333
|
+
return nil
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
294
337
|
def find_best_virtual_media_device
|
295
338
|
media_list = virtual_media
|
296
339
|
|
data/lib/supermicro.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: supermicro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Siegel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- LICENSE
|
148
148
|
- README.md
|
149
149
|
- lib/supermicro.rb
|
150
|
+
- lib/supermicro/bios.rb
|
150
151
|
- lib/supermicro/boot.rb
|
151
152
|
- lib/supermicro/client.rb
|
152
153
|
- lib/supermicro/error.rb
|