idrac 0.1.20 → 0.1.22

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87d81ae109224d8bfee1a4513bb10d307c703b74ab0d540a78aed849f16e26c4
4
- data.tar.gz: 210a4e787490a16f76994090dc908248b97efec8f7bdb3c8a8136e6aee63ec5d
3
+ metadata.gz: 84cc513a711ebdea97f786857702ff36f607a0787e32dfe9b923f2587fc0f547
4
+ data.tar.gz: f6ff7e6b297064d268c702bbe89c7a6c663a9e0b7b4096bb92ae3ba922731e49
5
5
  SHA512:
6
- metadata.gz: ea89b9514218b74a2393bc5eb46cc121a60ce9c6c9e0726f6c9df38b40dbb2cb5196bbc20350e56662729af77ef278dbabf6c4b61d162ac1a97467e3ef4fb548
7
- data.tar.gz: a4213988f3a7ad827b20f90b8cdb4be43ffed7b20bc58f6dce3b9570b6504d73dfc34f67285f6cdd94515e0ec0a52651b27360c75464e297d0e0a448fbbf6774
6
+ metadata.gz: 40c1af62898da133ddb2ecf7dab1888c4b3b6758c7af912f9cdd3f2f644c089d42dea211c58a0aa5ceb056a04dec9df3cd98cdbe3b664c271aec3e4bc03f686a
7
+ data.tar.gz: b1977e34694d5a5c41d54fe461bcb8c03724e4bdf5a6c59aa08fd6325ddd22c41a50d0e615fbfba86428aaf73b6960f03f6dedf1a4811617d98aa466d04dbfa7
data/README.md CHANGED
@@ -52,6 +52,20 @@ idrac firmware:interactive --host=192.168.1.100 --username=root --password=calvi
52
52
 
53
53
  All commands automatically handle session expiration by re-authenticating when necessary, ensuring that long-running operations like firmware updates complete successfully even if the iDRAC session times out.
54
54
 
55
+ #### Session Management Options
56
+
57
+ By default, the client will automatically delete existing sessions when the maximum session limit is reached. You can control this behavior with the `--auto-delete-sessions` option:
58
+
59
+ ```bash
60
+ # Disable automatic session deletion (use direct mode instead when max sessions reached)
61
+ idrac firmware:status --host=192.168.1.100 --no-auto-delete-sessions
62
+
63
+ # Explicitly enable automatic session deletion (this is the default)
64
+ idrac firmware:status --host=192.168.1.100 --auto-delete-sessions
65
+ ```
66
+
67
+ When `--auto-delete-sessions` is enabled (the default), the client will attempt to delete existing sessions when it encounters a "maximum number of user sessions" error. When disabled, it will switch to direct mode (using Basic Authentication) instead of trying to clear sessions.
68
+
55
69
  ### Ruby API
56
70
 
57
71
  ```ruby
@@ -95,6 +109,14 @@ end
95
109
  # Update firmware
96
110
  job_id = firmware.update('/path/to/firmware.exe', wait: true)
97
111
  puts "Update completed with job ID: #{job_id}"
112
+
113
+ # Create a client with auto_delete_sessions disabled
114
+ client = IDRAC.new(
115
+ host: '192.168.1.100',
116
+ username: 'root',
117
+ password: 'calvin',
118
+ auto_delete_sessions: false
119
+ )
98
120
  ```
99
121
 
100
122
  ## Development
@@ -105,6 +127,21 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
105
127
 
106
128
  ## Changelog
107
129
 
130
+ ### Version 0.1.22
131
+ - **Session Management Control**: Added `--auto-delete-sessions` CLI option (default: true)
132
+ - When enabled, the client automatically deletes existing sessions when maximum session limit is reached
133
+ - When disabled, the client switches to direct mode instead of trying to clear sessions
134
+ - Added detailed logging for session management decisions
135
+ - Updated documentation with examples of how to use the new option
136
+
137
+ ### Version 0.1.21
138
+ - **Improved Authentication Flow**: Completely restructured the login process
139
+ - Renamed `legacy_login` to `webui_login` and limited its use to screenshot functionality only
140
+ - Implemented proper Redfish authentication flow: start with direct login to get a session
141
+ - Enhanced session management: when max sessions are encountered, delete sessions using direct login
142
+ - Simplified code by removing redundant authentication methods and focusing on Redfish standards
143
+ - Improved error handling and logging for better troubleshooting
144
+
108
145
  ### Version 0.1.20
