idrac 0.9.4 → 0.9.7

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: 54237d0e5e104838b8770088791b1896b11928d0092572035ee05117de1d6733
4
- data.tar.gz: 802a048dcf46d8d9a1eab4626be4425269ee58d0012b61f1ac167b44a8b1def9
3
+ metadata.gz: cc0edda7bbcbf4e86782d914c909557f3bf6f24e5fa40284761f0e8d550c18b5
4
+ data.tar.gz: c8e3b1fa48d0eba346ab6d3410894e7ecce6c211c5f8719653c99bb384222e9e
5
5
  SHA512:
6
- metadata.gz: 95ff7c2f6e83194632b4ceb9febd251f01332f39271c02bfc2e0f35e677159b873b7ff74a8906f5b293af855aa2fb3f69ed4e73242be8ca704f6346da507c9a8
7
- data.tar.gz: 94f8e2b5ab66ff0cf428bdb1903d3ba3d38d9b3e2002323e36a866ffb4461ae22a2766861550d4daf0fe74b274e2991ef9280fd9732581af9d0b86d3209b7047
6
+ metadata.gz: '059e0a01815b8b138f247a9a2698a415f342decd78f3a3bc2c028215a0e1d6b9bc415f3ba1e4b2badb53d598910061072e9016ca491d9bf47e63bb3935ef1c1d'
7
+ data.tar.gz: a72e06912c09d645b45ffa5ecb52c703489729a0c8cac87b7cef55117802b396138624f9171318d222eb56ed0defd9442b4d3a5e14d7874a62c0e57d194fa837
data/lib/idrac/client.rb CHANGED
@@ -165,34 +165,33 @@ module IDRAC
165
165
  end
166
166
  end
167
167
 
168
+ # GET that returns parsed JSON on 200, nil on error. Never raises.
169
+ # Use for exploratory/optional endpoints that may not exist.
170
+ def safe_get(path)
171
+ response = authenticated_request(:get, path) { |r| r }
172
+ return nil unless response.status == 200
173
+ JSON.parse(response.body)
174
+ rescue
175
+ nil
176
+ end
177
+
168
178
  private
169
179
 
170
- # Implementation of authenticated request without retry logic
171
- def _perform_authenticated_request(method, path, options = {}, retry_count = 0)
172
- # Check retry count to prevent infinite recursion
173
- if retry_count >= @retry_count
174
- debug "Maximum retry count reached", 1, :red
175
- raise Error, "Failed to authenticate after #{@retry_count} retries"
176
- end
177
-
180
+ # Single-attempt authenticated request. Retry logic is in with_retries.
181
+ def _perform_authenticated_request(method, path, options = {})
178
182
  debug "Authenticated request: #{method.to_s.upcase} #{path}", 1
179
-
180
- # Extract options and prepare headers
183
+
181
184
  body = options[:body]
182
185
  headers = options[:headers] || {}
183
186
  timeout = options[:timeout]
184
187
  open_timeout = options[:open_timeout]
185
-
188
+
186
189
  headers['User-Agent'] ||= 'iDRAC Ruby Client'
187
190
  headers['Accept'] ||= 'application/json'
188
191
  headers['Host'] = @host_header if @host_header
189
-
190
- # Debug the body being sent
191
- if body && @verbosity >= 2
192
- debug "Request body: #{body}", 2
193
- end
194
-
195
- # Determine authentication method and set headers
192
+
193
+ debug "Request body: #{body}", 2 if body && @verbosity >= 2
194
+
196
195
  if @direct_mode
197
196
  headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
198
197
  debug "Using Basic Auth for request (direct mode)", 2
@@ -200,22 +199,22 @@ module IDRAC
200
199
  headers['X-Auth-Token'] = session.x_auth_token
201
200
  debug "Using X-Auth-Token for authentication", 2
202
201
  end
