idrac 0.1.38 → 0.1.41
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 +76 -14
- data/bin/idrac +246 -89
- data/lib/idrac/client.rb +72 -382
- data/lib/idrac/firmware.rb +26 -2
- data/lib/idrac/jobs.rb +212 -0
- data/lib/idrac/lifecycle.rb +300 -0
- data/lib/idrac/power.rb +195 -0
- data/lib/idrac/session.rb +717 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/web.rb +187 -0
- data/lib/idrac.rb +29 -7
- metadata +8 -4
- data/lib/idrac/screenshot.rb +0 -49
data/lib/idrac/client.rb
CHANGED
@@ -9,23 +9,29 @@ require 'colorize'
|
|
9
9
|
|
10
10
|
module IDRAC
|
11
11
|
class Client
|
12
|
-
attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl, :auto_delete_sessions
|
13
|
-
attr_accessor :direct_mode
|
12
|
+
attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl, :auto_delete_sessions, :session, :web
|
13
|
+
attr_accessor :direct_mode, :verbosity
|
14
|
+
|
15
|
+
include PowerMethods
|
16
|
+
include SessionMethods
|
17
|
+
include Debuggable
|
18
|
+
include JobMethods
|
19
|
+
include LifecycleMethods
|
14
20
|
|
15
|
-
def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl:
|
21
|
+
def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: false, direct_mode: false, auto_delete_sessions: true)
|
16
22
|
@host = host
|
17
23
|
@username = username
|
18
24
|
@password = password
|
19
25
|
@port = port
|
20
26
|
@use_ssl = use_ssl
|
21
27
|
@verify_ssl = verify_ssl
|
22
|
-
@session_id = nil
|
23
|
-
@cookies = nil
|
24
|
-
@x_auth_token = nil
|
25
28
|
@direct_mode = direct_mode
|
26
|
-
@sessions_maxed = false
|
27
|
-
@tried_clearing_sessions = false
|
28
29
|
@auto_delete_sessions = auto_delete_sessions
|
30
|
+
@verbosity = 0
|
31
|
+
|
32
|
+
# Initialize the session and web classes
|
33
|
+
@session = Session.new(self)
|
34
|
+
@web = Web.new(self)
|
29
35
|
end
|
30
36
|
|
31
37
|
def connection
|
@@ -33,353 +39,13 @@ module IDRAC
|
|
33
39
|
faraday.request :multipart
|
34
40
|
faraday.request :url_encoded
|
35
41
|
faraday.adapter Faraday.default_adapter
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
puts "Attempting to force clear all sessions...".light_cyan
|
42
|
-
|
43
|
-
# Try to delete sessions directly using Basic Auth
|
44
|
-
if delete_all_sessions_with_basic_auth
|
45
|
-
puts "Successfully cleared sessions using Basic Auth".green
|
46
|
-
return true
|
47
|
-
else
|
48
|
-
puts "Failed to clear sessions using Basic Auth".red
|
49
|
-
return false
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Delete all sessions using Basic Authentication
|
54
|
-
def delete_all_sessions_with_basic_auth
|
55
|
-
puts "Attempting to delete all sessions using Basic Authentication...".light_cyan
|
56
|
-
|
57
|
-
# First, get the list of sessions
|
58
|
-
sessions_url = '/redfish/v1/SessionService/Sessions'
|
59
|
-
|
60
|
-
# Create a connection with Basic Auth
|
61
|
-
basic_auth_headers = {
|
62
|
-
'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
|
63
|
-
'Content-Type' => 'application/json'
|
64
|
-
}
|
65
|
-
|
66
|
-
begin
|
67
|
-
# Get the list of sessions
|
68
|
-
response = connection.get(sessions_url) do |req|
|
69
|
-
req.headers.merge!(basic_auth_headers)
|
70
|
-
end
|
71
|
-
|
72
|
-
if response.status != 200
|
73
|
-
puts "Failed to get sessions list: #{response.status} - #{response.body}".red
|
74
|
-
return false
|
75
|
-
end
|
76
|
-
|
77
|
-
# Parse the response to get session IDs
|
78
|
-
begin
|
79
|
-
sessions_data = JSON.parse(response.body)
|
80
|
-
|
81
|
-
if sessions_data['Members'] && sessions_data['Members'].any?
|
82
|
-
puts "Found #{sessions_data['Members'].count} active sessions".light_yellow
|
83
|
-
|
84
|
-
# Delete each session
|
85
|
-
success = true
|
86
|
-
sessions_data['Members'].each do |session|
|
87
|
-
session_url = session['@odata.id']
|
88
|
-
|
89
|
-
# Skip if no URL
|
90
|
-
next unless session_url
|
91
|
-
|
92
|
-
# Delete the session
|
93
|
-
delete_response = connection.delete(session_url) do |req|
|
94
|
-
req.headers.merge!(basic_auth_headers)
|
95
|
-
end
|
96
|
-
|
97
|
-
if delete_response.status == 200 || delete_response.status == 204
|
98
|
-
puts "Successfully deleted session: #{session_url}".green
|
99
|
-
else
|
100
|
-
puts "Failed to delete session #{session_url}: #{delete_response.status}".red
|
101
|
-
success = false
|
102
|
-
end
|
103
|
-
|
104
|
-
# Small delay between deletions
|
105
|
-
sleep(1)
|
106
|
-
end
|
107
|
-
|
108
|
-
return success
|
109
|
-
else
|
110
|
-
puts "No active sessions found".light_yellow
|
111
|
-
return true
|
42
|
+
# Add request/response logging based on verbosity
|
43
|
+
if @verbosity > 0
|
44
|
+
faraday.response :logger, Logger.new(STDOUT), bodies: @verbosity >= 2 do |logger|
|
45
|
+
logger.filter(/(Authorization: Basic )([^,\n]+)/, '\1[FILTERED]')
|
46
|
+
logger.filter(/(Password"=>"?)([^,"]+)/, '\1[FILTERED]')
|
112
47
|
end
|
113
|
-
rescue JSON::ParserError => e
|
114
|
-
puts "Error parsing sessions response: #{e.message}".red.bold
|
115
|
-
return false
|
116
48
|
end
|
117
|
-
rescue => e
|
118
|
-
puts "Error during session deletion with Basic Auth: #{e.message}".red.bold
|
119
|
-
return false
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
# Create a Redfish session
|
124
|
-
def create_redfish_session
|
125
|
-
# Skip if we're in direct mode
|
126
|
-
if @direct_mode
|
127
|
-
puts "Skipping Redfish session creation (direct mode)".light_yellow
|
128
|
-
return false
|
129
|
-
end
|
130
|
-
|
131
|
-
url = '/redfish/v1/SessionService/Sessions'
|
132
|
-
payload = { "UserName" => username, "Password" => password }
|
133
|
-
|
134
|
-
# Try first with just Content-Type header (no Basic Auth)
|
135
|
-
begin
|
136
|
-
response = connection.post(url) do |req|
|
137
|
-
req.headers['Content-Type'] = 'application/json'
|
138
|
-
req.body = payload.to_json
|
139
|
-
end
|
140
|
-
|
141
|
-
if response.status == 201 || response.status == 200
|
142
|
-
# Extract X-Auth-Token from response headers
|
143
|
-
@x_auth_token = response.headers['X-Auth-Token']
|
144
|
-
|
145
|
-
# Extract session location from response headers
|
146
|
-
@session_location = response.headers['Location']
|
147
|
-
|
148
|
-
puts "Redfish session created successfully".green
|
149
|
-
@sessions_maxed = false
|
150
|
-
return true
|
151
|
-
end
|
152
|
-
rescue => e
|
153
|
-
puts "First session creation attempt failed: #{e.message}".light_red
|
154
|
-
end
|
155
|
-
|
156
|
-
# If that fails, try with Basic Auth
|
157
|
-
begin
|
158
|
-
# Use Basic Auth for the session creation
|
159
|
-
basic_auth_headers = {
|
160
|
-
'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
|
161
|
-
'Content-Type' => 'application/json'
|
162
|
-
}
|
163
|
-
|
164
|
-
response = connection.post(url) do |req|
|
165
|
-
req.headers.merge!(basic_auth_headers)
|
166
|
-
req.body = payload.to_json
|
167
|
-
end
|
168
|
-
|
169
|
-
if response.status == 201 || response.status == 200
|
170
|
-
# Extract X-Auth-Token from response headers
|
171
|
-
@x_auth_token = response.headers['X-Auth-Token']
|
172
|
-
|
173
|
-
# Extract session location from response headers
|
174
|
-
@session_location = response.headers['Location']
|
175
|
-
|
176
|
-
puts "Redfish session created successfully with Basic Auth".green
|
177
|
-
@sessions_maxed = false
|
178
|
-
return true
|
179
|
-
elsif response.status == 400 && response.body.include?("maximum number of user sessions")
|
180
|
-
puts "Maximum sessions reached during Redfish session creation".light_red
|
181
|
-
@sessions_maxed = true
|
182
|
-
|
183
|
-
# Try to clear sessions if auto_delete_sessions is enabled
|
184
|
-
if @auto_delete_sessions
|
185
|
-
puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
|
186
|
-
if force_clear_sessions
|
187
|
-
puts "Successfully cleared sessions, trying to create a new session".green
|
188
|
-
|
189
|
-
# Try one more time after clearing
|
190
|
-
response = connection.post(url) do |req|
|
191
|
-
req.headers.merge!(basic_auth_headers)
|
192
|
-
req.body = payload.to_json
|
193
|
-
end
|
194
|
-
|
195
|
-
if response.status == 201 || response.status == 200
|
196
|
-
@x_auth_token = response.headers['X-Auth-Token']
|
197
|
-
@session_location = response.headers['Location']
|
198
|
-
puts "Redfish session created successfully after clearing sessions".green
|
199
|
-
@sessions_maxed = false
|
200
|
-
return true
|
201
|
-
else
|
202
|
-
puts "Failed to create Redfish session after clearing: #{response.status} - #{response.body}".red
|
203
|
-
# If we still can't create a session, switch to direct mode
|
204
|
-
@direct_mode = true
|
205
|
-
return false
|
206
|
-
end
|
207
|
-
else
|
208
|
-
puts "Failed to clear sessions, switching to direct mode".light_yellow
|
209
|
-
@direct_mode = true
|
210
|
-
return false
|
211
|
-
end
|
212
|
-
else
|
213
|
-
puts "Auto-delete sessions is disabled, switching to direct mode".light_yellow
|
214
|
-
@direct_mode = true
|
215
|
-
return false
|
216
|
-
end
|
217
|
-
else
|
218
|
-
puts "Failed to create Redfish session: #{response.status} - #{response.body}".red
|
219
|
-
|
220
|
-
# If we get a 415 error, try with form-urlencoded
|
221
|
-
if response.status == 415
|
222
|
-
puts "Trying with form-urlencoded content type".light_cyan
|
223
|
-
response = connection.post(url) do |req|
|
224
|
-
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
225
|
-
req.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
226
|
-
req.body = "UserName=#{URI.encode_www_form_component(username)}&Password=#{URI.encode_www_form_component(password)}"
|
227
|
-
end
|
228
|
-
|
229
|
-
if response.status == 201 || response.status == 200
|
230
|
-
@x_auth_token = response.headers['X-Auth-Token']
|
231
|
-
@session_location = response.headers['Location']
|
232
|
-
puts "Redfish session created successfully with form-urlencoded".green
|
233
|
-
@sessions_maxed = false
|
234
|
-
return true
|
235
|
-
else
|
236
|
-
puts "Failed with form-urlencoded too: #{response.status} - #{response.body}".red
|
237
|
-
@direct_mode = true
|
238
|
-
return false
|
239
|
-
end
|
240
|
-
else
|
241
|
-
@direct_mode = true
|
242
|
-
return false
|
243
|
-
end
|
244
|
-
end
|
245
|
-
rescue => e
|
246
|
-
puts "Error during Redfish session creation: #{e.message}".red.bold
|
247
|
-
@direct_mode = true
|
248
|
-
return false
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
# Delete the Redfish session
|
253
|
-
def delete_redfish_session
|
254
|
-
return unless @x_auth_token && @session_location
|
255
|
-
|
256
|
-
begin
|
257
|
-
puts "Deleting Redfish session...".light_cyan
|
258
|
-
|
259
|
-
# Use the X-Auth-Token for authentication
|
260
|
-
headers = { 'X-Auth-Token' => @x_auth_token }
|
261
|
-
|
262
|
-
response = connection.delete(@session_location) do |req|
|
263
|
-
req.headers.merge!(headers)
|
264
|
-
end
|
265
|
-
|
266
|
-
if response.status == 200 || response.status == 204
|
267
|
-
puts "Redfish session deleted successfully".green
|
268
|
-
@x_auth_token = nil
|
269
|
-
@session_location = nil
|
270
|
-
return true
|
271
|
-
else
|
272
|
-
puts "Failed to delete Redfish session: #{response.status} - #{response.body}".red
|
273
|
-
return false
|
274
|
-
end
|
275
|
-
rescue => e
|
276
|
-
puts "Error during Redfish session deletion: #{e.message}".red.bold
|
277
|
-
return false
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# Login to the WebUI (for screenshot functionality)
|
282
|
-
def webui_login(retry_count = 0)
|
283
|
-
# Limit retries to prevent infinite loops
|
284
|
-
if retry_count >= 3
|
285
|
-
puts "Maximum retry count reached for WebUI login".red
|
286
|
-
return false
|
287
|
-
end
|
288
|
-
|
289
|
-
# Skip if we already have a session ID
|
290
|
-
return true if @session_id
|
291
|
-
|
292
|
-
begin
|
293
|
-
puts "Logging in to WebUI...".light_cyan
|
294
|
-
|
295
|
-
# Create the login URL
|
296
|
-
login_url = "#{base_url}/data/login"
|
297
|
-
|
298
|
-
# Create the login payload
|
299
|
-
payload = {
|
300
|
-
'user' => username,
|
301
|
-
'password' => password
|
302
|
-
}
|
303
|
-
|
304
|
-
# Make the login request
|
305
|
-
response = HTTParty.post(
|
306
|
-
login_url,
|
307
|
-
body: payload,
|
308
|
-
verify: verify_ssl,
|
309
|
-
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
310
|
-
)
|
311
|
-
|
312
|
-
# Check if the login was successful
|
313
|
-
if response.code == 200
|
314
|
-
# Extract the session ID from the response
|
315
|
-
if response.body.include?('ST2')
|
316
|
-
@session_id = response.body.match(/ST2=([^;]+)/)[1]
|
317
|
-
@cookies = response.headers['set-cookie']
|
318
|
-
puts "WebUI login successful".green
|
319
|
-
return true
|
320
|
-
else
|
321
|
-
puts "WebUI login failed: No session ID found in response".red
|
322
|
-
return false
|
323
|
-
end
|
324
|
-
elsif response.code == 400 && response.body.include?("maximum number of user sessions")
|
325
|
-
puts "Maximum sessions reached during WebUI login".light_red
|
326
|
-
|
327
|
-
# Try to clear sessions if auto_delete_sessions is enabled
|
328
|
-
if @auto_delete_sessions && !@tried_clearing_sessions
|
329
|
-
puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
|
330
|
-
@tried_clearing_sessions = true
|
331
|
-
|
332
|
-
if force_clear_sessions
|
333
|
-
puts "Successfully cleared sessions, trying WebUI login again".green
|
334
|
-
return webui_login(retry_count + 1)
|
335
|
-
else
|
336
|
-
puts "Failed to clear sessions for WebUI login".red
|
337
|
-
return false
|
338
|
-
end
|
339
|
-
else
|
340
|
-
puts "Auto-delete sessions is disabled or already tried clearing".light_yellow
|
341
|
-
return false
|
342
|
-
end
|
343
|
-
else
|
344
|
-
puts "WebUI login failed: #{response.code} - #{response.body}".red
|
345
|
-
return false
|
346
|
-
end
|
347
|
-
rescue => e
|
348
|
-
puts "Error during WebUI login: #{e.message}".red.bold
|
349
|
-
return false
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
# Logout from the WebUI
|
354
|
-
def webui_logout
|
355
|
-
return unless @session_id
|
356
|
-
|
357
|
-
begin
|
358
|
-
puts "Logging out from WebUI...".light_cyan
|
359
|
-
|
360
|
-
# Create the logout URL
|
361
|
-
logout_url = "#{base_url}/data/logout"
|
362
|
-
|
363
|
-
# Make the logout request
|
364
|
-
response = HTTParty.get(
|
365
|
-
logout_url,
|
366
|
-
verify: verify_ssl,
|
367
|
-
headers: { 'Cookie' => @cookies }
|
368
|
-
)
|
369
|
-
|
370
|
-
# Check if the logout was successful
|
371
|
-
if response.code == 200
|
372
|
-
puts "WebUI logout successful".green
|
373
|
-
@session_id = nil
|
374
|
-
@cookies = nil
|
375
|
-
return true
|
376
|
-
else
|
377
|
-
puts "WebUI logout failed: #{response.code} - #{response.body}".red
|
378
|
-
return false
|
379
|
-
end
|
380
|
-
rescue => e
|
381
|
-
puts "Error during WebUI logout: #{e.message}".red.bold
|
382
|
-
return false
|
383
49
|
end
|
384
50
|
end
|
385
51
|
|
@@ -392,16 +58,11 @@ module IDRAC
|
|
392
58
|
end
|
393
59
|
|
394
60
|
# Try to create a Redfish session
|
395
|
-
if
|
61
|
+
if session.create
|
396
62
|
puts "Successfully logged in to iDRAC using Redfish session".green
|
397
63
|
return true
|
398
64
|
else
|
399
|
-
|
400
|
-
puts "Maximum sessions reached and could not clear sessions".light_red
|
401
|
-
else
|
402
|
-
puts "Failed to create Redfish session, falling back to direct mode".light_yellow
|
403
|
-
end
|
404
|
-
|
65
|
+
puts "Failed to create Redfish session, falling back to direct mode".light_yellow
|
405
66
|
@direct_mode = true
|
406
67
|
return true
|
407
68
|
end
|
@@ -409,14 +70,8 @@ module IDRAC
|
|
409
70
|
|
410
71
|
# Logout from iDRAC
|
411
72
|
def logout
|
412
|
-
if
|
413
|
-
|
414
|
-
end
|
415
|
-
|
416
|
-
if @session_id
|
417
|
-
webui_logout
|
418
|
-
end
|
419
|
-
|
73
|
+
session.delete if session.x_auth_token
|
74
|
+
web.logout if web.session_id
|
420
75
|
puts "Logged out from iDRAC".green
|
421
76
|
return true
|
422
77
|
end
|
@@ -429,6 +84,8 @@ module IDRAC
|
|
429
84
|
raise Error, "Maximum retry count reached for authenticated request"
|
430
85
|
end
|
431
86
|
|
87
|
+
debug "Authenticated request: #{method.to_s.upcase} #{path}", 1
|
88
|
+
|
432
89
|
# If we're in direct mode, use Basic Auth
|
433
90
|
if @direct_mode
|
434
91
|
# Create Basic Auth header
|
@@ -438,6 +95,8 @@ module IDRAC
|
|
438
95
|
options[:headers] ||= {}
|
439
96
|
options[:headers]['Authorization'] = auth_header
|
440
97
|
|
98
|
+
debug "Using Basic Auth for request", 2
|
99
|
+
|
441
100
|
# Make the request
|
442
101
|
begin
|
443
102
|
response = connection.send(method, path) do |req|
|
@@ -445,6 +104,10 @@ module IDRAC
|
|
445
104
|
req.body = options[:body] if options[:body]
|
446
105
|
end
|
447
106
|
|
107
|
+
debug "Response status: #{response.status}", 1
|
108
|
+
debug "Response headers: #{response.headers.inspect}", 2
|
109
|
+
debug "Response body: #{response.body}", 3 if response.body
|
110
|
+
|
448
111
|
return response
|
449
112
|
rescue => e
|
450
113
|
puts "Error during authenticated request (direct mode): #{e.message}".red.bold
|
@@ -452,10 +115,12 @@ module IDRAC
|
|
452
115
|
end
|
453
116
|
else
|
454
117
|
# Use X-Auth-Token if available
|
455
|
-
if
|
118
|
+
if session.x_auth_token
|
456
119
|
# Add the X-Auth-Token header to the request
|
457
120
|
options[:headers] ||= {}
|
458
|
-
options[:headers]['X-Auth-Token'] =
|
121
|
+
options[:headers]['X-Auth-Token'] = session.x_auth_token
|
122
|
+
|
123
|
+
debug "Using X-Auth-Token for request", 2
|
459
124
|
|
460
125
|
# Make the request
|
461
126
|
begin
|
@@ -464,12 +129,16 @@ module IDRAC
|
|
464
129
|
req.body = options[:body] if options[:body]
|
465
130
|
end
|
466
131
|
|
132
|
+
debug "Response status: #{response.status}", 1
|
133
|
+
debug "Response headers: #{response.headers.inspect}", 2
|
134
|
+
debug "Response body: #{response.body}", 3 if response.body
|
135
|
+
|
467
136
|
# Check if the session is still valid
|
468
137
|
if response.status == 401 || response.status == 403
|
469
138
|
puts "Session expired or invalid, attempting to create a new session...".light_yellow
|
470
139
|
|
471
140
|
# Try to create a new session
|
472
|
-
if
|
141
|
+
if session.create
|
473
142
|
puts "Successfully created a new session, retrying request...".green
|
474
143
|
return authenticated_request(method, path, options, retry_count + 1)
|
475
144
|
else
|
@@ -484,7 +153,7 @@ module IDRAC
|
|
484
153
|
puts "Error during authenticated request (token mode): #{e.message}".red.bold
|
485
154
|
|
486
155
|
# Try to create a new session
|
487
|
-
if
|
156
|
+
if session.create
|
488
157
|
puts "Successfully created a new session after error, retrying request...".green
|
489
158
|
return authenticated_request(method, path, options, retry_count + 1)
|
490
159
|
else
|
@@ -495,7 +164,7 @@ module IDRAC
|
|
495
164
|
end
|
496
165
|
else
|
497
166
|
# If we don't have a token, try to create a session
|
498
|
-
if
|
167
|
+
if session.create
|
499
168
|
puts "Successfully created a new session, making request...".green
|
500
169
|
return authenticated_request(method, path, options, retry_count + 1)
|
501
170
|
else
|
@@ -509,40 +178,61 @@ module IDRAC
|
|
509
178
|
|
510
179
|
def get(path:, headers: {})
|
511
180
|
# For screenshot functionality, we need to use the WebUI cookies
|
512
|
-
if
|
513
|
-
|
181
|
+
if web.cookies.nil? && path.include?('screen/screen.jpg')
|
182
|
+
web.login unless web.session_id
|
514
183
|
end
|
515
184
|
|
185
|
+
debug "GET request to #{base_url}/#{path}", 1
|
186
|
+
|
516
187
|
headers_to_use = {
|
517
188
|
"User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
|
518
189
|
"Accept-Encoding" => "deflate, gzip"
|
519
190
|
}
|
520
191
|
|
521
|
-
if
|
522
|
-
headers_to_use["Cookie"] =
|
192
|
+
if web.cookies
|
193
|
+
headers_to_use["Cookie"] = web.cookies
|
194
|
+
debug "Using WebUI cookies for request", 2
|
523
195
|
elsif @direct_mode
|
524
196
|
# In direct mode, use Basic Auth
|
525
197
|
headers_to_use["Authorization"] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
526
|
-
|
527
|
-
|
198
|
+
debug "Using Basic Auth for GET request", 2
|
199
|
+
elsif session.x_auth_token
|
200
|
+
headers_to_use["X-Auth-Token"] = session.x_auth_token
|
201
|
+
debug "Using X-Auth-Token for GET request", 2
|
528
202
|
end
|
529
203
|
|
204
|
+
debug "Request headers: #{headers_to_use.merge(headers).inspect}", 3
|
205
|
+
|
530
206
|
response = HTTParty.get(
|
531
207
|
"#{base_url}/#{path}",
|
532
208
|
headers: headers_to_use.merge(headers),
|
533
209
|
verify: false
|
534
210
|
)
|
211
|
+
|
212
|
+
debug "Response status: #{response.code}", 1
|
213
|
+
debug "Response headers: #{response.headers.inspect}", 2
|
214
|
+
debug "Response body: #{response.body.to_s[0..500]}#{response.body.to_s.length > 500 ? '...' : ''}", 3 if response.body
|
215
|
+
|
216
|
+
response
|
535
217
|
end
|
536
218
|
|
537
219
|
def screenshot
|
538
|
-
|
539
|
-
screenshot_instance = Screenshot.new(self)
|
540
|
-
screenshot_instance.capture
|
220
|
+
web.capture_screenshot
|
541
221
|
end
|
542
222
|
|
543
223
|
def base_url
|
544
224
|
protocol = use_ssl ? 'https' : 'http'
|
545
225
|
"#{protocol}://#{host}:#{port}"
|
546
226
|
end
|
227
|
+
|
228
|
+
def redfish_version
|
229
|
+
response = authenticated_request(:get, "/redfish/v1")
|
230
|
+
if response.status == 200
|
231
|
+
data = JSON.parse(response.body)
|
232
|
+
data["RedfishVersion"]
|
233
|
+
else
|
234
|
+
raise Error, "Failed to get Redfish version: #{response.status} - #{response.body}"
|
235
|
+
end
|
236
|
+
end
|
547
237
|
end
|
548
238
|
end
|
data/lib/idrac/firmware.rb
CHANGED
@@ -8,6 +8,8 @@ require 'securerandom'
|
|
8
8
|
require 'set'
|
9
9
|
require 'colorize'
|
10
10
|
require_relative 'firmware_catalog'
|
11
|
+
require 'faraday'
|
12
|
+
require 'faraday/multipart'
|
11
13
|
|
12
14
|
module IDRAC
|
13
15
|
class Firmware
|
@@ -427,6 +429,24 @@ module IDRAC
|
|
427
429
|
end
|
428
430
|
end
|
429
431
|
|
432
|
+
def get_power_state
|
433
|
+
# Ensure we have a client
|
434
|
+
raise Error, "Client is required for power management" unless client
|
435
|
+
|
436
|
+
# Login to iDRAC if needed
|
437
|
+
client.login unless client.instance_variable_get(:@session_id)
|
438
|
+
|
439
|
+
# Get system information
|
440
|
+
response = client.authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
|
441
|
+
|
442
|
+
if response.status == 200
|
443
|
+
system_data = JSON.parse(response.body)
|
444
|
+
return system_data["PowerState"]
|
445
|
+
else
|
446
|
+
raise Error, "Failed to get power state. Status code: #{response.status}"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
430
450
|
private
|
431
451
|
|
432
452
|
def upload_firmware(firmware_path)
|
@@ -478,16 +498,20 @@ module IDRAC
|
|
478
498
|
file_content = File.read(firmware_path)
|
479
499
|
|
480
500
|
headers = {
|
481
|
-
'Content-Type' => '
|
501
|
+
'Content-Type' => 'multipart/form-data',
|
482
502
|
'If-Match' => etag
|
483
503
|
}
|
484
504
|
|
505
|
+
# Create a temp file for multipart upload
|
506
|
+
upload_io = Faraday::UploadIO.new(firmware_path, 'application/octet-stream')
|
507
|
+
payload = { :file => upload_io }
|
508
|
+
|
485
509
|
upload_response = client.authenticated_request(
|
486
510
|
:post,
|
487
511
|
http_push_uri,
|
488
512
|
{
|
489
513
|
headers: headers,
|
490
|
-
body:
|
514
|
+
body: payload,
|
491
515
|
}
|
492
516
|
)
|
493
517
|
|