109
146
  - **Simplified CLI Interface**: Removed the direct-mode CLI option
110
147
  - Maintained internal direct mode functionality as an automatic fallback mechanism
data/bin/idrac CHANGED
@@ -19,6 +19,7 @@ module IDRAC
19
19
  class_option :port, type: :numeric, default: 443, desc: "iDRAC port"
20
20
  class_option :no_ssl, type: :boolean, default: false, desc: "Disable SSL"
21
21
  class_option :verify_ssl, type: :boolean, default: false, desc: "Enable SSL verification (not recommended for iDRAC's self-signed certificates)"
22
+ class_option :auto_delete_sessions, type: :boolean, default: true, desc: "Automatically delete sessions when maximum sessions are reached (default: true)"
22
23
 
23
24
  desc "firmware:update PATH", "Update firmware using the specified file"
24
25
  method_option :wait, type: :boolean, default: true, desc: "Wait for the update to complete"
@@ -191,7 +192,8 @@ module IDRAC
191
192
  password: options[:password],
192
193
  port: options[:port],
193
194
  use_ssl: !options[:no_ssl],
194
- verify_ssl: options[:verify_ssl]
195
+ verify_ssl: options[:verify_ssl],
196
+ auto_delete_sessions: options[:auto_delete_sessions]
195
197
  )
196
198
  end
197
199
  end
data/lib/idrac/client.rb CHANGED
@@ -8,10 +8,10 @@ require 'json'
8
8
 
9
9
  module IDRAC
10
10
  class Client
11
- attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl
11
+ attr_reader :host, :username, :password, :port, :use_ssl, :verify_ssl, :auto_delete_sessions
12
12
  attr_accessor :direct_mode
13
13
 
14
- def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true, direct_mode: false)
14
+ def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true, direct_mode: false, auto_delete_sessions: true)
15
15
  @host = host
16
16
  @username = username
17
17
  @password = password
@@ -24,6 +24,7 @@ module IDRAC
24
24
  @direct_mode = direct_mode
25
25
  @sessions_maxed = false
26
26
  @tried_clearing_sessions = false
27
+ @auto_delete_sessions = auto_delete_sessions
27
28
  end
28
29
 
29
30
  def connection
@@ -34,58 +35,18 @@ module IDRAC
34
35
  end
35
36
  end
36
37
 
37
- # Force clear all sessions by attempting multiple login/logout cycles
38
+ # Force clear all sessions by directly using Basic Auth
38
39
  def force_clear_sessions
39
40
  puts "Attempting to force clear all sessions..."
40
41
 
41
- # First try to delete sessions directly using Basic Auth
42
+ # Try to delete sessions directly using Basic Auth
42
43
  if delete_all_sessions_with_basic_auth
43
44
  puts "Successfully cleared sessions using Basic Auth"
44
45
  return true
46
+ else
47
+ puts "Failed to clear sessions using Basic Auth"
48
+ return false
45
49
  end
46
-
47
- # If direct deletion fails, try the login/logout cycle approach
48
- puts "Falling back to login/logout cycle approach for clearing sessions..."
49
-
50
- # Try multiple login/logout cycles with both methods
51
- 3.times do |i|
52
- begin
53
- # Try Redfish session first
54
- begin
55
- if create_redfish_session
56
- puts "Successfully created Redfish session (attempt #{i+1})"
57
- delete_redfish_session
58
- puts "Deleted Redfish session (attempt #{i+1})"
59
- sleep(2)
60
- end
61
- rescue => e
62
- puts "Error during Redfish session cycle: #{e.message}"
63
- end
64
-
65
- # Then try legacy session
66
- begin
67
- legacy_login(i)
68
- puts "Successfully created legacy session (attempt #{i+1})"
69
- legacy_logout
70
- puts "Deleted legacy session (attempt #{i+1})"
71
- sleep(2)
72
- rescue => e
73
- # If we get "maximum sessions" error, that's expected
74
- if e.message.include?("maximum number of user sessions")
75
- puts "Maximum sessions error during legacy login (expected during clearing)"
76
- else
77
- puts "Error during legacy session cycle: #{e.message}"
78
- end
79
- end
80
-
81
- # Add a longer delay between cycles
82
- sleep(3) if i < 2
83
- rescue => e
84
- puts "Error during session clearing cycle #{i+1}: #{e.message}"
85
- end
86
- end
87
-
88
- puts "Completed session clearing attempts"
89
50
  end
90
51
 