203
-
204
- # Make request with timeout handling
202
+
205
203
  response = make_request_with_timeouts(method, path, body, headers, timeout, open_timeout)
206
-
207
- # Handle authentication and connection errors
204
+
208
205
  case response.status
209
206
  when 401, 403
210
- handle_auth_failure(method, path, options, retry_count)
207
+ handle_auth_failure(method, path, options, nil)
211
208
  else
212
209
  debug "Response status: #{response.status}", 2
213
210
  response
214
211
  end
215
212
  rescue Faraday::ConnectionFailed, Faraday::TimeoutError, Faraday::SSLError => e
216
- handle_connection_error(e, method, path, options, retry_count)
213
+ handle_connection_error(e, method, path, options, nil)
214
+ rescue IDRAC::Error
215
+ raise
217
216
  rescue => e
218
- handle_general_error(e, method, path, options, retry_count)
217
+ handle_general_error(e, method, path, options, nil)
219
218
  end
220
219
 
221
220
  # Make request with timeout handling
@@ -241,56 +240,29 @@ module IDRAC
241
240
  end
242
241
  end
243
242
 
244
- # Handle authentication failures
243
+ # Handle authentication failures — recreate session, then raise so with_retries retries
245
244
  def handle_auth_failure(method, path, options, retry_count)
246
- if @direct_mode
247
- debug "Authentication failed in direct mode, retrying...", 1, :light_yellow
248
- sleep(retry_count + 1)
249
- return _perform_authenticated_request(method, path, options, retry_count + 1)
250
- else
251
- debug "Session expired, creating new session...", 1, :light_yellow
252
- session.delete if session.x_auth_token
253
-
254
- if session.create
255
- debug "New session created, retrying request...", 1, :green
256
- return _perform_authenticated_request(method, path, options, retry_count + 1)
257
- else
258
- debug "Session creation failed, falling back to direct mode...", 1, :light_yellow
259
- @direct_mode = true
260
- return _perform_authenticated_request(method, path, options, retry_count + 1)
261
- end
245
+ debug "Authentication failed (401/403), recreating session...", 1, :light_yellow
246
+ session.delete if session.x_auth_token
247
+
248
+ unless session.create
249
+ debug "Session creation failed, falling back to direct mode", 1, :light_yellow
250
+ @direct_mode = true
262
251
  end
252
+
253
+ raise Error, "Authentication failed, session recreated"
263
254
  end
264
255
 
265
- # Handle connection errors
256
+ # Handle connection errors — raise so with_retries can handle retry logic
266
257
  def handle_connection_error(error, method, path, options, retry_count)
267
258
  debug "Connection error: #{error.message}", 1, :red
268
- sleep(retry_count + 1)
269
-
270
- if @direct_mode || session.x_auth_token
271
- return _perform_authenticated_request(method, path, options, retry_count + 1)
272
- elsif session.create
273
- debug "Created new session after connection error", 1, :green
274
- return _perform_authenticated_request(method, path, options, retry_count + 1)
275
- else
276
- @direct_mode = true
277
- return _perform_authenticated_request(method, path, options, retry_count + 1)
278
- end
259
+ raise Error, "Connection failed: #{error.message}"
279
260
  end
280
-
281
- # Handle general errors
261
+
262
+ # Handle general errors — raise so with_retries can handle retry logic
282
263
  def handle_general_error(error, method, path, options, retry_count)
283
264
  debug "Error during request: #{error.message}", 1, :red
284
-
285
- if @direct_mode
286
- raise Error, "Error during authenticated request: #{error.message}"
287
- elsif session.create
288
- debug "Created new session after error, retrying...", 1, :green
289
- return _perform_authenticated_request(method, path, options, retry_count + 1)
290
- else
291
- @direct_mode = true
292
- return _perform_authenticated_request(method, path, options, retry_count + 1)
293
- end
265
+ raise Error, "Request failed: #{error.message}"
294
266
  end
295
267
 
296
268
  def _perform_get(path:, headers: {})
