idrac 0.1.38 → 0.1.40
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 +1 -6
- data/bin/idrac +2 -3
- data/lib/idrac/client.rb +22 -383
- data/lib/idrac/session.rb +278 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/web.rb +187 -0
- data/lib/idrac.rb +2 -1
- metadata +5 -4
- data/lib/idrac/screenshot.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 822357c4c005e77d857696867b06be46e3f1f473a239e2936119aec1e68c5eb9
|
4
|
+
data.tar.gz: 6ca9c128259d2d94518b2b22ee0d8bfff02624a8f8b15fbabd816a2e08351f8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a50e1c605aaaa5c4cfe72a49a60d41c63765c20e6bfc3d7d43063a0e53cd29804797094e38287e63ee3cddbcd1335ba543417ec75f089541a950ef55654042df
|
7
|
+
data.tar.gz: d3306667a4271507a7923a95b78b557ca55abe78900fee320c36bfb98eda0cafbd268afaaa4bf4fee4a2241b344b634ce445900bae9a768132862995553dd813
|
data/README.md
CHANGED
@@ -92,15 +92,10 @@ client = IDRAC.new(
|
|
92
92
|
# The client automatically handles session expiration (401 errors)
|
93
93
|
# by re-authenticating and retrying the request
|
94
94
|
|
95
|
-
# Take a screenshot (using the client
|
95
|
+
# Take a screenshot (using the client method)
|
96
96
|
filename = client.screenshot
|
97
97
|
puts "Screenshot saved to: #{filename}"
|
98
98
|
|
99
|
-
# Or use the Screenshot class directly for more control
|
100
|
-
screenshot = IDRAC::Screenshot.new(client)
|
101
|
-
filename = screenshot.capture
|
102
|
-
puts "Screenshot saved to: #{filename}"
|
103
|
-
|
104
99
|
# Firmware operations
|
105
100
|
firmware = IDRAC::Firmware.new(client)
|
106
101
|
|
data/bin/idrac
CHANGED
@@ -185,9 +185,8 @@ module IDRAC
|
|
185
185
|
client = create_client
|
186
186
|
|
187
187
|
begin
|
188
|
-
#
|
189
|
-
|
190
|
-
filename = screenshot.capture
|
188
|
+
# Capture a screenshot using the client
|
189
|
+
filename = client.screenshot
|
191
190
|
|
192
191
|
# Rename the file if output option is provided
|
193
192
|
if options[:output]
|
data/lib/idrac/client.rb
CHANGED
@@ -9,7 +9,7 @@ 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
|
12
|
+
attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl, :auto_delete_sessions, :session, :web
|
13
13
|
attr_accessor :direct_mode
|
14
14
|
|
15
15
|
def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true, direct_mode: false, auto_delete_sessions: true)
|
@@ -19,13 +19,12 @@ module IDRAC
|
|
19
19
|
@port = port
|
20
20
|
@use_ssl = use_ssl
|
21
21
|
@verify_ssl = verify_ssl
|
22
|
-
@session_id = nil
|
23
|
-
@cookies = nil
|
24
|
-
@x_auth_token = nil
|
25
22
|
@direct_mode = direct_mode
|
26
|
-
@sessions_maxed = false
|
27
|
-
@tried_clearing_sessions = false
|
28
23
|
@auto_delete_sessions = auto_delete_sessions
|
24
|
+
|
25
|
+
# Initialize the session and web classes
|
26
|
+
@session = Session.new(self)
|
27
|
+
@web = Web.new(self)
|
29
28
|
end
|
30
29
|
|
31
30
|
def connection
|
@@ -36,353 +35,6 @@ module IDRAC
|
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
|
-
# Force clear all sessions by directly using Basic Auth
|
40
|
-
def force_clear_sessions
|
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
|
112
|
-
end
|
113
|
-
rescue JSON::ParserError => e
|
114
|
-
puts "Error parsing sessions response: #{e.message}".red.bold
|
115
|
-
return false
|
116
|
-
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
|
-
end
|
384
|
-
end
|
385
|
-
|
386
38
|
# Login to iDRAC
|
387
39
|
def login
|
388
40
|
# If we're in direct mode, skip login attempts
|
@@ -392,16 +44,11 @@ module IDRAC
|
|
392
44
|
end
|
393
45
|
|
394
46
|
# Try to create a Redfish session
|
395
|
-
if
|
47
|
+
if session.create
|
396
48
|
puts "Successfully logged in to iDRAC using Redfish session".green
|
397
49
|
return true
|
398
50
|
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
|
-
|
51
|
+
puts "Failed to create Redfish session, falling back to direct mode".light_yellow
|
405
52
|
@direct_mode = true
|
406
53
|
return true
|
407
54
|
end
|
@@ -409,14 +56,8 @@ module IDRAC
|
|
409
56
|
|
410
57
|
# Logout from iDRAC
|
411
58
|
def logout
|
412
|
-
if
|
413
|
-
|
414
|
-
end
|
415
|
-
|
416
|
-
if @session_id
|
417
|
-
webui_logout
|
418
|
-
end
|
419
|
-
|
59
|
+
session.delete if session.x_auth_token
|
60
|
+
web.logout if web.session_id
|
420
61
|
puts "Logged out from iDRAC".green
|
421
62
|
return true
|
422
63
|
end
|
@@ -452,10 +93,10 @@ module IDRAC
|
|
452
93
|
end
|
453
94
|
else
|
454
95
|
# Use X-Auth-Token if available
|
455
|
-
if
|
96
|
+
if session.x_auth_token
|
456
97
|
# Add the X-Auth-Token header to the request
|
457
98
|
options[:headers] ||= {}
|
458
|
-
options[:headers]['X-Auth-Token'] =
|
99
|
+
options[:headers]['X-Auth-Token'] = session.x_auth_token
|
459
100
|
|
460
101
|
# Make the request
|
461
102
|
begin
|
@@ -469,7 +110,7 @@ module IDRAC
|
|
469
110
|
puts "Session expired or invalid, attempting to create a new session...".light_yellow
|
470
111
|
|
471
112
|
# Try to create a new session
|
472
|
-
if
|
113
|
+
if session.create
|
473
114
|
puts "Successfully created a new session, retrying request...".green
|
474
115
|
return authenticated_request(method, path, options, retry_count + 1)
|
475
116
|
else
|
@@ -484,7 +125,7 @@ module IDRAC
|
|
484
125
|
puts "Error during authenticated request (token mode): #{e.message}".red.bold
|
485
126
|
|
486
127
|
# Try to create a new session
|
487
|
-
if
|
128
|
+
if session.create
|
488
129
|
puts "Successfully created a new session after error, retrying request...".green
|
489
130
|
return authenticated_request(method, path, options, retry_count + 1)
|
490
131
|
else
|
@@ -495,7 +136,7 @@ module IDRAC
|
|
495
136
|
end
|
496
137
|
else
|
497
138
|
# If we don't have a token, try to create a session
|
498
|
-
if
|
139
|
+
if session.create
|
499
140
|
puts "Successfully created a new session, making request...".green
|
500
141
|
return authenticated_request(method, path, options, retry_count + 1)
|
501
142
|
else
|
@@ -509,8 +150,8 @@ module IDRAC
|
|
509
150
|
|
510
151
|
def get(path:, headers: {})
|
511
152
|
# For screenshot functionality, we need to use the WebUI cookies
|
512
|
-
if
|
513
|
-
|
153
|
+
if web.cookies.nil? && path.include?('screen/screen.jpg')
|
154
|
+
web.login unless web.session_id
|
514
155
|
end
|
515
156
|
|
516
157
|
headers_to_use = {
|
@@ -518,16 +159,16 @@ module IDRAC
|
|
518
159
|
"Accept-Encoding" => "deflate, gzip"
|
519
160
|
}
|
520
161
|
|
521
|
-
if
|
522
|
-
headers_to_use["Cookie"] =
|
162
|
+
if web.cookies
|
163
|
+
headers_to_use["Cookie"] = web.cookies
|
523
164
|
elsif @direct_mode
|
524
165
|
# In direct mode, use Basic Auth
|
525
166
|
headers_to_use["Authorization"] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
526
|
-
elsif
|
527
|
-
headers_to_use["X-Auth-Token"] =
|
167
|
+
elsif session.x_auth_token
|
168
|
+
headers_to_use["X-Auth-Token"] = session.x_auth_token
|
528
169
|
end
|
529
170
|
|
530
|
-
|
171
|
+
HTTParty.get(
|
531
172
|
"#{base_url}/#{path}",
|
532
173
|
headers: headers_to_use.merge(headers),
|
533
174
|
verify: false
|
@@ -535,9 +176,7 @@ module IDRAC
|
|
535
176
|
end
|
536
177
|
|
537
178
|
def screenshot
|
538
|
-
|
539
|
-
screenshot_instance = Screenshot.new(self)
|
540
|
-
screenshot_instance.capture
|
179
|
+
web.capture_screenshot
|
541
180
|
end
|
542
181
|
|
543
182
|
def base_url
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
require 'colorize'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module IDRAC
|
8
|
+
class Session
|
9
|
+
attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl,
|
10
|
+
:x_auth_token, :session_location, :direct_mode, :auto_delete_sessions
|
11
|
+
|
12
|
+
def initialize(client)
|
13
|
+
@client = client
|
14
|
+
@host = client.host
|
15
|
+
@username = client.username
|
16
|
+
@password = client.password
|
17
|
+
@port = client.port
|
18
|
+
@use_ssl = client.use_ssl
|
19
|
+
@verify_ssl = client.verify_ssl
|
20
|
+
@x_auth_token = nil
|
21
|
+
@session_location = nil
|
22
|
+
@direct_mode = client.direct_mode
|
23
|
+
@sessions_maxed = false
|
24
|
+
@auto_delete_sessions = client.auto_delete_sessions
|
25
|
+
end
|
26
|
+
|
27
|
+
def connection
|
28
|
+
@connection ||= Faraday.new(url: base_url, ssl: { verify: verify_ssl }) do |faraday|
|
29
|
+
faraday.request :multipart
|
30
|
+
faraday.request :url_encoded
|
31
|
+
faraday.adapter Faraday.default_adapter
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Force clear all sessions by directly using Basic Auth
|
36
|
+
def force_clear_sessions
|
37
|
+
puts "Attempting to force clear all sessions...".light_cyan
|
38
|
+
|
39
|
+
if delete_all_sessions_with_basic_auth
|
40
|
+
puts "Successfully cleared sessions using Basic Auth".green
|
41
|
+
true
|
42
|
+
else
|
43
|
+
puts "Failed to clear sessions using Basic Auth".red
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Delete all sessions using Basic Authentication
|
49
|
+
def delete_all_sessions_with_basic_auth
|
50
|
+
puts "Attempting to delete all sessions using Basic Authentication...".light_cyan
|
51
|
+
|
52
|
+
# First, get the list of sessions
|
53
|
+
sessions_url = '/redfish/v1/SessionService/Sessions'
|
54
|
+
|
55
|
+
begin
|
56
|
+
# Get the list of sessions
|
57
|
+
response = request_with_basic_auth(:get, sessions_url)
|
58
|
+
|
59
|
+
if response.status != 200
|
60
|
+
puts "Failed to get sessions list: #{response.status} - #{response.body}".red
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parse the response to get session IDs
|
65
|
+
begin
|
66
|
+
sessions_data = JSON.parse(response.body)
|
67
|
+
|
68
|
+
if sessions_data['Members'] && sessions_data['Members'].any?
|
69
|
+
puts "Found #{sessions_data['Members'].count} active sessions".light_yellow
|
70
|
+
|
71
|
+
# Delete each session
|
72
|
+
success = true
|
73
|
+
sessions_data['Members'].each do |session|
|
74
|
+
session_url = session['@odata.id']
|
75
|
+
|
76
|
+
# Skip if no URL
|
77
|
+
next unless session_url
|
78
|
+
|
79
|
+
# Delete the session
|
80
|
+
delete_response = request_with_basic_auth(:delete, session_url)
|
81
|
+
|
82
|
+
if delete_response.status == 200 || delete_response.status == 204
|
83
|
+
puts "Successfully deleted session: #{session_url}".green
|
84
|
+
else
|
85
|
+
puts "Failed to delete session #{session_url}: #{delete_response.status}".red
|
86
|
+
success = false
|
87
|
+
end
|
88
|
+
|
89
|
+
# Small delay between deletions
|
90
|
+
sleep(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
return success
|
94
|
+
else
|
95
|
+
puts "No active sessions found".light_yellow
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
rescue JSON::ParserError => e
|
99
|
+
puts "Error parsing sessions response: #{e.message}".red.bold
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
rescue => e
|
103
|
+
puts "Error during session deletion with Basic Auth: #{e.message}".red.bold
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create a Redfish session
|
109
|
+
def create
|
110
|
+
# Skip if we're in direct mode
|
111
|
+
if @direct_mode
|
112
|
+
puts "Skipping Redfish session creation (direct mode)".light_yellow
|
113
|
+
return false
|
114
|
+
end
|
115
|
+
|
116
|
+
url = '/redfish/v1/SessionService/Sessions'
|
117
|
+
payload = { "UserName" => username, "Password" => password }
|
118
|
+
|
119
|
+
# Try creation methods in sequence
|
120
|
+
return true if create_session_with_content_type(url, payload)
|
121
|
+
return true if create_session_with_basic_auth(url, payload)
|
122
|
+
return true if handle_max_sessions_and_retry(url, payload)
|
123
|
+
return true if create_session_with_form_urlencoded(url, payload)
|
124
|
+
|
125
|
+
# If all attempts fail, switch to direct mode
|
126
|
+
@direct_mode = true
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
# Delete the Redfish session
|
131
|
+
def delete
|
132
|
+
return unless @x_auth_token && @session_location
|
133
|
+
|
134
|
+
begin
|
135
|
+
puts "Deleting Redfish session...".light_cyan
|
136
|
+
|
137
|
+
# Use the X-Auth-Token for authentication
|
138
|
+
headers = { 'X-Auth-Token' => @x_auth_token }
|
139
|
+
|
140
|
+
response = connection.delete(@session_location) do |req|
|
141
|
+
req.headers.merge!(headers)
|
142
|
+
end
|
143
|
+
|
144
|
+
if response.status == 200 || response.status == 204
|
145
|
+
puts "Redfish session deleted successfully".green
|
146
|
+
@x_auth_token = nil
|
147
|
+
@session_location = nil
|
148
|
+
return true
|
149
|
+
else
|
150
|
+
puts "Failed to delete Redfish session: #{response.status} - #{response.body}".red
|
151
|
+
return false
|
152
|
+
end
|
153
|
+
rescue => e
|
154
|
+
puts "Error during Redfish session deletion: #{e.message}".red.bold
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def base_url
|
162
|
+
protocol = use_ssl ? 'https' : 'http'
|
163
|
+
"#{protocol}://#{host}:#{port}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def basic_auth_headers
|
167
|
+
{
|
168
|
+
'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
|
169
|
+
'Content-Type' => 'application/json'
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def request_with_basic_auth(method, url, body = nil)
|
174
|
+
connection.send(method, url) do |req|
|
175
|
+
req.headers.merge!(basic_auth_headers)
|
176
|
+
req.body = body if body
|
177
|
+
end
|
178
|
+
rescue => e
|
179
|
+
puts "Error during #{method} request with Basic Auth: #{e.message}".red.bold
|
180
|
+
raise e
|
181
|
+
end
|
182
|
+
|
183
|
+
def process_session_response(response)
|
184
|
+
if response.status == 201 || response.status == 200
|
185
|
+
@x_auth_token = response.headers['X-Auth-Token']
|
186
|
+
@session_location = response.headers['Location']
|
187
|
+
@sessions_maxed = false
|
188
|
+
true
|
189
|
+
else
|
190
|
+
false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def create_session_with_content_type(url, payload)
|
195
|
+
begin
|
196
|
+
response = connection.post(url) do |req|
|
197
|
+
req.headers['Content-Type'] = 'application/json'
|
198
|
+
req.body = payload.to_json
|
199
|
+
end
|
200
|
+
|
201
|
+
if process_session_response(response)
|
202
|
+
puts "Redfish session created successfully".green
|
203
|
+
return true
|
204
|
+
end
|
205
|
+
rescue => e
|
206
|
+
puts "First session creation attempt failed: #{e.message}".light_red
|
207
|
+
end
|
208
|
+
false
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_session_with_basic_auth(url, payload)
|
212
|
+
begin
|
213
|
+
response = request_with_basic_auth(:post, url, payload.to_json)
|
214
|
+
|
215
|
+
if process_session_response(response)
|
216
|
+
puts "Redfish session created successfully with Basic Auth".green
|
217
|
+
return true
|
218
|
+
elsif response.status == 400 && response.body.include?("maximum number of user sessions")
|
219
|
+
puts "Maximum sessions reached during Redfish session creation".light_red
|
220
|
+
@sessions_maxed = true
|
221
|
+
return false
|
222
|
+
else
|
223
|
+
puts "Failed to create Redfish session: #{response.status} - #{response.body}".red
|
224
|
+
return false
|
225
|
+
end
|
226
|
+
rescue => e
|
227
|
+
puts "Error during Redfish session creation with Basic Auth: #{e.message}".red.bold
|
228
|
+
return false
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def handle_max_sessions_and_retry(url, payload)
|
233
|
+
return false unless @sessions_maxed && @auto_delete_sessions
|
234
|
+
|
235
|
+
puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
|
236
|
+
if force_clear_sessions
|
237
|
+
puts "Successfully cleared sessions, trying to create a new session".green
|
238
|
+
|
239
|
+
# Try one more time after clearing
|
240
|
+
response = request_with_basic_auth(:post, url, payload.to_json)
|
241
|
+
|
242
|
+
if process_session_response(response)
|
243
|
+
puts "Redfish session created successfully after clearing sessions".green
|
244
|
+
return true
|
245
|
+
else
|
246
|
+
puts "Failed to create Redfish session after clearing sessions: #{response.status} - #{response.body}".red
|
247
|
+
return false
|
248
|
+
end
|
249
|
+
else
|
250
|
+
puts "Failed to clear sessions, switching to direct mode".light_yellow
|
251
|
+
return false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def create_session_with_form_urlencoded(url, payload)
|
256
|
+
# Only try with form-urlencoded if we had a 415 error previously
|
257
|
+
begin
|
258
|
+
puts "Trying with form-urlencoded content type".light_cyan
|
259
|
+
response = connection.post(url) do |req|
|
260
|
+
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
261
|
+
req.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
|
262
|
+
req.body = "UserName=#{URI.encode_www_form_component(username)}&Password=#{URI.encode_www_form_component(password)}"
|
263
|
+
end
|
264
|
+
|
265
|
+
if process_session_response(response)
|
266
|
+
puts "Redfish session created successfully with form-urlencoded".green
|
267
|
+
return true
|
268
|
+
else
|
269
|
+
puts "Failed with form-urlencoded too: #{response.status} - #{response.body}".red
|
270
|
+
return false
|
271
|
+
end
|
272
|
+
rescue => e
|
273
|
+
puts "Error during form-urlencoded session creation: #{e.message}".red.bold
|
274
|
+
return false
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
data/lib/idrac/version.rb
CHANGED
data/lib/idrac/web.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'uri'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
module IDRAC
|
7
|
+
class Web
|
8
|
+
attr_reader :client, :session_id, :cookies
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
@session_id = nil
|
13
|
+
@cookies = nil
|
14
|
+
@tried_clearing_sessions = false
|
15
|
+
end
|
16
|
+
|
17
|
+
# Login to the WebUI
|
18
|
+
def login(retry_count = 0)
|
19
|
+
# Limit retries to prevent infinite loops
|
20
|
+
if retry_count >= 3
|
21
|
+
puts "Maximum retry count reached for WebUI login".red
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
|
25
|
+
# Skip if we already have a session ID
|
26
|
+
return true if @session_id
|
27
|
+
|
28
|
+
begin
|
29
|
+
puts "Logging in to WebUI...".light_cyan
|
30
|
+
|
31
|
+
# Create the login URL
|
32
|
+
login_url = "#{base_url}/data/login"
|
33
|
+
|
34
|
+
# Create the login payload
|
35
|
+
payload = {
|
36
|
+
'user' => client.username,
|
37
|
+
'password' => client.password
|
38
|
+
}
|
39
|
+
|
40
|
+
# Make the login request
|
41
|
+
response = HTTParty.post(
|
42
|
+
login_url,
|
43
|
+
body: payload,
|
44
|
+
verify: client.verify_ssl,
|
45
|
+
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
46
|
+
)
|
47
|
+
|
48
|
+
# Check if the login was successful
|
49
|
+
if response.code == 200
|
50
|
+
# Extract the session ID from the response
|
51
|
+
if response.body.include?('ST2')
|
52
|
+
@session_id = response.body.match(/ST2=([^;]+)/)[1]
|
53
|
+
@cookies = response.headers['set-cookie']
|
54
|
+
puts "WebUI login successful".green
|
55
|
+
return response.body
|
56
|
+
else
|
57
|
+
puts "WebUI login failed: No session ID found in response".red
|
58
|
+
return false
|
59
|
+
end
|
60
|
+
elsif response.code == 400 && response.body.include?("maximum number of user sessions")
|
61
|
+
puts "Maximum sessions reached during WebUI login".light_red
|
62
|
+
|
63
|
+
# Try to clear sessions if auto_delete_sessions is enabled
|
64
|
+
if client.auto_delete_sessions && !@tried_clearing_sessions
|
65
|
+
puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
|
66
|
+
@tried_clearing_sessions = true
|
67
|
+
|
68
|
+
if client.session.force_clear_sessions
|
69
|
+
puts "Successfully cleared sessions, trying WebUI login again".green
|
70
|
+
return login(retry_count + 1)
|
71
|
+
else
|
72
|
+
puts "Failed to clear sessions for WebUI login".red
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
else
|
76
|
+
puts "Auto-delete sessions is disabled or already tried clearing".light_yellow
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
else
|
80
|
+
puts "WebUI login failed: #{response.code} - #{response.body}".red
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
rescue => e
|
84
|
+
puts "Error during WebUI login: #{e.message}".red.bold
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Logout from the WebUI
|
90
|
+
def logout
|
91
|
+
return unless @session_id
|
92
|
+
|
93
|
+
begin
|
94
|
+
puts "Logging out from WebUI...".light_cyan
|
95
|
+
|
96
|
+
# Create the logout URL
|
97
|
+
logout_url = "#{base_url}/data/logout"
|
98
|
+
|
99
|
+
# Make the logout request
|
100
|
+
response = HTTParty.get(
|
101
|
+
logout_url,
|
102
|
+
verify: client.verify_ssl,
|
103
|
+
headers: { 'Cookie' => @cookies }
|
104
|
+
)
|
105
|
+
|
106
|
+
# Check if the logout was successful
|
107
|
+
if response.code == 200
|
108
|
+
puts "WebUI logout successful".green
|
109
|
+
@session_id = nil
|
110
|
+
@cookies = nil
|
111
|
+
return true
|
112
|
+
else
|
113
|
+
puts "WebUI logout failed: #{response.code} - #{response.body}".red
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
rescue => e
|
117
|
+
puts "Error during WebUI logout: #{e.message}".red.bold
|
118
|
+
return false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Capture a screenshot
|
123
|
+
def capture_screenshot
|
124
|
+
# Login to get the forward URL and cookies
|
125
|
+
forward_url = login
|
126
|
+
return nil unless forward_url
|
127
|
+
|
128
|
+
# Extract the key-value pairs from the forward URL (format: index?ST1=ABC,ST2=DEF)
|
129
|
+
tokens = forward_url.split("?").last.split(",").inject({}) do |acc, kv|
|
130
|
+
k, v = kv.split("=")
|
131
|
+
acc[k] = v
|
132
|
+
acc
|
133
|
+
end
|
134
|
+
|
135
|
+
# Generate a timestamp for the request
|
136
|
+
timestamp_ms = (Time.now.to_f * 1000).to_i
|
137
|
+
|
138
|
+
# First request to trigger the screenshot capture
|
139
|
+
path = "data?get=consolepreview[manual%20#{timestamp_ms}]"
|
140
|
+
res = get(path: path, headers: tokens)
|
141
|
+
raise Error, "Failed to trigger screenshot capture." unless res.code.between?(200, 299)
|
142
|
+
|
143
|
+
# Wait for the screenshot to be generated
|
144
|
+
sleep 2
|
145
|
+
|
146
|
+
# Second request to get the actual screenshot image
|
147
|
+
path = "capconsole/scapture0.png?#{timestamp_ms}"
|
148
|
+
res = get(path: path, headers: tokens)
|
149
|
+
raise Error, "Failed to retrieve screenshot image." unless res.code.between?(200, 299)
|
150
|
+
|
151
|
+
# Save the screenshot to a file
|
152
|
+
filename = "idrac_screenshot_#{timestamp_ms}.png"
|
153
|
+
File.open(filename, "wb") { |f| f.write(res.body) }
|
154
|
+
|
155
|
+
# Return the filename
|
156
|
+
filename
|
157
|
+
end
|
158
|
+
|
159
|
+
# HTTP GET request for WebUI operations
|
160
|
+
def get(path:, headers: {})
|
161
|
+
headers_to_use = {
|
162
|
+
"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",
|
163
|
+
"Accept-Encoding" => "deflate, gzip"
|
164
|
+
}
|
165
|
+
|
166
|
+
if @cookies
|
167
|
+
headers_to_use["Cookie"] = @cookies
|
168
|
+
elsif client.direct_mode
|
169
|
+
# In direct mode, use Basic Auth
|
170
|
+
headers_to_use["Authorization"] = "Basic #{Base64.strict_encode64("#{client.username}:#{client.password}")}"
|
171
|
+
end
|
172
|
+
|
173
|
+
HTTParty.get(
|
174
|
+
"#{base_url}/#{path}",
|
175
|
+
headers: headers_to_use.merge(headers),
|
176
|
+
verify: false
|
177
|
+
)
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def base_url
|
183
|
+
protocol = client.use_ssl ? 'https' : 'http'
|
184
|
+
"#{protocol}://#{client.host}:#{client.port}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/lib/idrac.rb
CHANGED
@@ -12,8 +12,9 @@ require 'debug' if ENV['RUBY_ENV'] == 'development'
|
|
12
12
|
|
13
13
|
require_relative "idrac/version"
|
14
14
|
require_relative "idrac/error"
|
15
|
+
require_relative "idrac/session"
|
16
|
+
require_relative "idrac/web"
|
15
17
|
require_relative "idrac/client"
|
16
|
-
require_relative "idrac/screenshot"
|
17
18
|
require_relative "idrac/firmware"
|
18
19
|
require_relative "idrac/firmware_catalog"
|
19
20
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: idrac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.40
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Siegel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -224,8 +224,9 @@ files:
|
|
224
224
|
- lib/idrac/error.rb
|
225
225
|
- lib/idrac/firmware.rb
|
226
226
|
- lib/idrac/firmware_catalog.rb
|
227
|
-
- lib/idrac/
|
227
|
+
- lib/idrac/session.rb
|
228
228
|
- lib/idrac/version.rb
|
229
|
+
- lib/idrac/web.rb
|
229
230
|
homepage: http://github.com
|
230
231
|
licenses:
|
231
232
|
- MIT
|
@@ -246,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
246
247
|
- !ruby/object:Gem::Version
|
247
248
|
version: '0'
|
248
249
|
requirements: []
|
249
|
-
rubygems_version: 3.
|
250
|
+
rubygems_version: 3.5.16
|
250
251
|
signing_key:
|
251
252
|
specification_version: 4
|
252
253
|
summary: API Client for Dell iDRAC
|
data/lib/idrac/screenshot.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
require 'nokogiri'
|
3
|
-
|
4
|
-
module IDRAC
|
5
|
-
# Reverse engineered screenshot functionality for iDRAC
|
6
|
-
# This uses introspection on how the web UI creates screenshots rather than the Redfish API
|
7
|
-
class Screenshot
|
8
|
-
attr_reader :client
|
9
|
-
|
10
|
-
def initialize(client)
|
11
|
-
@client = client
|
12
|
-
end
|
13
|
-
|
14
|
-
def capture
|
15
|
-
# Login to get the forward URL and cookies
|
16
|
-
forward_url = client.login
|
17
|
-
|
18
|
-
# Extract the key-value pairs from the forward URL (format: index?ST1=ABC,ST2=DEF)
|
19
|
-
tokens = forward_url.split("?").last.split(",").inject({}) do |acc, kv|
|
20
|
-
k, v = kv.split("=")
|
21
|
-
acc[k] = v
|
22
|
-
acc
|
23
|
-
end
|
24
|
-
|
25
|
-
# Generate a timestamp for the request
|
26
|
-
timestamp_ms = (Time.now.to_f * 1000).to_i
|
27
|
-
|
28
|
-
# First request to trigger the screenshot capture
|
29
|
-
path = "data?get=consolepreview[manual%20#{timestamp_ms}]"
|
30
|
-
res = client.get(path: path, headers: tokens)
|
31
|
-
raise Error, "Failed to trigger screenshot capture." unless res.code.between?(200, 299)
|
32
|
-
|
33
|
-
# Wait for the screenshot to be generated
|
34
|
-
sleep 2
|
35
|
-
|
36
|
-
# Second request to get the actual screenshot image
|
37
|
-
path = "capconsole/scapture0.png?#{timestamp_ms}"
|
38
|
-
res = client.get(path: path, headers: tokens)
|
39
|
-
raise Error, "Failed to retrieve screenshot image." unless res.code.between?(200, 299)
|
40
|
-
|
41
|
-
# Save the screenshot to a file
|
42
|
-
filename = "idrac_screenshot_#{timestamp_ms}.png"
|
43
|
-
File.open(filename, "wb") { |f| f.write(res.body) }
|
44
|
-
|
45
|
-
# Return the filename
|
46
|
-
filename
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|