91
52
  # Delete all sessions using Basic Authentication
@@ -158,19 +119,25 @@ module IDRAC
158
119
  end
159
120
  end
160
121
 
161
- # Create a Redfish session (preferred method for all Redfish API calls)
122
+ # Create a Redfish session
162
123
  def create_redfish_session
163
- # Skip if we're in direct mode or we know sessions are maxed out
164
- if @direct_mode || @sessions_maxed
165
- puts "Skipping Redfish session creation (direct mode or sessions maxed)"
124
+ # Skip if we're in direct mode
125
+ if @direct_mode
126
+ puts "Skipping Redfish session creation (direct mode)"
166
127
  return false
167
128
  end
168
129
 
169
130
  url = '/redfish/v1/SessionService/Sessions'
170
131
  payload = { "UserName" => username, "Password" => password }
171
132
 
133
+ # Use Basic Auth for the initial session creation
134
+ basic_auth_headers = {
135
+ 'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
136
+ 'Content-Type' => 'application/json'
137
+ }
138
+
172
139
  response = connection.post(url) do |req|
173
- req.headers['Content-Type'] = 'application/json'
140
+ req.headers.merge!(basic_auth_headers)
174
141
  req.body = payload.to_json
175
142
  end
176
143
 
@@ -188,31 +155,37 @@ module IDRAC
188
155
  puts "Maximum sessions reached during Redfish session creation"
189
156
  @sessions_maxed = true
190
157
 
191
- # Only try to clear sessions if we haven't tried too many times
192
- if !@tried_clearing_sessions
193
- @tried_clearing_sessions = true
194
- force_clear_sessions
195
-
196
- # Try one more time after clearing
197
- response = connection.post(url) do |req|
198
- req.headers['Content-Type'] = 'application/json'
199
- req.body = payload.to_json
200
- end
201
-
202
- if response.status == 201 || response.status == 200
203
- @x_auth_token = response.headers['X-Auth-Token']
204
- @session_location = response.headers['Location']
205
- puts "Redfish session created successfully after clearing sessions"
206
- @sessions_maxed = false
207
- return true
158
+ # Try to clear sessions if auto_delete_sessions is enabled
159
+ if @auto_delete_sessions
160
+ puts "Auto-delete sessions is enabled, attempting to clear sessions"
161
+ if force_clear_sessions
162
+ puts "Successfully cleared sessions, trying to create a new session"
163
+
164
+ # Try one more time after clearing
165
+ response = connection.post(url) do |req|
166
+ req.headers.merge!(basic_auth_headers)
167
+ req.body = payload.to_json
168
+ end
169
+
170
+ if response.status == 201 || response.status == 200
171
+ @x_auth_token = response.headers['X-Auth-Token']
172
+ @session_location = response.headers['Location']
173
+ puts "Redfish session created successfully after clearing sessions"
174
+ @sessions_maxed = false
175
+ return true
176
+ else
177
+ puts "Failed to create Redfish session after clearing: #{response.status} - #{response.body}"
178
+ # If we still can't create a session, switch to direct mode
179
+ @direct_mode = true
180
+ return false
181
+ end
208
182
  else
209
- puts "Failed to create Redfish session after clearing: #{response.status} - #{response.body}"
210
- # If we still can't create a session, switch to direct mode
183
+ puts "Failed to clear sessions, switching to direct mode"
211
184
  @direct_mode = true
212
185
  return false
213
186
  end
214
187
  else
215
- # If we've already tried clearing sessions, switch to direct mode
188
+ puts "Auto-delete sessions is disabled, switching to direct mode"
216
189
  @direct_mode = true
217
190
  return false
218
191
  end
@@ -244,24 +217,13 @@ module IDRAC
244
217
  end
245
218
  end
246
219
 
247
- # Legacy login method for screenshot functionality
248
- def legacy_login(retry_count = 0)
249
- # Skip if we're in direct mode
250
- if @direct_mode
251
- puts "Skipping legacy login (direct mode)"
252
- return false
253
- end
254
-
220
+ # WebUI login method for screenshot functionality only
221
+ def webui_login(retry_count = 0)
255
222
  # Limit retries to prevent infinite loops
256
223
  if retry_count >= 3
257
- # If we've tried too many times, switch to direct mode
258
- @direct_mode = true
259
- raise Error, "Failed to login after multiple attempts due to maximum sessions limit"
224
+ raise Error, "Failed to login to WebUI after multiple attempts"
260
225
  end
261
226
 
