idrac 0.1.91 → 0.3.1
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/bin/idrac +754 -4
- data/lib/idrac/boot.rb +357 -0
- data/lib/idrac/client.rb +167 -72
- data/lib/idrac/core_ext.rb +100 -0
- data/lib/idrac/lifecycle.rb +86 -189
- data/lib/idrac/session.rb +134 -85
- data/lib/idrac/storage.rb +374 -0
- data/lib/idrac/system.rb +383 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/virtual_media.rb +285 -0
- data/lib/idrac.rb +16 -11
- metadata +7 -2
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Add ActiveSupport-like blank? method to core Ruby classes
|
4
|
+
# This allows us to use blank? without requiring Rails' ActiveSupport
|
5
|
+
|
6
|
+
class NilClass
|
7
|
+
# nil is always blank
|
8
|
+
def blank?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class String
|
14
|
+
# A string is blank if it's empty or contains whitespace only
|
15
|
+
def blank?
|
16
|
+
strip.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Array
|
21
|
+
# An array is blank if it's empty
|
22
|
+
def blank?
|
23
|
+
empty?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Hash
|
28
|
+
# A hash is blank if it's empty
|
29
|
+
def blank?
|
30
|
+
empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Object
|
35
|
+
# An object is blank if it responds to empty? and is empty
|
36
|
+
# Otherwise return false
|
37
|
+
def blank?
|
38
|
+
respond_to?(:empty?) ? empty? : false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Add ActiveSupport-like numeric extensions
|
43
|
+
class Integer
|
44
|
+
# Byte size helpers
|
45
|
+
def byte
|
46
|
+
self
|
47
|
+
end
|
48
|
+
alias_method :bytes, :byte
|
49
|
+
|
50
|
+
def kilobyte
|
51
|
+
self * 1024
|
52
|
+
end
|
53
|
+
alias_method :kilobytes, :kilobyte
|
54
|
+
|
55
|
+
def megabyte
|
56
|
+
self * 1024 * 1024
|
57
|
+
end
|
58
|
+
alias_method :megabytes, :megabyte
|
59
|
+
|
60
|
+
def gigabyte
|
61
|
+
self * 1024 * 1024 * 1024
|
62
|
+
end
|
63
|
+
alias_method :gigabytes, :gigabyte
|
64
|
+
|
65
|
+
def terabyte
|
66
|
+
self * 1024 * 1024 * 1024 * 1024
|
67
|
+
end
|
68
|
+
alias_method :terabytes, :terabyte
|
69
|
+
|
70
|
+
def petabyte
|
71
|
+
self * 1024 * 1024 * 1024 * 1024 * 1024
|
72
|
+
end
|
73
|
+
alias_method :petabytes, :petabyte
|
74
|
+
|
75
|
+
# Time duration helpers (for potential future use)
|
76
|
+
def second
|
77
|
+
self
|
78
|
+
end
|
79
|
+
alias_method :seconds, :second
|
80
|
+
|
81
|
+
def minute
|
82
|
+
self * 60
|
83
|
+
end
|
84
|
+
alias_method :minutes, :minute
|
85
|
+
|
86
|
+
def hour
|
87
|
+
self * 60 * 60
|
88
|
+
end
|
89
|
+
alias_method :hours, :hour
|
90
|
+
|
91
|
+
def day
|
92
|
+
self * 24 * 60 * 60
|
93
|
+
end
|
94
|
+
alias_method :days, :day
|
95
|
+
|
96
|
+
def week
|
97
|
+
self * 7 * 24 * 60 * 60
|
98
|
+
end
|
99
|
+
alias_method :weeks, :week
|
100
|
+
end
|
data/lib/idrac/lifecycle.rb
CHANGED
@@ -3,221 +3,113 @@ require 'colorize'
|
|
3
3
|
|
4
4
|
module IDRAC
|
5
5
|
module LifecycleMethods
|
6
|
-
#
|
6
|
+
# Check if the Lifecycle Controller is enabled
|
7
7
|
def get_lifecycle_status
|
8
|
-
# Try
|
9
|
-
|
8
|
+
# Try the standard Attributes endpoint first
|
9
|
+
path = "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes"
|
10
|
+
response = authenticated_request(:get, path)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
if response.status.between?(200, 299)
|
20
|
-
begin
|
21
|
-
lc_data = JSON.parse(response.body)
|
22
|
-
puts "LC Status: #{lc_data['LCStatus']}".light_cyan
|
23
|
-
return lc_data
|
24
|
-
rescue JSON::ParserError
|
25
|
-
# Fall through to alternative method
|
12
|
+
if response.status == 200
|
13
|
+
begin
|
14
|
+
attributes_data = JSON.parse(response.body)
|
15
|
+
if attributes_data["Attributes"] && attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
16
|
+
lifecycle_state = attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
17
|
+
debug "Lifecycle Controller state (from Attributes): #{lifecycle_state}".light_cyan, 1
|
18
|
+
return lifecycle_state == "Enabled"
|
26
19
|
end
|
20
|
+
rescue JSON::ParserError
|
21
|
+
debug "Failed to parse Attributes response".yellow, 1
|
22
|
+
# Fall through to registry method if parsing fails or attribute not found
|
27
23
|
end
|
28
|
-
|
29
|
-
|
24
|
+
else
|
25
|
+
debug "Failed to get Attributes endpoint (Status: #{response.status}), trying registry method...".yellow, 1
|
30
26
|
end
|
31
27
|
|
32
|
-
# Try
|
33
|
-
|
28
|
+
# Try getting the DellAttributes for LifecycleController directly
|
29
|
+
# The key insight is that we need to use just the base path without the fragment
|
30
|
+
attributes_path = "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/LifecycleController.Embedded.1"
|
31
|
+
attributes_response = authenticated_request(:get, attributes_path)
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
if attributes_data["Attributes"] && attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
43
|
-
lifecycle_state = attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
44
|
-
puts "Lifecycle Controller state: #{lifecycle_state}".light_cyan
|
45
|
-
return { "LCStatus" => lifecycle_state }
|
46
|
-
end
|
47
|
-
rescue JSON::ParserError
|
48
|
-
# Fall through to final error
|
49
|
-
end
|
50
|
-
end
|
51
|
-
rescue => e
|
52
|
-
# Fall through to final error
|
53
|
-
end
|
54
|
-
|
55
|
-
# If we get here, try one last approach - try to get iDRAC status
|
56
|
-
begin
|
57
|
-
response = authenticated_request(:get, '/redfish/v1/Managers/iDRAC.Embedded.1')
|
58
|
-
|
59
|
-
if response.status.between?(200, 299)
|
60
|
-
begin
|
61
|
-
data = JSON.parse(response.body)
|
62
|
-
status = data["Status"] && data["Status"]["State"]
|
63
|
-
if status
|
64
|
-
puts "iDRAC State: #{status}".light_cyan
|
65
|
-
puts "Note: Could not retrieve direct LC status, showing iDRAC status instead".yellow
|
66
|
-
return { "iDRACStatus" => status }
|
67
|
-
end
|
68
|
-
rescue JSON::ParserError
|
69
|
-
# Fall through to final error
|
33
|
+
if attributes_response.status == 200
|
34
|
+
begin
|
35
|
+
dell_attr_data = JSON.parse(attributes_response.body)
|
36
|
+
if dell_attr_data["Attributes"] && dell_attr_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
37
|
+
lifecycle_state = dell_attr_data["Attributes"]["LCAttributes.1.LifecycleControllerState"]
|
38
|
+
debug "Lifecycle Controller state (from Dell Attributes): #{lifecycle_state}".light_cyan, 1
|
39
|
+
return lifecycle_state == "Enabled"
|
70
40
|
end
|
41
|
+
rescue JSON::ParserError
|
42
|
+
debug "Failed to parse Dell Attributes response".yellow, 1
|
43
|
+
# Fall through to registry method if parsing fails or attribute not found
|
71
44
|
end
|
72
|
-
|
73
|
-
#
|
45
|
+
else
|
46
|
+
debug "Failed to get Dell Attributes (Status: #{attributes_response.status}), trying registry method...".yellow, 1
|
74
47
|
end
|
75
|
-
|
76
|
-
# If we reached here, all methods failed
|
77
|
-
puts "Unable to retrieve Lifecycle Controller status through any available method".red
|
78
|
-
raise Error, "Failed to get Lifecycle Controller status through any available method"
|
79
|
-
end
|
80
|
-
|
81
|
-
# Check if the Lifecycle Controller is enabled
|
82
|
-
def get_idrac_lifecycle_status
|
83
|
-
# Use the DellLCService GetRemoteServicesAPIStatus
|
84
|
-
path = '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus'
|
85
48
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
headers: { 'Content-Type' => 'application/json' }
|
49
|
+
# Fallback to the registry method if both Attributes endpoints fail
|
50
|
+
registry_response = authenticated_request(
|
51
|
+
:get,
|
52
|
+
"/redfish/v1/Registries/ManagerAttributeRegistry/ManagerAttributeRegistry.v1_0_0.json"
|
91
53
|
)
|
92
54
|
|
93
|
-
if
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
begin
|
106
|
-
attributes_data = JSON.parse(attributes_response.body)
|
107
|
-
lc_replication = attributes_data["Attributes"]["ServiceModule.1.LCLReplication"]
|
108
|
-
|
109
|
-
debug "ServiceModule.1.LCLReplication: #{lc_replication}", 1
|
110
|
-
|
111
|
-
is_enabled = lc_replication == "Enabled"
|
112
|
-
|
113
|
-
puts "Lifecycle Controller replication is #{is_enabled ? 'enabled' : 'disabled'}".light_cyan
|
114
|
-
puts "Lifecycle Controller status: #{status}".light_cyan
|
115
|
-
return is_enabled
|
116
|
-
rescue => e
|
117
|
-
debug "Error parsing attributes: #{e.message}", 1
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# If we can't determine from attributes, just return if LC is Ready
|
122
|
-
is_ready = status == "Ready"
|
123
|
-
puts "Lifecycle Controller is #{is_ready ? 'Ready' : status}".light_cyan
|
124
|
-
return is_ready
|
125
|
-
rescue JSON::ParserError
|
126
|
-
raise Error, "Failed to parse Lifecycle Controller status response: #{response.body}"
|
55
|
+
if registry_response.status != 200
|
56
|
+
debug "Failed to get Lifecycle Controller Attributes Registry", 0, :red
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
registry_data = JSON.parse(registry_response.body)
|
62
|
+
# This is the attribute we want:
|
63
|
+
target = registry_data['RegistryEntries']['Attributes'].find {|q| q['AttributeName'] =~ /LCAttributes.1.LifecycleControllerState/ }
|
64
|
+
if !target
|
65
|
+
debug "Could not find LCAttributes.1.LifecycleControllerState in registry", 0, :red
|
66
|
+
return false
|
127
67
|
end
|
128
|
-
|
129
|
-
|
68
|
+
|
69
|
+
debug "Found attribute in registry but couldn't access it via other endpoints".yellow, 1
|
70
|
+
return false
|
71
|
+
rescue JSON::ParserError, NoMethodError, StandardError => e
|
72
|
+
debug "Error during registry access: #{e.message}", 0, :red
|
73
|
+
return false
|
130
74
|
end
|
131
75
|
end
|
132
76
|
|
133
77
|
# Set the Lifecycle Controller status (enable/disable)
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# Create the payload with the attribute we want to modify
|
143
|
-
payload = {
|
144
|
-
"Attributes": {
|
145
|
-
"ServiceModule.1.LCLReplication": enabled ? "Enabled" : "Disabled"
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
debug "Using attributes endpoint: #{path}", 1
|
150
|
-
debug "Payload: #{payload.inspect}", 1
|
78
|
+
def set_lifecycle_status(status)
|
79
|
+
payload = { "Attributes": { "LCAttributes.1.LifecycleControllerState": status ? 'Enabled' : 'Disabled' } }
|
80
|
+
response = authenticated_request(
|
81
|
+
:patch,
|
82
|
+
"/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/LifecycleController.Embedded.1",
|
83
|
+
body: payload.to_json,
|
84
|
+
headers: { 'Content-Type': 'application/json' }
|
85
|
+
)
|
151
86
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
debug "Response body: #{response.body}", 2 if response.body
|
162
|
-
|
163
|
-
if response.status.between?(200, 299)
|
164
|
-
puts "Successfully #{enabled ? 'enabled' : 'disabled'} Lifecycle Controller".green
|
165
|
-
return true
|
166
|
-
else
|
167
|
-
error_message = "Failed to set Lifecycle Controller status. Status code: #{response.status}"
|
168
|
-
|
169
|
-
# Print the full response body for debugging
|
170
|
-
puts "Full error response body:".red
|
171
|
-
puts response.body.inspect.red
|
172
|
-
|
173
|
-
begin
|
174
|
-
error_data = JSON.parse(response.body)
|
175
|
-
puts "Extended error information:".red if error_data['@Message.ExtendedInfo']
|
176
|
-
|
177
|
-
if error_data['error'] && error_data['error']['message']
|
178
|
-
error_message += ", Message: #{error_data['error']['message']}"
|
179
|
-
end
|
180
|
-
|
181
|
-
if error_data['@Message.ExtendedInfo']
|
182
|
-
error_data['@Message.ExtendedInfo'].each do |info|
|
183
|
-
puts " Message: #{info['Message']}".red
|
184
|
-
puts " Resolution: #{info['Resolution']}".yellow if info['Resolution']
|
185
|
-
puts " Severity: #{info['Severity']}".yellow if info['Severity']
|
186
|
-
puts " MessageId: #{info['MessageId']}".yellow if info['MessageId']
|
187
|
-
end
|
188
|
-
|
189
|
-
if error_data['@Message.ExtendedInfo'].first
|
190
|
-
error_message += ", Message: #{error_data['@Message.ExtendedInfo'].first['Message']}"
|
191
|
-
error_message += ", Resolution: #{error_data['@Message.ExtendedInfo'].first['Resolution']}" if error_data['@Message.ExtendedInfo'].first['Resolution']
|
192
|
-
end
|
193
|
-
end
|
194
|
-
rescue => e
|
195
|
-
debug "Error parsing response: #{e.message}", 1
|
196
|
-
# Ignore JSON parsing errors
|
197
|
-
end
|
198
|
-
|
199
|
-
raise Error, error_message
|
200
|
-
end
|
201
|
-
rescue => e
|
202
|
-
debug "Error in request: #{e.message}", 1
|
203
|
-
raise Error, "Failed to set Lifecycle Controller status: #{e.message}"
|
87
|
+
code = response.status
|
88
|
+
case code
|
89
|
+
when 200..299
|
90
|
+
debug "Lifecycle Controller is now #{status ? 'Enabled' : 'Disabled'}".green, 1
|
91
|
+
when 400..499
|
92
|
+
debug "[#{code}] This iDRAC does not support Lifecycle Controller", 0, :red
|
93
|
+
when 500..599
|
94
|
+
debug "[#{code}] iDRAC does not support Lifecycle Controller", 0, :red
|
95
|
+
else
|
204
96
|
end
|
205
97
|
end
|
206
98
|
|
207
99
|
# Ensure the Lifecycle Controller is enabled
|
208
100
|
def ensure_lifecycle_controller!
|
209
|
-
if !
|
210
|
-
|
211
|
-
|
101
|
+
if !get_lifecycle_status
|
102
|
+
debug "Lifecycle Controller is disabled, enabling...".yellow, 1
|
103
|
+
set_lifecycle_status(true)
|
212
104
|
|
213
105
|
# Verify it was enabled
|
214
|
-
if !
|
106
|
+
if !get_lifecycle_status
|
215
107
|
raise Error, "Failed to enable Lifecycle Controller"
|
216
108
|
end
|
217
109
|
|
218
|
-
|
110
|
+
debug "Lifecycle Controller successfully enabled".green, 1
|
219
111
|
else
|
220
|
-
|
112
|
+
debug "Lifecycle Controller is already enabled".green, 1
|
221
113
|
end
|
222
114
|
|
223
115
|
return true
|
@@ -236,10 +128,10 @@ module IDRAC
|
|
236
128
|
)
|
237
129
|
|
238
130
|
if response.status.between?(200, 299)
|
239
|
-
|
131
|
+
debug "Lifecycle log cleared", 0, :green
|
240
132
|
return true
|
241
133
|
else
|
242
|
-
|
134
|
+
debug "Failed to clear Lifecycle log", 0, :red
|
243
135
|
|
244
136
|
error_message = "Failed to clear Lifecycle log. Status code: #{response.status}"
|
245
137
|
|
@@ -279,10 +171,10 @@ module IDRAC
|
|
279
171
|
response = authenticated_request(:post, path, body: {}.to_json, headers: { 'Content-Type' => 'application/json' })
|
280
172
|
|
281
173
|
if response.status.between?(200, 299)
|
282
|
-
|
174
|
+
debug "System Event Logs cleared", 0, :green
|
283
175
|
return true
|
284
176
|
else
|
285
|
-
|
177
|
+
debug "Failed to clear System Event Logs", 0, :red
|
286
178
|
|
287
179
|
error_message = "Failed to clear System Event Logs. Status code: #{response.status}"
|
288
180
|
|
@@ -296,5 +188,10 @@ module IDRAC
|
|
296
188
|
raise Error, error_message
|
297
189
|
end
|
298
190
|
end
|
191
|
+
|
192
|
+
# Updates the status message for the lifecycle controller
|
193
|
+
def update_status_message(status)
|
194
|
+
debug "Lifecycle Controller is now #{status ? 'Enabled' : 'Disabled'}".green, 1
|
195
|
+
end
|
299
196
|
end
|
300
197
|
end
|