data/lib/idrac/license.rb CHANGED
@@ -26,15 +26,6 @@ module IDRAC
26
26
 
27
27
  private
28
28
 
29
- # GET that returns parsed JSON on 200, nil otherwise. Never raises on 4xx.
30
- def safe_get(path)
31
- response = authenticated_request(:get, path) { |r| r }
32
- return nil unless response.status == 200
33
- JSON.parse(response.body)
34
- rescue JSON::ParserError
35
- nil
36
- end
37
-
38
29
  def compute_license_version
39
30
  license = license_info
40
31
  if license
data/lib/idrac/system.rb CHANGED
@@ -314,161 +314,80 @@ module IDRAC
314
314
 
315
315
  # Get PCI device information
316
316
  def pci_devices
317
- # First try the standard PCIeDevices endpoint
318
- response = authenticated_request(:get, "/redfish/v1/Chassis/System.Embedded.1/PCIeDevices?$expand=*($levels=1)")
319
-
320
- if response.status == 200
321
- begin
322
- data = JSON.parse(response.body)
323
-
324
- pci = data["Members"].map do |stub|
325
- manufacturer = stub["Manufacturer"]
326
-
327
- # Get PCIe function details if available
328
- pcie_function = nil
329
- if stub.dig("Links", "PCIeFunctions", 0, "@odata.id")
330
- pcie_function_path = stub.dig("Links", "PCIeFunctions", 0, "@odata.id").split("v1/").last
331
- function_response = authenticated_request(:get, "/redfish/v1/#{pcie_function_path}")
332
-
333
- if function_response.status == 200
334
- pcie_function = JSON.parse(function_response.body)
335
- end
336
- end
337
-
338
- # Create device info with available data
339
- device_info = {
340
- "device_class" => pcie_function ? pcie_function["DeviceClass"] : nil,
341
- "manufacturer" => manufacturer,
342
- "name" => stub["Name"],
343
- "description" => stub["Description"],
344
- "id" => pcie_function ? pcie_function["Id"] : stub["Id"],
345
- "slot_type" => pcie_function ? pcie_function.dig("Oem", "Dell", "DellPCIeFunction", "SlotType") : nil,
346
- "bus_width" => pcie_function ? pcie_function.dig("Oem", "Dell", "DellPCIeFunction", "DataBusWidth") : nil,
347
- "nic" => pcie_function ? pcie_function.dig("Links", "EthernetInterfaces", 0, "@odata.id") : nil
348
- }
349
-
350
- puts "PCI Device: #{device_info["name"]} > #{device_info["manufacturer"]} > #{device_info["device_class"]} > #{device_info["description"]} > #{device_info["id"]}"
351
-
352
- device_info
353
- end
354
-
355
- return pci
356
- rescue JSON::ParserError
357
- raise Error, "Failed to parse PCI devices response: #{response.body}"
358
- end
359
- else
360
- # For iDRAC 8, try Dell's recommended approach using System endpoint with PCIeDevices select option
361
- system_pcie_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1?$select=PCIeDevices")
362
-
363
- if system_pcie_response.status == 200
364
- begin
365
- system_data = JSON.parse(system_pcie_response.body)
366
-
367
- if system_data.key?("PCIeDevices") && !system_data["PCIeDevices"].empty?
368
- pci_devices = []
369
-
370
- # Process each PCIe device
371
- system_data["PCIeDevices"].each do |device_link|
372
- if device_link.is_a?(Hash) && device_link["@odata.id"]
373
- device_path = device_link["@odata.id"]
374
- device_response = authenticated_request(:get, device_path)
375
-
376
- if device_response.status == 200
377
- device_data = JSON.parse(device_response.body)
378
-
379
- pci_devices << {
380
- "device_class" => device_data["DeviceType"] || "Unknown",
381
- "manufacturer" => device_data["Manufacturer"],
382
- "name" => device_data["Name"] || device_data["Id"],
383
- "description" => device_data["Description"],
384
- "id" => device_data["Id"],
385
- "slot_type" => device_data.dig("Oem", "Dell", "SlotType"),
386
- "bus_width" => device_data.dig("Oem", "Dell", "BusWidth"),
387
- "nic" => nil
388
- }
389
- end
390
- end
391
- end
392
-
393
- return pci_devices unless pci_devices.empty?
394
- end
395
- rescue JSON::ParserError
396
- # Continue to next approach
397
- end
398
- end
399
-
400
- # Try NetworkAdapters as an alternative for finding PCIe devices (especially NICs and FC adapters)
401
- nic_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/NetworkAdapters?$expand=*($levels=1)")
402
-
403
- if nic_response.status == 200
404
- begin
405
- nic_data = JSON.parse(nic_response.body)
406
-
407
- pci_devices = []
408
-
409
- # Extract PCI info from network adapters
410
- if nic_data["Members"] && !nic_data["Members"].empty?
411
- nic_data["Members"].each do |adapter|
412
- next unless adapter["Model"] || adapter["Manufacturer"]
413
-
414
- # Check if this is a Fiber Channel adapter by name or model
415
- is_fc = (adapter["Name"] =~ /FC/i || adapter["Model"] =~ /FC/i ||
416
- adapter["Id"] =~ /FC/i || adapter["Description"] =~ /Fibre/i) ? true : false
417
-
418
- device_class = is_fc ? "FibreChannelController" : "NetworkController"
419
-
420
- pci_devices << {
421
- "device_class" => device_class,
422
- "manufacturer" => adapter["Manufacturer"],
423
- "name" => adapter["Name"] || adapter["Id"],
424
- "description" => adapter["Description"],
425
- "id" => adapter["Id"],
426
- "slot_type" => adapter.dig("Oem", "Dell", "SlotType") ||
427
- (adapter["Id"] =~ /Slot\.(\d+)/ ? "Slot #{$1}" : nil),
428
- "bus_width" => nil,
429
- "nic" => adapter["@odata.id"]
430
- }
431
- end
432
-
433
- return pci_devices unless pci_devices.empty?
434
- end
435
- rescue JSON::ParserError
436
- # Continue to fallback
437
- end
438
- end
439
-
440
- # Last resort: check if PCIeFunctions are directly available
441
- pcie_functions_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/PCIeFunctions?$expand=*($levels=1)")
442
-
443
- if pcie_functions_response.status == 200
444
- begin
445
- functions_data = JSON.parse(pcie_functions_response.body)
446
-
447
- if functions_data["Members"] && !functions_data["Members"].empty?
448
- pci_devices = functions_data["Members"].map do |function|
449
- {
450
- "device_class" => function["DeviceClass"] || "Unknown",
451
- "manufacturer" => function["Manufacturer"] || "Unknown",
452
- "name" => function["Name"] || function["Id"],
453
- "description" => function["Description"],
454
- "id" => function["Id"],
455
- "slot_type" => function.dig("Oem", "Dell", "SlotType"),
456
- "bus_width" => function.dig("Oem", "Dell", "DataBusWidth"),
457
- "nic" => nil
458
- }
459
- end
460
-
461
- return pci_devices
462
- end
463
- rescue JSON::ParserError
464
- # Continue to fallback
465
- end
466
- end
467
-
468
- # Fallback for any version when all endpoints unavailable
469
- puts "PCI device information not available through standard or alternative endpoints" if @verbose
470
- return []
317
+ # iDRAC 9+: standard PCIeDevices endpoint with expand
318
+ if (data = safe_get("/redfish/v1/Chassis/System.Embedded.1/PCIeDevices?$expand=*($levels=1)"))
319
+ return data["Members"].map { |stub|
320
+ fn = stub.dig("Links", "PCIeFunctions", 0, "@odata.id")
321
+ pcie_fn = fn ? safe_get(fn) : nil
322
+ {
323
+ "device_class" => pcie_fn&.dig("DeviceClass"),
324
+ "manufacturer" => stub["Manufacturer"],
325
+ "name" => stub["Name"],
326
+ "description" => stub["Description"],
327
+ "id" => pcie_fn&.dig("Id") || stub["Id"],
328
+ "slot_type" => pcie_fn&.dig("Oem", "Dell", "DellPCIeFunction", "SlotType"),
329
+ "bus_width" => pcie_fn&.dig("Oem", "Dell", "DellPCIeFunction", "DataBusWidth"),
330
+ "nic" => pcie_fn&.dig("Links", "EthernetInterfaces", 0, "@odata.id")
331
+ }
332
+ }
333
+ end
334
+
335
+ # iDRAC 8: PCIeDevices via System endpoint
336
+ if (data = safe_get("/redfish/v1/Systems/System.Embedded.1?$select=PCIeDevices"))
337
+ devices = Array(data["PCIeDevices"]).filter_map { |link|
338
+ next unless link.is_a?(Hash) && link["@odata.id"]
339
+ d = safe_get(link["@odata.id"])
340
+ next unless d
341
+ {
342
+ "device_class" => d["DeviceType"] || "Unknown",
343
+ "manufacturer" => d["Manufacturer"],
344
+ "name" => d["Name"] || d["Id"],
345
+ "description" => d["Description"],
346
+ "id" => d["Id"],
347
+ "slot_type" => d.dig("Oem", "Dell", "SlotType"),
348
+ "bus_width" => d.dig("Oem", "Dell", "BusWidth"),
349
+ "nic" => nil
350
+ }
351
+ }
352
+ return devices unless devices.empty?
471
353
  end