262
- # Always try to logout first to clear any existing sessions
263
- legacy_logout if retry_count > 0
264
-
265
227
  response = connection.post('/data/login') do |req|
266
228
  req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
267
229
  req.body = "user=#{username}&password=#{password}"
@@ -282,13 +244,17 @@ module IDRAC
282
244
  if error_message && !error_message.empty?
283
245
  # Check for maximum sessions error
284
246
  if error_message.include?("maximum number of user sessions")
285
- puts "Maximum sessions reached, attempting to clear sessions (attempt #{retry_count + 1}/3)..."
286
- # Try to clear any existing sessions by forcing a logout
287
- legacy_logout
288
- # Wait longer for the server to process the logout
289
- sleep(5)
290
- # Try logging in again with incremented retry counter
291
- return legacy_login(retry_count + 1)
247
+ puts "Maximum sessions reached for WebUI, attempting to clear sessions (attempt #{retry_count + 1}/3)..."
248
+ # Try to clear any existing sessions if auto_delete_sessions is enabled
249
+ if @auto_delete_sessions
250
+ force_clear_sessions
251
+ # Wait for the server to process the session changes
252
+ sleep(3)
253
+ # Try logging in again with incremented retry counter
254
+ return webui_login(retry_count + 1)
255
+ else
256
+ raise Error, "Maximum sessions reached and auto-delete sessions is disabled"
257
+ end
292
258
  end
293
259
 
294
260
  raise Error, "Error Message: #{error_message}"
@@ -297,27 +263,21 @@ module IDRAC
297
263
  forward_url = xml_doc.xpath('//forwardUrl').text
298
264
  return forward_url
299
265
  else
300
- raise Error, "Login failed with status #{response.status}: #{response.body}"
266
+ raise Error, "WebUI login failed with status #{response.status}: #{response.body}"
301
267
  end
302
268
  end
303
269
 
304
- # Legacy logout method for screenshot functionality
305
- def legacy_logout
270
+ # WebUI logout method for screenshot functionality
271
+ def webui_logout
306
272
  return unless @session_id
307
273
 
308
274
  begin
309
- # Try multiple logout attempts to ensure session is cleared
310
- 3.times do |i|
311
- response = connection.get('/data/logout') do |req|
312
- req.headers['Cookie'] = "sessionid=#{@session_id}"
313
- end
314
-
315
- break if response.status == 200
316
- sleep(1) if i < 2 # Sleep between attempts, but not after the last one
275
+ response = connection.get('/data/logout') do |req|
276
+ req.headers['Cookie'] = "sessionid=#{@session_id}"
317
277
  end
318
278
  rescue => e
319
279
  # Ignore errors during logout
320
- puts "Warning: Error during logout: #{e.message}"
280
+ puts "Warning: Error during WebUI logout: #{e.message}"
321
281
  ensure
322
282
  @session_id = nil
323
283
  @cookies = nil
@@ -326,7 +286,7 @@ module IDRAC
326
286
  true
327
287
  end
328
288
 
329
- # Main login method that decides which approach to use
289
+ # Main login method
330
290
  def login
331
291
  # If we're in direct mode, skip login attempts
332
292
  if @direct_mode
@@ -334,40 +294,29 @@ module IDRAC
334
294
  return true
335
295
  end
336
296
 
337
- # First try to create a Redfish session
297
+ # Try to create a Redfish session
338
298
  if create_redfish_session
339
299
  return true
340
300
  else
341
- # If we failed to create a Redfish session, try to clear sessions first
342
- if !@tried_clearing_sessions
343
- @tried_clearing_sessions = true
344
- force_clear_sessions
345
-
346
- # Try Redfish again
347
- if create_redfish_session
348
- return true
349
- end
350
- end
351
-
352
- # Fall back to legacy login if Redfish session creation fails
353
- if !@direct_mode
354
- puts "Falling back to legacy login method"
355
- return legacy_login
356
- else
301
+ # If we failed to create a Redfish session and direct_mode is set, just return
302
+ if @direct_mode
357
303
  puts "Operating in direct mode (no session)"
358
304
  return true
305
+ else
306
+ # If we're here, something went wrong with session creation
307
+ raise Error, "Failed to create a session and direct mode is not enabled"
359
308
  end
360
309
  end
361
310
  end
362
311
 
363
- # Main logout method that decides which approach to use
312
+ # Main logout method
364
313
  def logout
365
314
  if @x_auth_token
366
315
  delete_redfish_session
