idrac 0.7.1 → 0.7.2
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/README.md +13 -0
- data/bin/idrac +374 -93
- data/lib/idrac/boot.rb +186 -79
- data/lib/idrac/client.rb +95 -0
- data/lib/idrac/jobs.rb +8 -4
- data/lib/idrac/license.rb +325 -39
- data/lib/idrac/lifecycle.rb +88 -2
- data/lib/idrac/power.rb +22 -63
- data/lib/idrac/storage.rb +275 -115
- data/lib/idrac/system.rb +227 -130
- data/lib/idrac/system_config.rb +163 -139
- data/lib/idrac/utility.rb +60 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/virtual_media.rb +19 -29
- data/lib/idrac.rb +2 -0
- metadata +3 -2
data/lib/idrac/license.rb
CHANGED
@@ -3,61 +3,347 @@ module IDRAC
|
|
3
3
|
# Gets the license information from the iDRAC
|
4
4
|
# @return [Hash] License details
|
5
5
|
def license_info
|
6
|
+
# Try the standard license endpoint first (works in iDRAC 9+)
|
6
7
|
response = authenticated_request(:get, "/redfish/v1/LicenseService/Licenses")
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
|
9
|
+
if response.status == 200
|
10
|
+
license_data = JSON.parse(response.body)
|
11
|
+
debug "License collection: #{license_data}", 2
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
# Check if there are any license entries
|
14
|
+
if !license_data["Members"] || license_data["Members"].empty?
|
15
|
+
debug "No licenses found", 1, :yellow
|
16
|
+
return try_dell_oem_license_path()
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
debug "
|
18
|
-
return nil
|
19
|
-
end
|
20
|
-
|
21
|
-
# Get the first license in the list
|
22
|
-
license_uri = license_data["Members"][0]["@odata.id"]
|
23
|
-
debug "Using license URI: #{license_uri}", 2
|
19
|
+
# Get the first license in the list
|
20
|
+
license_uri = license_data["Members"][0]["@odata.id"]
|
21
|
+
debug "Using license URI: #{license_uri}", 2
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
# Get detailed license information
|
24
|
+
license_response = authenticated_request(:get, license_uri)
|
25
|
+
if license_response.status != 200
|
26
|
+
debug "Failed to retrieve license details: #{license_response.status}", 1, :red
|
27
|
+
return try_dell_oem_license_path()
|
28
|
+
end
|
31
29
|
|
32
|
-
|
33
|
-
|
30
|
+
license_details = JSON.parse(license_response.body)
|
31
|
+
debug "License details: #{license_details}", 2
|
34
32
|
|
35
|
-
|
33
|
+
return license_details
|
34
|
+
else
|
35
|
+
# The endpoint is not available (probably iDRAC 8)
|
36
|
+
debug "Standard license endpoint failed: #{response.status}, trying Dell OEM path", 1, :yellow
|
37
|
+
return try_dell_oem_license_path()
|
38
|
+
end
|
36
39
|
end
|
37
40
|
|
38
|
-
# Extracts the iDRAC version from the license description
|
41
|
+
# Extracts the iDRAC version from the license description or server header
|
39
42
|
# @return [Integer, nil] The license version (e.g. 9) or nil if not found
|
40
43
|
def license_version
|
44
|
+
# First try to get from license info
|
41
45
|
license = license_info
|
42
|
-
|
46
|
+
if license
|
47
|
+
# Check the Description field, which often contains the version
|
48
|
+
# Example: "iDRAC9 Enterprise License"
|
49
|
+
if license["Description"]&.match(/iDRAC(\d+)/i)
|
50
|
+
version = license["Description"].match(/iDRAC(\d+)/i)[1].to_i
|
51
|
+
debug "Found license version from Description: #{version}", 1
|
52
|
+
return version
|
53
|
+
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
55
|
+
# Try alternative fields if Description didn't work
|
56
|
+
if license["Name"]&.match(/iDRAC(\d+)/i)
|
57
|
+
version = license["Name"].match(/iDRAC(\d+)/i)[1].to_i
|
58
|
+
debug "Found license version from Name: #{version}", 1
|
59
|
+
return version
|
60
|
+
end
|
61
|
+
|
62
|
+
# For Dell OEM license response format
|
63
|
+
if license["LicenseDescription"]&.match(/iDRAC(\d+)/i)
|
64
|
+
version = license["LicenseDescription"].match(/iDRAC(\d+)/i)[1].to_i
|
65
|
+
debug "Found license version from LicenseDescription: #{version}", 1
|
66
|
+
return version
|
67
|
+
end
|
50
68
|
end
|
51
|
-
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
69
|
+
|
70
|
+
# If license info failed or didn't have version info, try to get from server header
|
71
|
+
# Make a simple request to check the server header (often contains iDRAC version)
|
72
|
+
response = authenticated_request(:get, "/redfish/v1")
|
73
|
+
if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
|
74
|
+
version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
|
75
|
+
debug "Found license version from server header: #{version}", 1
|
56
76
|
return version
|
57
77
|
end
|
58
|
-
|
59
|
-
debug "Could not determine license version from license info", 1, :yellow
|
78
|
+
|
79
|
+
debug "Could not determine license version from license info or server header", 1, :yellow
|
60
80
|
nil
|
61
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Attempt to get license information using Dell OEM extension path (for iDRAC 8)
|
86
|
+
# @return [Hash, nil] License info or nil if not found
|
87
|
+
def try_dell_oem_license_path
|
88
|
+
# Try several potential Dell license paths (order matters - most likely first)
|
89
|
+
dell_license_paths = [
|
90
|
+
"/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLicenseManagementService/Licenses",
|
91
|
+
"/redfish/v1/Managers/iDRAC.Embedded.1/Attributes", # iDRAC attributes might contain license info
|
92
|
+
"/redfish/v1/Managers/iDRAC.Embedded.1" # Manager entity might have license info embedded
|
93
|
+
]
|
94
|
+
|
95
|
+
dell_license_paths.each do |path|
|
96
|
+
response = authenticated_request(:get, path)
|
97
|
+
|
98
|
+
if response.status == 200
|
99
|
+
debug "Found valid Dell license path: #{path}", 2
|
100
|
+
data = JSON.parse(response.body)
|
101
|
+
|
102
|
+
# Check for license info in this response based on the path
|
103
|
+
if path.include?("DellLicenseManagementService")
|
104
|
+
return handle_dell_license_service_response(data)
|
105
|
+
elsif path.include?("Attributes")
|
106
|
+
return handle_dell_attributes_response(data)
|
107
|
+
elsif path.include?("iDRAC.Embedded.1") && !path.include?("Attributes")
|
108
|
+
return handle_dell_manager_response(data)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
debug "Dell path #{path} response status: #{response.status}", 3
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# If we couldn't find any API path that works, try the service tag detection method
|
116
|
+
service_tag = get_service_tag
|
117
|
+
if service_tag
|
118
|
+
# Service tag is often used to indicate Enterprise licenses on Dell systems
|
119
|
+
license_type = determine_license_type()
|
120
|
+
return {
|
121
|
+
"Id" => "iDRAC-License",
|
122
|
+
"Description" => "iDRAC8 #{license_type} License",
|
123
|
+
"Name" => "iDRAC License",
|
124
|
+
"LicenseType" => license_type,
|
125
|
+
"Status" => { "Health" => "OK" },
|
126
|
+
"Removable" => false,
|
127
|
+
"EntitlementID" => service_tag # Dell often uses service tag as entitlement ID
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Fall back to feature detection if all else fails
|
132
|
+
debug "All Dell OEM license paths failed, using fallback detection", 1, :yellow
|
133
|
+
return create_fallback_license_info()
|
134
|
+
end
|
135
|
+
|
136
|
+
# Handle response from Dell License Management Service
|
137
|
+
def handle_dell_license_service_response(data)
|
138
|
+
# Check if there are any license entries
|
139
|
+
if !data["Members"] || data["Members"].empty?
|
140
|
+
debug "No licenses found in Dell OEM path", 1, :yellow
|
141
|
+
return create_fallback_license_info(use_basic: true)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the first license in the list
|
145
|
+
license_uri = data["Members"][0]["@odata.id"]
|
146
|
+
debug "Using Dell OEM license URI: #{license_uri}", 2
|
147
|
+
|
148
|
+
# Get detailed license information
|
149
|
+
license_response = authenticated_request(:get, license_uri)
|
150
|
+
if license_response.status != 200
|
151
|
+
debug "Failed to retrieve Dell OEM license details: #{license_response.status}", 1, :red
|
152
|
+
return create_fallback_license_info(use_basic: true)
|
153
|
+
end
|
154
|
+
|
155
|
+
dell_license = JSON.parse(license_response.body)
|
156
|
+
debug "Dell OEM license details: #{dell_license}", 2
|
157
|
+
|
158
|
+
# Convert Dell OEM license format to standard format
|
159
|
+
license_info = {
|
160
|
+
"Id" => dell_license["EntitlementID"] || "iDRAC-License",
|
161
|
+
"Description" => dell_license["LicenseDescription"] || "iDRAC License",
|
162
|
+
"Name" => dell_license["LicenseDescription"] || "iDRAC License",
|
163
|
+
"LicenseType" => dell_license["LicenseType"] || get_license_type_from_description(dell_license["LicenseDescription"]),
|
164
|
+
"Status" => { "Health" => "OK" },
|
165
|
+
"Removable" => true
|
166
|
+
}
|
167
|
+
|
168
|
+
return license_info
|
169
|
+
end
|
170
|
+
|
171
|
+
# Handle response from Dell Manager attributes
|
172
|
+
def handle_dell_attributes_response(data)
|
173
|
+
# Look for license information in attributes
|
174
|
+
if data["Attributes"] && (
|
175
|
+
data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] ||
|
176
|
+
data["Attributes"]["System.ServerOS.1.OSName"] ||
|
177
|
+
data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
|
178
|
+
)
|
179
|
+
|
180
|
+
license_info = data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] ||
|
181
|
+
data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
|
182
|
+
|
183
|
+
if license_info
|
184
|
+
license_type = license_info.include?("Enterprise") ? "Enterprise" :
|
185
|
+
license_info.include?("Express") ? "Express" : "Basic"
|
186
|
+
|
187
|
+
return {
|
188
|
+
"Id" => "iDRAC-License",
|
189
|
+
"Description" => "iDRAC8 #{license_type} License",
|
190
|
+
"Name" => "iDRAC License",
|
191
|
+
"LicenseType" => license_type,
|
192
|
+
"Status" => { "Health" => "OK" },
|
193
|
+
"Removable" => false
|
194
|
+
}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# If no license attributes found, fall back to feature detection
|
199
|
+
license_type = determine_license_type()
|
200
|
+
return {
|
201
|
+
"Id" => "iDRAC-License",
|
202
|
+
"Description" => "iDRAC8 #{license_type} License",
|
203
|
+
"Name" => "iDRAC License",
|
204
|
+
"LicenseType" => license_type,
|
205
|
+
"Status" => { "Health" => "OK" },
|
206
|
+
"Removable" => false
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
# Handle response from Dell Manager entity
|
211
|
+
def handle_dell_manager_response(data)
|
212
|
+
# Look for license information in Oem data
|
213
|
+
if data["Oem"] && data["Oem"]["Dell"]
|
214
|
+
dell_data = data["Oem"]["Dell"]
|
215
|
+
|
216
|
+
if dell_data["DellLicenseManagementService"]
|
217
|
+
# Found license service reference, but need to query it directly
|
218
|
+
service_uri = dell_data["DellLicenseManagementService"]["@odata.id"]
|
219
|
+
debug "Found license service URI: #{service_uri}", 2
|
220
|
+
|
221
|
+
service_response = authenticated_request(:get, service_uri)
|
222
|
+
if service_response.status == 200
|
223
|
+
return handle_dell_license_service_response(JSON.parse(service_response.body))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Check if license info is embedded directly
|
228
|
+
if dell_data["LicenseType"] || dell_data["License"]
|
229
|
+
license_type = dell_data["LicenseType"] ||
|
230
|
+
(dell_data["License"] && dell_data["License"].include?("Enterprise") ? "Enterprise" :
|
231
|
+
dell_data["License"].include?("Express") ? "Express" : "Basic")
|
232
|
+
|
233
|
+
return {
|
234
|
+
"Id" => "iDRAC-License",
|
235
|
+
"Description" => "iDRAC8 #{license_type} License",
|
236
|
+
"Name" => "iDRAC License",
|
237
|
+
"LicenseType" => license_type,
|
238
|
+
"Status" => { "Health" => "OK" },
|
239
|
+
"Removable" => false
|
240
|
+
}
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Check for license type in model name or description
|
245
|
+
if data["Model"] && data["Model"].include?("Enterprise")
|
246
|
+
return {
|
247
|
+
"Id" => "iDRAC-License",
|
248
|
+
"Description" => "iDRAC8 Enterprise License",
|
249
|
+
"Name" => "iDRAC License",
|
250
|
+
"LicenseType" => "Enterprise",
|
251
|
+
"Status" => { "Health" => "OK" },
|
252
|
+
"Removable" => false
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
# If no license info found in manager data, fall back to feature detection
|
257
|
+
license_type = determine_license_type()
|
258
|
+
return {
|
259
|
+
"Id" => "iDRAC-License",
|
260
|
+
"Description" => "iDRAC8 #{license_type} License",
|
261
|
+
"Name" => "iDRAC License",
|
262
|
+
"LicenseType" => license_type,
|
263
|
+
"Status" => { "Health" => "OK" },
|
264
|
+
"Removable" => false
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
# Get service tag from system info
|
269
|
+
def get_service_tag
|
270
|
+
response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
|
271
|
+
if response.status == 200
|
272
|
+
data = JSON.parse(response.body)
|
273
|
+
return data["SKU"] if data["SKU"] # Service tag is usually in SKU field
|
274
|
+
end
|
275
|
+
|
276
|
+
# Try alternate location
|
277
|
+
response = authenticated_request(:get, "/redfish/v1")
|
278
|
+
if response.status == 200
|
279
|
+
data = JSON.parse(response.body)
|
280
|
+
if data["Oem"] && data["Oem"]["Dell"] && data["Oem"]["Dell"]["ServiceTag"]
|
281
|
+
return data["Oem"]["Dell"]["ServiceTag"]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
nil
|
286
|
+
end
|
287
|
+
|
288
|
+
# Helper method to extract license type from description
|
289
|
+
def get_license_type_from_description(description)
|
290
|
+
return "Unknown" unless description
|
291
|
+
|
292
|
+
if description.include?("Enterprise")
|
293
|
+
return "Enterprise"
|
294
|
+
elsif description.include?("Express")
|
295
|
+
return "Express"
|
296
|
+
elsif description.include?("Datacenter")
|
297
|
+
return "Datacenter"
|
298
|
+
else
|
299
|
+
return "Basic"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Creates a basic license info object based on system information
|
304
|
+
# Used as a fallback when neither the standard nor Dell OEM endpoints work
|
305
|
+
# @return [Hash] A basic license info object
|
306
|
+
def create_fallback_license_info(use_basic: false)
|
307
|
+
# Get the iDRAC version number from server headers
|
308
|
+
version = nil
|
309
|
+
response = authenticated_request(:get, "/redfish/v1")
|
310
|
+
if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
|
311
|
+
version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
|
312
|
+
end
|
313
|
+
|
314
|
+
# Try to determine if it's Enterprise or Express based on available features
|
315
|
+
license_type = use_basic ? "Basic" : determine_license_type
|
316
|
+
|
317
|
+
license_info = {
|
318
|
+
"Id" => "iDRAC-License",
|
319
|
+
"Description" => version ? "iDRAC#{version} #{license_type} License" : "iDRAC License",
|
320
|
+
"Name" => "iDRAC License",
|
321
|
+
"LicenseType" => license_type,
|
322
|
+
"Status" => { "Health" => "OK" },
|
323
|
+
"Removable" => false
|
324
|
+
}
|
325
|
+
|
326
|
+
debug "Created fallback license info: #{license_info}", 2
|
327
|
+
license_info
|
328
|
+
end
|
329
|
+
|
330
|
+
# Attempt to determine the license type (Enterprise/Express) based on available features
|
331
|
+
# @return [String] The license type (Enterprise, Express, or Basic)
|
332
|
+
def determine_license_type
|
333
|
+
# We can try to check for features only available in Enterprise
|
334
|
+
begin
|
335
|
+
# For example, virtual media is typically an Enterprise feature
|
336
|
+
virtual_media_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/VirtualMedia")
|
337
|
+
# If we successfully get virtual media, it's likely Enterprise
|
338
|
+
if virtual_media_response.status == 200
|
339
|
+
return "Enterprise"
|
340
|
+
end
|
341
|
+
rescue
|
342
|
+
# If the request fails, don't fail the whole method
|
343
|
+
end
|
344
|
+
|
345
|
+
# Default to basic license if we can't determine
|
346
|
+
return "Express"
|
347
|
+
end
|
62
348
|
end
|
63
349
|
end
|
data/lib/idrac/lifecycle.rb
CHANGED
@@ -3,8 +3,94 @@ require 'colorize'
|
|
3
3
|
|
4
4
|
module IDRAC
|
5
5
|
module Lifecycle
|
6
|
-
#
|
6
|
+
# This follows from these Scripts "GetIdracLcSystemAttributesREDFISH.py" and "SetIdracLcSystemAttributesREDFISH.py"
|
7
|
+
# They can do more than just the lifecycle, but that's what we need right now.
|
8
|
+
# True or False if it's enabled or not
|
7
9
|
def get_lifecycle_status
|
10
|
+
# Check iDRAC version first to determine the right approach
|
11
|
+
idrac_version = get_idrac_version rescue 0
|
12
|
+
|
13
|
+
debug "Detected iDRAC version: #{idrac_version}", 1
|
14
|
+
|
15
|
+
# Use version-specific methods
|
16
|
+
if idrac_version > 9
|
17
|
+
debug "Using modern approach for iDRAC > 9", 1
|
18
|
+
return get_lifecycle_status_modern_firmware
|
19
|
+
elsif idrac_version == 9
|
20
|
+
debug "Using registry approach for iDRAC 9", 1
|
21
|
+
return get_lifecycle_status_from_registry
|
22
|
+
else
|
23
|
+
debug "Using SCP approach for older iDRAC (v#{idrac_version})", 1
|
24
|
+
return get_lifecycle_status_from_scp
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get lifecycle status from SCP export (for older iDRAC firmware)
|
29
|
+
def get_lifecycle_status_from_scp
|
30
|
+
debug "Exporting System Configuration Profile to check LifecycleController state...", 1
|
31
|
+
|
32
|
+
begin
|
33
|
+
# Use the SCP export to get LifecycleController state
|
34
|
+
scp = get_system_configuration_profile(target: "LifecycleController")
|
35
|
+
|
36
|
+
# Check if we have data in the expected format
|
37
|
+
if scp && scp["SystemConfiguration"] && scp["SystemConfiguration"]["Components"]
|
38
|
+
# Find the LifecycleController component
|
39
|
+
lc_component = scp["SystemConfiguration"]["Components"].find do |component|
|
40
|
+
component["FQDD"] == "LifecycleController.Embedded.1"
|
41
|
+
end
|
42
|
+
|
43
|
+
if lc_component && lc_component["Attributes"]
|
44
|
+
# Find the LifecycleControllerState attribute
|
45
|
+
lc_state_attr = lc_component["Attributes"].find do |attr|
|
46
|
+
attr["Name"] == "LCAttributes.1#LifecycleControllerState"
|
47
|
+
end
|
48
|
+
|
49
|
+
if lc_state_attr
|
50
|
+
debug "Found LifecycleController state from SCP: #{lc_state_attr["Value"]}", 1
|
51
|
+
return lc_state_attr["Value"] == "Enabled"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
debug "Could not find LifecycleController state in SCP export", 1, :yellow
|
57
|
+
return false
|
58
|
+
rescue => e
|
59
|
+
debug "Error getting Lifecycle Controller status from SCP: #{e.message}", 1, :red
|
60
|
+
debug e.backtrace.join("\n"), 3, :red
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get lifecycle status from registry (for iDRAC 9)
|
66
|
+
def get_lifecycle_status_from_registry
|
67
|
+
# This big JSON explains all the attributes:
|
68
|
+
path = "/redfish/v1/Registries/ManagerAttributeRegistry/ManagerAttributeRegistry.v1_0_0.json"
|
69
|
+
response = authenticated_request(:get, path)
|
70
|
+
if response.status != 200
|
71
|
+
debug "Failed to get any Lifecycle Controller Attributes".red, 1
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
attributes = JSON.parse(response.body)
|
75
|
+
# This is the attribute we want:
|
76
|
+
target = attributes&.dig('RegistryEntries', 'Attributes')&.find {|q| q['AttributeName'] =~ /LCAttributes.1.LifecycleControllerState/ }
|
77
|
+
# This is the FQDN of the attribute we want to get the value of:
|
78
|
+
fqdn = target.dig('Id') # LifecycleController.Embedded.1#LCAttributes.1#LifecycleControllerState
|
79
|
+
# This is the Current Value:
|
80
|
+
response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/#{fqdn}")
|
81
|
+
if response.status != 200
|
82
|
+
debug "Failed to get Lifecycle Controller Attributes".red, 1
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
attributes = JSON.parse(response.body)
|
86
|
+
# There is a ValueName and a Value Display Name (e.g. Enabled, Disabled, Recovery)
|
87
|
+
display = attributes&.dig('Attributes','LCAttributes.1.LifecycleControllerState')
|
88
|
+
value = target&.dig('Value')&.find { |v| v['ValueDisplayName'] == display }&.dig('ValueName')&.to_i
|
89
|
+
value == 1
|
90
|
+
end
|
91
|
+
|
92
|
+
# Check if the Lifecycle Controller is enabled
|
93
|
+
def get_lifecycle_status_modern_firmware
|
8
94
|
# Try the standard Attributes endpoint first
|
9
95
|
path = "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes"
|
10
96
|
response = authenticated_request(:get, path)
|
@@ -201,4 +287,4 @@ module IDRAC
|
|
201
287
|
debug "Lifecycle Controller is now #{status ? 'Enabled' : 'Disabled'}".green, 1
|
202
288
|
end
|
203
289
|
end
|
204
|
-
end
|
290
|
+
end
|
data/lib/idrac/power.rb
CHANGED
@@ -78,32 +78,10 @@ module IDRAC
|
|
78
78
|
|
79
79
|
response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
puts "Server power off command sent successfully".green
|
84
|
-
when 409
|
85
|
-
# Conflict -- Server is already off
|
86
|
-
begin
|
87
|
-
error_data = JSON.parse(response.body)
|
88
|
-
if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] &&
|
89
|
-
error_data["error"]["@Message.ExtendedInfo"].any? { |m| m["Message"] =~ /Server is already powered OFF/ }
|
90
|
-
puts "Server is already powered OFF.".yellow
|
91
|
-
return false
|
92
|
-
else
|
93
|
-
raise Error, "Failed to power off: #{error_data.inspect}"
|
94
|
-
end
|
95
|
-
rescue JSON::ParserError
|
96
|
-
raise Error, "Failed to power off with status 409: #{response.body}"
|
97
|
-
end
|
81
|
+
if response.status == 409
|
82
|
+
puts "Server is already powered OFF.".yellow
|
98
83
|
else
|
99
|
-
|
100
|
-
begin
|
101
|
-
error_data = JSON.parse(response.body)
|
102
|
-
error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
|
103
|
-
rescue
|
104
|
-
# Ignore JSON parsing errors
|
105
|
-
end
|
106
|
-
raise Error, error_message
|
84
|
+
handle_response(response)
|
107
85
|
end
|
108
86
|
|
109
87
|
# Wait for power state change if requested
|
@@ -124,6 +102,13 @@ module IDRAC
|
|
124
102
|
login unless @session_id
|
125
103
|
|
126
104
|
puts "Rebooting server...".light_cyan
|
105
|
+
|
106
|
+
# Check current power state first
|
107
|
+
current_state = get_power_state rescue "Unknown"
|
108
|
+
if current_state == "Off"
|
109
|
+
puts "Server is currently off, powering on instead of rebooting".yellow
|
110
|
+
return power_on
|
111
|
+
end
|
127
112
|
|
128
113
|
# Send reboot command (Reset with ResetType=ForceRestart)
|
129
114
|
path = "/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
|
@@ -134,16 +119,17 @@ module IDRAC
|
|
134
119
|
if response.status >= 200 && response.status < 300
|
135
120
|
puts "Server reboot command sent successfully".green
|
136
121
|
return true
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
end
|
122
|
+
elsif response.status == 409
|
123
|
+
error_data = JSON.parse(response.body) rescue nil
|
124
|
+
puts "Received conflict (409) error from iDRAC: #{error_data.inspect}"
|
125
|
+
# Try gracefulRestart as an alternative
|
126
|
+
puts "Trying GracefulRestart instead...".yellow
|
127
|
+
payload = { "ResetType" => "GracefulRestart" }
|
128
|
+
response = authenticated_request(:post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
145
129
|
|
146
|
-
|
130
|
+
handle_response(response)
|
131
|
+
else
|
132
|
+
raise Error, "Failed to reboot server. Status code: #{response.status}"
|
147
133
|
end
|
148
134
|
end
|
149
135
|
|
@@ -154,16 +140,7 @@ module IDRAC
|
|
154
140
|
# Get system information
|
155
141
|
response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1?$select=PowerState")
|
156
142
|
|
157
|
-
|
158
|
-
begin
|
159
|
-
system_data = JSON.parse(response.body)
|
160
|
-
return system_data["PowerState"]
|
161
|
-
rescue JSON::ParserError
|
162
|
-
raise Error, "Failed to parse power state response: #{response.body}"
|
163
|
-
end
|
164
|
-
else
|
165
|
-
raise Error, "Failed to get power state. Status code: #{response.status}"
|
166
|
-
end
|
143
|
+
JSON.parse(handle_response(response))&.dig("PowerState")
|
167
144
|
end
|
168
145
|
|
169
146
|
def get_power_usage_watts
|
@@ -172,25 +149,7 @@ module IDRAC
|
|
172
149
|
|
173
150
|
response = authenticated_request(:get, "/redfish/v1/Chassis/System.Embedded.1/Power")
|
174
151
|
|
175
|
-
|
176
|
-
begin
|
177
|
-
data = JSON.parse(response.body)
|
178
|
-
watts = data["PowerControl"][0]["PowerConsumedWatts"]
|
179
|
-
# puts "Power usage: #{watts} watts".light_cyan
|
180
|
-
return watts.to_f
|
181
|
-
rescue JSON::ParserError
|
182
|
-
raise Error, "Failed to parse power usage response: #{response.body}"
|
183
|
-
end
|
184
|
-
else
|
185
|
-
error_message = "Failed to get power usage. Status code: #{response.status}"
|
186
|
-
begin
|
187
|
-
error_data = JSON.parse(response.body)
|
188
|
-
error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message']
|
189
|
-
rescue
|
190
|
-
# Ignore JSON parsing errors
|
191
|
-
end
|
192
|
-
raise Error, error_message
|
193
|
-
end
|
152
|
+
JSON.parse(handle_response(response))&.dig("PowerControl", 0, "PowerConsumedWatts")&.to_f
|
194
153
|
end
|
195
154
|
|
196
155
|
private
|