354
+
355
+ # Fallback: NetworkAdapters
356
+ if (data = safe_get("/redfish/v1/Systems/System.Embedded.1/NetworkAdapters?$expand=*($levels=1)"))
357
+ devices = Array(data["Members"]).filter_map { |a|
358
+ next unless a["Model"] || a["Manufacturer"]
359
+ is_fc = [a["Name"], a["Model"], a["Id"], a["Description"]].any? { |f| f =~ /FC|Fibre/i }
360
+ {
361
+ "device_class" => is_fc ? "FibreChannelController" : "NetworkController",
362
+ "manufacturer" => a["Manufacturer"],
363
+ "name" => a["Name"] || a["Id"],
364
+ "description" => a["Description"],
365
+ "id" => a["Id"],
366
+ "slot_type" => a.dig("Oem", "Dell", "SlotType") || (a["Id"] =~ /Slot\.(\d+)/ ? "Slot #{$1}" : nil),
367
+ "bus_width" => nil,
368
+ "nic" => a["@odata.id"]
369
+ }
370
+ }
371
+ return devices unless devices.empty?
372
+ end
373
+
374
+ # Last resort: PCIeFunctions directly
375
+ if (data = safe_get("/redfish/v1/Systems/System.Embedded.1/PCIeFunctions?$expand=*($levels=1)"))
376
+ return Array(data["Members"]).map { |fn|
377
+ {
378
+ "device_class" => fn["DeviceClass"] || "Unknown",
379
+ "manufacturer" => fn["Manufacturer"] || "Unknown",
380
+ "name" => fn["Name"] || fn["Id"],
381
+ "description" => fn["Description"],
382
+ "id" => fn["Id"],
383
+ "slot_type" => fn.dig("Oem", "Dell", "SlotType"),
384
+ "bus_width" => fn.dig("Oem", "Dell", "DataBusWidth"),
385
+ "nic" => nil
386
+ }
387
+ }
388
+ end
389
+
390
+ []
472
391
  end
473
392
 
474
393
  # Map NICs to PCI bus IDs for Mellanox cards
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.9.4"
4
+ VERSION = "0.9.7"
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.9.4
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Siegel