367
316
  end
368
317
 
369
318
  if @session_id
370
- legacy_logout
319
+ webui_logout
371
320
  end
372
321
 
373
322
  true
@@ -384,23 +333,15 @@ module IDRAC
384
333
  # Ensure we have a valid session (unless in direct mode)
385
334
  unless @direct_mode
386
335
  begin
387
- login unless @x_auth_token || @session_id
336
+ login unless @x_auth_token
388
337
  rescue Error => e
389
- # If login fails with maximum sessions error, try to handle it
390
- if e.message.include?("maximum number of user sessions") && retry_count < 2
391
- puts "Maximum sessions reached during initial login, attempting to clear sessions (attempt #{retry_count + 1}/3)..."
392
- logout
393
- sleep(5) # Longer delay
394
- return authenticated_request(method, path, options, retry_count + 1)
338
+ # If login fails and we've tried too many times, switch to direct mode
339
+ if retry_count >= 2
340
+ @direct_mode = true
341
+ puts "Switching to direct mode after multiple authentication failures"
395
342
  else
396
- # If we've tried too many times, switch to direct mode
397
- if retry_count >= 2
398
- @direct_mode = true
399
- puts "Switching to direct mode after multiple authentication failures"
400
- else
401
- # Re-raise other errors or if we've tried too many times
402
- raise
403
- end
343
+ # Re-raise other errors
344
+ raise
404
345
  end
405
346
  end
406
347
  end
@@ -410,9 +351,6 @@ module IDRAC
410
351
  # Use X-Auth-Token if available (preferred Redfish method)
411
352
  if @x_auth_token
412
353
  options[:headers]['X-Auth-Token'] = @x_auth_token
413
- elsif @session_id
414
- # Fall back to session cookie if needed
415
- options[:headers]['Cookie'] = "sessionid=#{@session_id}"
416
354
  elsif @direct_mode
417
355
  # In direct mode, use Basic Auth
418
356
  options[:headers]['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
@@ -427,15 +365,13 @@ module IDRAC
427
365
  if response.status == 401 && !@direct_mode
428
366
  puts "Session expired, re-authenticating..."
429
367
  logout
430
- sleep(3)
368
+ sleep(2)
431
369
  login
432
370
 
433
371
  # Update headers with new authentication
434
372
  options[:headers] ||= {}
435
373
  if @x_auth_token
436
374
  options[:headers]['X-Auth-Token'] = @x_auth_token
437
- elsif @session_id
438
- options[:headers]['Cookie'] = "sessionid=#{@session_id}"
439
375
  elsif @direct_mode
440
376
  options[:headers]['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
441
377
  end
@@ -453,31 +389,13 @@ module IDRAC
453
389
  end
454
390
  end
455
391
 
456
- # Check for maximum sessions error in the response
457
- if response.status == 200
458
- begin
459
- xml_doc = Nokogiri::XML(response.body)
460
- error_message = xml_doc.at_xpath('//errorMsg')&.text
461
-
462
- if error_message && error_message.include?("maximum number of user sessions") && retry_count < 2
463
- puts "Maximum sessions reached, clearing sessions and retrying (attempt #{retry_count + 1}/3)..."
464
- logout
465
- sleep(5)
466
- login
467
- return authenticated_request(method, path, options, retry_count + 1)
468
- end
469
- rescue => e
470
- # If we can't parse the response as XML, just continue
471
- end
472
- end
473
-
474
392
  response
475
393
  end
476
394
 
477
395
  def get(path:, headers: {})
478
- # For screenshot functionality, we need to use the legacy cookies
479
- if @cookies.nil? && !@direct_mode
480
- legacy_login unless @session_id
396
+ # For screenshot functionality, we need to use the WebUI cookies
397
+ if @cookies.nil? && path.include?('screen/screen.jpg')
398
+ webui_login unless @session_id
481
399
  end
482
400
 
483
401
  headers_to_use = {
@@ -490,6 +408,8 @@ module IDRAC
490
408
  elsif @direct_mode
491
409
  # In direct mode, use Basic Auth
492
410
  headers_to_use["Authorization"] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
411
+ elsif @x_auth_token
412
+ headers_to_use["X-Auth-Token"] = @x_auth_token
493
413
  end
494
414
 
495
415
  response = HTTParty.get(
data/lib/idrac/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IDRAC
4
- VERSION = "0.1.20"
4
+ VERSION = "0.1.22"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: idrac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.20
4
+ version: 0.1.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Siegel