idrac 0.1.31 → 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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -107
  3. data/bin/idrac +130 -15
  4. data/idrac.gemspec +9 -8
  5. data/lib/idrac/client.rb +22 -383
  6. data/lib/idrac/session.rb +278 -0
  7. data/lib/idrac/version.rb +1 -1
  8. data/lib/idrac/web.rb +187 -0
  9. data/lib/idrac.rb +3 -1
  10. metadata +7 -39
  11. data/.rspec +0 -3
  12. data/Rakefile +0 -17
  13. data/dell_firmware_downloads/Catalog.etag +0 -1
  14. data/dell_firmware_downloads/Catalog.xml +0 -0
  15. data/idrac-0.1.6/.rspec +0 -3
  16. data/idrac-0.1.6/README.md +0 -103
  17. data/idrac-0.1.6/Rakefile +0 -17
  18. data/idrac-0.1.6/bin/console +0 -11
  19. data/idrac-0.1.6/bin/idrac +0 -179
  20. data/idrac-0.1.6/bin/setup +0 -8
  21. data/idrac-0.1.6/idrac.gemspec +0 -51
  22. data/idrac-0.1.6/lib/idrac/client.rb +0 -109
  23. data/idrac-0.1.6/lib/idrac/firmware.rb +0 -366
  24. data/idrac-0.1.6/lib/idrac/version.rb +0 -5
  25. data/idrac-0.1.6/lib/idrac.rb +0 -30
  26. data/idrac-0.1.6/sig/idrac.rbs +0 -4
  27. data/idrac-0.1.7/.rspec +0 -3
  28. data/idrac-0.1.7/README.md +0 -103
  29. data/idrac-0.1.7/Rakefile +0 -17
  30. data/idrac-0.1.7/bin/console +0 -11
  31. data/idrac-0.1.7/bin/idrac +0 -179
  32. data/idrac-0.1.7/bin/setup +0 -8
  33. data/idrac-0.1.7/idrac.gemspec +0 -51
  34. data/idrac-0.1.7/lib/idrac/client.rb +0 -109
  35. data/idrac-0.1.7/lib/idrac/firmware.rb +0 -366
  36. data/idrac-0.1.7/lib/idrac/screenshot.rb +0 -49
  37. data/idrac-0.1.7/lib/idrac/version.rb +0 -5
  38. data/idrac-0.1.7/lib/idrac.rb +0 -30
  39. data/idrac-0.1.7/sig/idrac.rbs +0 -4
  40. data/idrac.py +0 -500
  41. data/lib/idrac/screenshot.rb +0 -49
  42. data/sig/idrac.rbs +0 -4
  43. data/test_firmware_update.rb +0 -68
  44. data/updater.rb +0 -729
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 create_redfish_session
47
+ if session.create
396
48
  puts "Successfully logged in to iDRAC using Redfish session".green
397
49
  return true
398
50
  else
399
- if @sessions_maxed
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 @x_auth_token
413
- delete_redfish_session
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 @x_auth_token
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'] = @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 create_redfish_session
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 create_redfish_session
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 create_redfish_session
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 @cookies.nil? && path.include?('screen/screen.jpg')
513
- webui_login unless @session_id
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 @cookies
522
- headers_to_use["Cookie"] = @cookies
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 @x_auth_token
527
- headers_to_use["X-Auth-Token"] = @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
- response = HTTParty.get(
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
- # Create a Screenshot instance and capture a screenshot
539
- screenshot_instance = Screenshot.new(self)
540
- screenshot_instance.capture
179
+ web.capture_screenshot
541
180
  end
542
181
 
543
182
  def base_url