Class: Radfish::IdracAdapter

Inherits:
Core::BaseClient
  • Object
show all
Includes:
Core::Boot, Core::Jobs, Core::Network, Core::Power, Core::Storage, Core::System, Core::Utility, Core::VirtualMedia
Defined in:
lib/radfish/idrac_adapter.rb

Constant Summary collapse

RESTART_TYPES =

Reset types that restart the host. These must never be sent through #power_off: the wait for "Off" cannot succeed, and the iDRAC gem reads that failure as a stuck shutdown.

%w[ForceRestart GracefulRestart PowerCycle].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host:, username:, password:, **options) ⇒ IdracAdapter

Returns a new instance of IdracAdapter.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/radfish/idrac_adapter.rb', line 20

def initialize(host:, username:, password:, **options)
  super
  
  # Create the underlying iDRAC client
  @idrac_client = IDRAC::Client.new(
    host: host,
    username: username,
    password: password,
    port: options[:port] || 443,
    use_ssl: options.fetch(:use_ssl, true),
    verify_ssl: options.fetch(:verify_ssl, false),
    direct_mode: options.fetch(:direct_mode, false),
    retry_count: options[:retry_count] || 3,
    retry_delay: options[:retry_delay] || 1,
    host_header: options[:host_header]
  )
end

Instance Attribute Details

#idrac_clientObject (readonly)

Returns the value of attribute idrac_client.



18
19
20
# File 'lib/radfish/idrac_adapter.rb', line 18

def idrac_client
  @idrac_client
end

Instance Method Details

#accountsObject



721
722
723
# File 'lib/radfish/idrac_adapter.rb', line 721

def accounts
  @idrac_client.accounts
end

#all_seds?Boolean

Returns:

  • (Boolean)


851
852
853
# File 'lib/radfish/idrac_adapter.rb', line 851

def all_seds?
  @idrac_client.all_seds?
end

#authenticated_request(method, path, **options) ⇒ Object



57
58
59
# File 'lib/radfish/idrac_adapter.rb', line 57

def authenticated_request(method, path, **options)
  @idrac_client.authenticated_request(method, path, **options)
end

#bios_error_prompt_disabled?Boolean

BIOS Configuration

Returns:

  • (Boolean)


617
618
619
# File 'lib/radfish/idrac_adapter.rb', line 617

def bios_error_prompt_disabled?
  @idrac_client.bios_error_prompt_disabled?
end

#bios_hdd_placeholder_enabled?Boolean

Returns:

  • (Boolean)


621
622
623
# File 'lib/radfish/idrac_adapter.rb', line 621

def bios_hdd_placeholder_enabled?
  @idrac_client.bios_hdd_placeholder_enabled?
end

#bios_os_power_control_enabled?Boolean

Returns:

  • (Boolean)


625
626
627
# File 'lib/radfish/idrac_adapter.rb', line 625

def bios_os_power_control_enabled?
  @idrac_client.bios_os_power_control_enabled?
end

#bmc_infoObject



749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
# File 'lib/radfish/idrac_adapter.rb', line 749

def bmc_info
  # Map iDRAC gem data to radfish format
  info = {}
  
  # Get firmware version from idrac gem
  info[:firmware_version] = @idrac_client.get_firmware_version
  
  # Get iDRAC generation (7/8/9) from idrac gem's license_version method
  info[:license_version] = @idrac_client.license_version&.to_s
  
  # Get Redfish version from idrac gem
  info[:redfish_version] = @idrac_client.redfish_version
  
  # Get network info for MAC and IP
  network = @idrac_client.get_bmc_network
  if network.is_a?(Hash)
    info[:mac_address] = network["mac"]
    info[:ip_address] = network["ipv4"]
    info[:hostname] = network["hostname"] || network["fqdn"]
  end
  
  # Get health status from system info
  system = @idrac_client.system_info
  if system.is_a?(Hash)
    info[:health] = system.dig("Status", "Health") || system.dig("Status", "HealthRollup")
  end
  
  info
end

#bootObject

Shorter alias for convenience



515
516
517
# File 'lib/radfish/idrac_adapter.rb', line 515

def boot
  boot_config
end

#boot_configObject

Boot configuration



509
510
511
512
# File 'lib/radfish/idrac_adapter.rb', line 509

def boot_config
  # Return hash for consistent data structure
  @idrac_client.boot_config
end

#boot_optionsObject



519
520
521
522
523
# File 'lib/radfish/idrac_adapter.rb', line 519

def boot_options
  # Return array of OpenStructs for boot options
  options = @idrac_client.boot_options
  options.map { |opt| OpenStruct.new(opt) }
end

#boot_to_bios_setup(enabled: "Once", mode: nil) ⇒ Object



557
558
559
# File 'lib/radfish/idrac_adapter.rb', line 557

def boot_to_bios_setup(enabled: "Once", mode: nil)
  @idrac_client.boot_to_bios_setup(enabled: enabled, mode: mode)
end

#boot_to_cd(enabled: "Once", mode: nil) ⇒ Object



549
550
551
# File 'lib/radfish/idrac_adapter.rb', line 549

def boot_to_cd(enabled: "Once", mode: nil)
  @idrac_client.boot_to_cd(enabled: enabled, mode: mode)
end

#boot_to_disk(enabled: "Once", mode: nil) ⇒ Object



545
546
547
# File 'lib/radfish/idrac_adapter.rb', line 545

def boot_to_disk(enabled: "Once", mode: nil)
  @idrac_client.boot_to_disk(enabled: enabled, mode: mode)
end

#boot_to_pxe(enabled: "Once", mode: nil) ⇒ Object



541
542
543
# File 'lib/radfish/idrac_adapter.rb', line 541

def boot_to_pxe(enabled: "Once", mode: nil)
  @idrac_client.boot_to_pxe(enabled: enabled, mode: mode)
end

#boot_to_usb(enabled: "Once", mode: nil) ⇒ Object



553
554
555
# File 'lib/radfish/idrac_adapter.rb', line 553

def boot_to_usb(enabled: "Once", mode: nil)
  @idrac_client.boot_to_usb(enabled: enabled, mode: mode)
end

#cancel_job(job_id) ⇒ Object



596
597
598
# File 'lib/radfish/idrac_adapter.rb', line 596

def cancel_job(job_id)
  @idrac_client.cancel_job(job_id)
end

#clear_boot_overrideObject



529
530
531
# File 'lib/radfish/idrac_adapter.rb', line 529

def clear_boot_override
  @idrac_client.clear_boot_override
end

#clear_jobs!Object



600
601
602
# File 'lib/radfish/idrac_adapter.rb', line 600

def clear_jobs!
  @idrac_client.clear_jobs!
end

#clear_sel_logObject



713
714
715
# File 'lib/radfish/idrac_adapter.rb', line 713

def clear_sel_log
  @idrac_client.clear_sel_log
end

#controller_encryption_capable?Boolean

Additional storage helper methods available in iDRAC gem

Returns:

  • (Boolean)


843
844
845
# File 'lib/radfish/idrac_adapter.rb', line 843

def controller_encryption_capable?
  @idrac_client.controller_encryption_capable?
end

#controller_encryption_enabled?Boolean

Returns:

  • (Boolean)


847
848
849
# File 'lib/radfish/idrac_adapter.rb', line 847

def controller_encryption_enabled?
  @idrac_client.controller_encryption_enabled?
end

#cpusObject



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/radfish/idrac_adapter.rb', line 213

def cpus
  # The idrac gem returns a summary hash, but radfish expects an array of CPU objects
  # For Dell servers, typically all CPUs are identical, so we create objects based on the summary
  cpu_summary = @idrac_client.cpus
  
  # Create CPU objects that support dot notation
  count = cpu_summary["count"] || 0
  return [] if count == 0
  
  # For each CPU socket, create an object
  # Dell typically has identical CPUs, so we use the summary data for each
  (1..count).map do |socket_num|
    OpenStruct.new(
      socket: socket_num,
      manufacturer: "Intel", # Dell servers typically use Intel
      model: cpu_summary["model"],
      speed_mhz: nil, # Not provided in summary
      cores: cpu_summary["cores"] ? (cpu_summary["cores"] / count) : nil,
      threads: cpu_summary["threads"] ? (cpu_summary["threads"] / count) : nil,
      health: cpu_summary["status"]
    )
  end
end

#create_account(username:, password:, role: "Administrator") ⇒ Object



725
726
727
# File 'lib/radfish/idrac_adapter.rb', line 725

def (username:, password:, role: "Administrator")
  @idrac_client.(username: username, password: password, role: role)
end

#create_virtual_disk(**options) ⇒ Object



833
834
835
# File 'lib/radfish/idrac_adapter.rb', line 833

def create_virtual_disk(**options)
  @idrac_client.create_virtual_disk(**options)
end

#delete_account(username) ⇒ Object



729
730
731
# File 'lib/radfish/idrac_adapter.rb', line 729

def (username)
  @idrac_client.(username)
end

#delete_volume(volume_odata_id) ⇒ Object



837
838
839
# File 'lib/radfish/idrac_adapter.rb', line 837

def delete_volume(volume_odata_id)
  @idrac_client.delete_volume(volume_odata_id)
end

#disable_local_key_management(controller_id:) ⇒ Object



829
830
831
# File 'lib/radfish/idrac_adapter.rb', line 829

def disable_local_key_management(controller_id:)
  @idrac_client.disable_local_key_management(controller_id: controller_id)
end

#drives(controller) ⇒ Object

Raises:

  • (ArgumentError)


318
319
320
321
322
323
324
325
326
327
328
# File 'lib/radfish/idrac_adapter.rb', line 318

def drives(controller)
  # The iDRAC gem requires a controller identifier; derive it from the controller
  raise ArgumentError, "Controller required" unless controller
  controller_id = extract_controller_identifier(controller)
  raise ArgumentError, "Controller identifier missing" unless controller_id
  
  drive_data = @idrac_client.drives(controller_id)
  
  # Convert to OpenStruct for consistency
  drive_data.map { |drive| OpenStruct.new(drive) }
end

#eject_virtual_media(device: "CD") ⇒ Object



473
474
475
476
477
478
479
480
481
482
483
# File 'lib/radfish/idrac_adapter.rb', line 473

def eject_virtual_media(device: "CD")
  @idrac_client.eject_virtual_media(device: device)
rescue IDRAC::Error => e
  if e.message.include?("not found") || e.message.include?("does not exist")
    raise Radfish::VirtualMediaNotFoundError, "Virtual media device not found: #{e.message}"
  else
    raise Radfish::VirtualMediaError, "Failed to eject virtual media: #{e.message}"
  end
rescue StandardError => e
  raise Radfish::VirtualMediaError, "Failed to eject virtual media: #{e.message}"
end

#enable_local_key_management(controller_id:, passphrase:, key_id:) ⇒ Object

Storage management methods - controller encryption and virtual disk operations



821
822
823
824
825
826
827
# File 'lib/radfish/idrac_adapter.rb', line 821

def enable_local_key_management(controller_id:, passphrase:, key_id:)
  @idrac_client.enable_local_key_management(
    controller_id: controller_id,
    passphrase: passphrase,
    key_id: key_id
  )
end

#ensure_sensible_bios!(options = {}) ⇒ Object



643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
# File 'lib/radfish/idrac_adapter.rb', line 643

def ensure_sensible_bios!(options = {})
  # Check current state
  if bios_error_prompt_disabled? && 
     bios_hdd_placeholder_enabled? && 
     bios_os_power_control_enabled?
    puts "BIOS settings already configured correctly".green
    return { changes_made: false }
  end
  
  puts "Configuring BIOS settings...".yellow
  
  # Build the System Configuration Profile (SCP)
  scp = {}
  
  # Disable error prompt (don't halt on errors)
  if !bios_error_prompt_disabled?
    scp = @idrac_client.merge_scp(scp, {
      "BIOS.Setup.1-1" => {
        "ErrPrompt" => "Disabled"
      }
    })
  end
  
  # Enable HDD placeholder for boot order control
  if !bios_hdd_placeholder_enabled?
    scp = @idrac_client.merge_scp(scp, {
      "BIOS.Setup.1-1" => {
        "HddPlaceholder" => "Enabled"
      }
    })
  end
  
  # Enable OS power control
  if !bios_os_power_control_enabled?
    scp = @idrac_client.merge_scp(scp, {
      "BIOS.Setup.1-1" => {
        "ProcCStates" => "Enabled",
        "SysProfile" => "PerfPerWattOptimizedOs",
        "ProcPwrPerf" => "OsDbpm"
      }
    })
  end
  
  # Set UEFI boot mode
  scp = @idrac_client.merge_scp(scp, {
    "BIOS.Setup.1-1" => {
      "BootMode" => "Uefi"
    }
  })
  
  # Disable host header check for better compatibility
  scp = @idrac_client.merge_scp(scp, {
    "iDRAC.Embedded.1" => {
      "WebServer.1#HostHeaderCheck" => "Disabled"
    }
  })
  
  # Apply the configuration
  puts "Final SCP to be applied: #{JSON.pretty_generate(scp)}".cyan
  @idrac_client.set_system_configuration_profile(scp)
  
  { changes_made: true }
end

#ensure_uefi_bootObject



629
630
631
# File 'lib/radfish/idrac_adapter.rb', line 629

def ensure_uefi_boot
  @idrac_client.ensure_uefi_boot
end

#ensure_vendor_specific_bmc_ready!Object

BMC Management



610
611
612
613
# File 'lib/radfish/idrac_adapter.rb', line 610

def ensure_vendor_specific_bmc_ready!
  # For iDRAC, ensure the Lifecycle Controller is enabled
  @idrac_client.ensure_lifecycle_controller!
end

#fansObject



258
259
260
261
262
263
264
265
# File 'lib/radfish/idrac_adapter.rb', line 258

def fans
  # Convert hash array to OpenStruct objects for dot notation access
  fan_data = @idrac_client.fans
  
  fan_data.map do |fan|
    OpenStruct.new(fan)
  end
end

#get_bmc_networkObject

Network management



797
798
799
# File 'lib/radfish/idrac_adapter.rb', line 797

def get_bmc_network
  @idrac_client.get_bmc_network
end

#get_boot_devicesObject



537
538
539
# File 'lib/radfish/idrac_adapter.rb', line 537

def get_boot_devices
  @idrac_client.get_boot_devices
end

#get_firmware_versionObject



745
746
747
# File 'lib/radfish/idrac_adapter.rb', line 745

def get_firmware_version
  @idrac_client.get_firmware_version
end

#insert_virtual_media(iso_url, device: nil) ⇒ Object



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/radfish/idrac_adapter.rb', line 448

def insert_virtual_media(iso_url, device: nil)
  # Default to "CD" for iDRAC if not specified
  device ||= "CD"
  @idrac_client.insert_virtual_media(iso_url, device: device)
rescue IDRAC::Error => e
  # Translate iDRAC errors to Radfish errors with context
  error_message = e.message
  
  if error_message.include?("connection refused") || error_message.include?("unreachable")
    raise Radfish::VirtualMediaConnectionError, "BMC cannot reach ISO server: #{error_message}"
  elsif error_message.include?("already attached") || error_message.include?("in use")
    raise Radfish::VirtualMediaBusyError, "Virtual media device busy: #{error_message}"
  elsif error_message.include?("not found") || error_message.include?("does not exist")
    raise Radfish::VirtualMediaNotFoundError, "Virtual media device not found: #{error_message}"
  elsif error_message.include?("timeout")
    raise Radfish::TaskTimeoutError, "Virtual media operation timed out: #{error_message}"
  else
    # Generic virtual media error
    raise Radfish::VirtualMediaError, error_message
  end
rescue StandardError => e
  # Catch any other errors and wrap them
  raise Radfish::VirtualMediaError, "Virtual media insertion failed: #{e.message}"
end

#job_status(job_id) ⇒ Object



588
589
590
# File 'lib/radfish/idrac_adapter.rb', line 588

def job_status(job_id)
  @idrac_client.job_status(job_id)
end

#jobsObject

Jobs



584
585
586
# File 'lib/radfish/idrac_adapter.rb', line 584

def jobs
  @idrac_client.jobs
end

#jobs_summaryObject



604
605
606
# File 'lib/radfish/idrac_adapter.rb', line 604

def jobs_summary
  jobs
end

#license_infoObject



791
792
793
# File 'lib/radfish/idrac_adapter.rb', line 791

def license_info
  @idrac_client.license_info
end

#loginObject

Session management



49
50
51
# File 'lib/radfish/idrac_adapter.rb', line 49

def 
  @idrac_client.
end

#logoutObject



53
54
55
# File 'lib/radfish/idrac_adapter.rb', line 53

def logout
  @idrac_client.logout
end

#makeObject



198
199
200
# File 'lib/radfish/idrac_adapter.rb', line 198

def make
  "Dell"
end

#memoryObject



237
238
239
240
241
242
243
# File 'lib/radfish/idrac_adapter.rb', line 237

def memory
  mem_data = @idrac_client.memory
  return [] unless mem_data
  
  # Convert to OpenStruct for dot notation access
  mem_data.map { |m| OpenStruct.new(m) }
end

#modelObject



202
203
204
205
206
207
# File 'lib/radfish/idrac_adapter.rb', line 202

def model
  @model ||= begin
    model = @idrac_client.system_info["model"]
    model&.gsub(/^PowerEdge\s+/i, '') if model  # Strip PowerEdge prefix
  end
end

#mount_iso_and_boot(iso_url, device: "CD") ⇒ Object



489
490
491
492
# File 'lib/radfish/idrac_adapter.rb', line 489

def mount_iso_and_boot(iso_url, device: "CD")
  insert_virtual_media(iso_url, device: device)
  boot_to_cd
end

#nicsObject



245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/radfish/idrac_adapter.rb', line 245

def nics
  nic_data = @idrac_client.nics
  return [] unless nic_data
  
  # Convert to OpenStruct for dot notation access, including nested ports
  nic_data.map do |nic|
    if nic["ports"]
      nic["ports"] = nic["ports"].map { |port| OpenStruct.new(port) }
    end
    OpenStruct.new(nic)
  end
end

#nics_with_pci_infoObject



571
572
573
574
575
576
577
578
579
580
# File 'lib/radfish/idrac_adapter.rb', line 571

def nics_with_pci_info
  nics = @idrac_client.nics
  pci = pci_devices
  
  # Use the existing nics_to_pci method from idrac gem
  nics_with_pci = @idrac_client.nics_to_pci(nics, pci.map(&:to_h))
  
  # Convert to OpenStruct
  nics_with_pci.map { |nic| OpenStruct.new(nic) }
end

#pci_devicesObject

PCI Devices



563
564
565
566
567
568
569
# File 'lib/radfish/idrac_adapter.rb', line 563

def pci_devices
  devices = @idrac_client.pci_devices
  return [] unless devices
  
  # Convert to OpenStruct for dot notation access
  devices.map { |device| OpenStruct.new(device) }
end

#power_consumptionObject



282
283
284
285
286
287
# File 'lib/radfish/idrac_adapter.rb', line 282

def power_consumption
  # Return a hash with power consumption data for radfish
  {
    consumed_watts: @idrac_client.get_power_usage_watts
  }
end

#power_consumption_wattsObject



289
290
291
# File 'lib/radfish/idrac_adapter.rb', line 289

def power_consumption_watts
  @idrac_client.get_power_usage_watts
end

#power_cycle(wait: true) ⇒ Object



155
156
157
158
159
160
# File 'lib/radfish/idrac_adapter.rb', line 155

def power_cycle(wait: true)
  # Power cycle: turn off then on
  power_off(type: "ForceOff", wait: wait)
  sleep 5
  power_on(wait: wait)
end

#power_off(type: "GracefulShutdown", wait: true) ⇒ Object

Raises:

  • (ArgumentError)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/radfish/idrac_adapter.rb', line 94

def power_off(type: "GracefulShutdown", wait: true)
  raise ArgumentError, "#{type} restarts the host; use #reboot" if RESTART_TYPES.include?(type)

  # Use the type parameter directly - it already uses Redfish standard values
  result = @idrac_client.power_off(kind: type, wait: wait)
  
  if wait && result
    # Wait for power off to complete
    max_attempts = 30
    attempts = 0
    while attempts < max_attempts
      sleep 2
      begin
        status = power_status
        break if status == "Off"
      rescue => e
        # BMC might be temporarily unavailable during power operations
        debug "Waiting for BMC to respond: #{e.message}", 1, :yellow
      end
      attempts += 1
    end
  end
  
  result
end

#power_on(wait: true) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/radfish/idrac_adapter.rb', line 67

def power_on(wait: true)
  result = @idrac_client.power_on
  
  if wait && result
    # Wait for power on to complete
    max_attempts = 30
    attempts = 0
    while attempts < max_attempts
      sleep 2
      begin
        status = power_status
        break if status == "On"
      rescue => e
        # BMC might be temporarily unavailable during power operations
        debug "Waiting for BMC to respond: #{e.message}", 1, :yellow
      end
      attempts += 1
    end
  end
  
  result
end

#power_statusObject

Power management - delegate to iDRAC client



63
64
65
# File 'lib/radfish/idrac_adapter.rb', line 63

def power_status
  @idrac_client.get_power_state
end

#psusObject



273
274
275
276
277
278
279
280
# File 'lib/radfish/idrac_adapter.rb', line 273

def psus
  # Convert hash array to OpenStruct objects for dot notation access
  psu_data = @idrac_client.psus
  
  psu_data.map do |psu|
    OpenStruct.new(psu)
  end
end

#reboot(type: "GracefulRestart", wait: true) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/radfish/idrac_adapter.rb', line 120

def reboot(type: "GracefulRestart", wait: true)
  # A restart goes to the restart action. Routing it through power_off left iDRAC8 hosts
  # powered down: the wait for "Off" failed and the iDRAC gem escalated to ForceOff.
  begin
    result = @idrac_client.reboot(kind: type, wait: wait)
  rescue => e
    # iDRAC8 has no GracefulRestart in its allowable reset types.
    raise e unless type == "GracefulRestart"
    debug "Graceful restart failed, using force restart", 1, :yellow
    result = @idrac_client.reboot(kind: "ForceRestart", wait: wait)
  end
  
  if wait && result
    # Wait for system to go down then come back up
    max_attempts = 60
    attempts = 0
    went_down = false
    
    while attempts < max_attempts
      sleep 2
      begin
        status = power_status
        went_down = true if status == "Off" && !went_down
        break if went_down && status == "On"
      rescue => e
        # BMC might be temporarily unavailable during reboot
        debug "Waiting for BMC during reboot: #{e.message}", 1, :yellow
      end
      attempts += 1
    end
  end
  
  result
end

#reset_type_allowedObject



162
163
164
165
# File 'lib/radfish/idrac_adapter.rb', line 162

def reset_type_allowed
  # iDRAC doesn't expose this directly, return common types
  ["On", "ForceOff", "GracefulShutdown", "GracefulRestart", "ForceRestart", "Nmi", "PushPowerButton"]
end

#screenshotObject

Additional iDRAC-specific methods



787
788
789
# File 'lib/radfish/idrac_adapter.rb', line 787

def screenshot
  @idrac_client.screenshot
end

#sed_ready?Boolean

Returns:

  • (Boolean)


855
856
857
# File 'lib/radfish/idrac_adapter.rb', line 855

def sed_ready?
  @idrac_client.sed_ready?
end

#sel_logObject

Utility



709
710
711
# File 'lib/radfish/idrac_adapter.rb', line 709

def sel_log
  @idrac_client.sel_log
end

#sel_summary(limit: 10) ⇒ Object



717
718
719
# File 'lib/radfish/idrac_adapter.rb', line 717

def sel_summary(limit: 10)
  @idrac_client.sel_summary(limit: limit)
end

#serialObject



209
210
211
# File 'lib/radfish/idrac_adapter.rb', line 209

def serial
  @serial ||= @idrac_client.system_info["service_tag"]  # Dell uses service tag as serial
end

#service_infoObject



741
742
743
# File 'lib/radfish/idrac_adapter.rb', line 741

def service_info
  @idrac_client.service_info
end

#service_tagObject

Individual accessor methods for Core::System interface



194
195
196
# File 'lib/radfish/idrac_adapter.rb', line 194

def service_tag
  @service_tag ||= @idrac_client.system_info["service_tag"]
end

#sessionsObject



737
738
739
# File 'lib/radfish/idrac_adapter.rb', line 737

def sessions
  @idrac_client.sessions
end

#set_bmc_dhcpObject



815
816
817
# File 'lib/radfish/idrac_adapter.rb', line 815

def set_bmc_dhcp
  @idrac_client.set_bmc_dhcp
end

#set_bmc_network(ipv4: nil, mask: nil, gateway: nil, dns_primary: nil, dns_secondary: nil, hostname: nil, dhcp: false) ⇒ Object



801
802
803
804
805
806
807
808
809
810
811
812
813
# File 'lib/radfish/idrac_adapter.rb', line 801

def set_bmc_network(ipv4: nil, mask: nil, gateway: nil, 
                    dns_primary: nil, dns_secondary: nil, hostname: nil, 
                    dhcp: false)
  @idrac_client.set_bmc_network(
    ipv4: ipv4,
    mask: mask,
    gateway: gateway,
    dns_primary: dns_primary,
    dns_secondary: dns_secondary,
    hostname: hostname,
    dhcp: dhcp
  )
end

#set_boot_order(devices) ⇒ Object



533
534
535
# File 'lib/radfish/idrac_adapter.rb', line 533

def set_boot_order(devices)
  @idrac_client.set_boot_order(devices)
end

#set_boot_order_hd_firstObject



638
639
640
641
# File 'lib/radfish/idrac_adapter.rb', line 638

def set_boot_order_hd_first
  # Use iDRAC's existing method for setting boot order to HD first
  @idrac_client.set_boot_order_hd_first
end

#set_boot_override(target, enabled: "Once", mode: nil) ⇒ Object



525
526
527
# File 'lib/radfish/idrac_adapter.rb', line 525

def set_boot_override(target, enabled: "Once", mode: nil)
  @idrac_client.set_boot_override(target, enabled: enabled, mode: mode)
end

#set_one_time_boot_to_virtual_mediaObject



633
634
635
636
# File 'lib/radfish/idrac_adapter.rb', line 633

def set_one_time_boot_to_virtual_media
  # Use iDRAC's existing method for setting one-time boot to virtual media
  @idrac_client.set_one_time_virtual_media_boot
end

#storage_controllersObject

Storage



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/radfish/idrac_adapter.rb', line 295

def storage_controllers
  # Convert hash array to OpenStruct objects for dot notation access
  # Note: idrac gem uses 'controllers' not 'storage_controllers'
  controller_data = @idrac_client.controllers
  
  controller_data.map do |controller|
    # Promote battery status if available in OEM fields
    begin
      battery = controller.dig("Oem", "Dell", "DellControllerBattery")
      if battery
        controller["battery_status"] ||= (battery["PrimaryStatus"] || battery["RAIDState"]) 
      end
    rescue => e
      debug "Battery status parse error: #{e.message}", 2, :yellow
    end
    # Convert drives array to OpenStruct objects if present
    if controller["drives"]
      controller["drives"] = controller["drives"].map { |drive| OpenStruct.new(drive) }
    end
    OpenStruct.new(controller)
  end
end

#storage_summaryObject



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/radfish/idrac_adapter.rb', line 393

def storage_summary
  # The iDRAC gem doesn't have a storage_summary method
  # We need to build it from controllers, drives, and volumes
  begin
    controllers = @idrac_client.controllers
    total_drives = 0
    total_volumes = 0
    
    controllers.each do |controller|
      if controller["@odata.id"]
        drives = @idrac_client.drives(controller["@odata.id"]) rescue []
        volumes = @idrac_client.volumes(controller["@odata.id"]) rescue []
        total_drives += drives.size
        total_volumes += volumes.size
      end
    end
    
    {
      "controller_count" => controllers.size,
      "drive_count" => total_drives,
      "volume_count" => total_volumes
    }
  rescue => e
    puts "Error fetching storage summary: #{e.message}" if @debug
    {
      "controller_count" => 0,
      "drive_count" => 0,
      "volume_count" => 0
    }
  end
end

#system_healthObject



779
780
781
782
783
# File 'lib/radfish/idrac_adapter.rb', line 779

def system_health
  # Convert hash to OpenStruct for dot notation access
  health_data = @idrac_client.system_health
  OpenStruct.new(health_data)
end

#system_infoObject

System information



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/radfish/idrac_adapter.rb', line 169

def system_info
  # iDRAC gem returns string keys, convert to symbols for radfish
  info = @idrac_client.system_info
  
  # Dell servers always have "Dell Inc." as manufacturer
  # Normalize for consistency
  manufacturer = "Dell"
  
  model = info["model"]
  model = model&.gsub(/^PowerEdge\s+/i, '') if model  # Strip PowerEdge prefix
  
  {
    service_tag: info["service_tag"],
    manufacturer: manufacturer,
    make: manufacturer,
    model: model,
    serial: info["service_tag"],  # Dell uses service tag as serial
    serial_number: info["service_tag"],
    firmware_version: info["firmware_version"],
    idrac_version: info["idrac_version"],
    is_dell: info["is_dell"]
  }.compact
end

#temperaturesObject



267
268
269
270
271
# File 'lib/radfish/idrac_adapter.rb', line 267

def temperatures
  # iDRAC doesn't provide a dedicated temperatures method
  # Return empty array to satisfy the interface
  []
end

#unmount_all_mediaObject



494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/radfish/idrac_adapter.rb', line 494

def unmount_all_media
  media_list = virtual_media
  success = true
  
  media_list.each do |media|
    if media[:inserted]
      success &&= eject_virtual_media(device: media[:device])
    end
  end
  
  success
end

#update_account_password(username:, new_password:) ⇒ Object



733
734
735
# File 'lib/radfish/idrac_adapter.rb', line 733

def (username:, new_password:)
  @idrac_client.(username: username, new_password: new_password)
end

#vendorObject



38
39
40
# File 'lib/radfish/idrac_adapter.rb', line 38

def vendor
  'dell'
end

#verbosity=(value) ⇒ Object



42
43
44
45
# File 'lib/radfish/idrac_adapter.rb', line 42

def verbosity=(value)
  super
  @idrac_client.verbosity = value if @idrac_client
end

#virtual_mediaObject

Virtual Media



444
445
446
# File 'lib/radfish/idrac_adapter.rb', line 444

def virtual_media
  @idrac_client.virtual_media
end

#virtual_media_statusObject



485
486
487
# File 'lib/radfish/idrac_adapter.rb', line 485

def virtual_media_status
  @idrac_client.virtual_media
end

#volume_drives(volume) ⇒ Object

Raises:

  • (ArgumentError)


366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/radfish/idrac_adapter.rb', line 366

def volume_drives(volume)
  # Resolve the physical drives that make up a volume
  raise ArgumentError, "Volume required" unless volume
  controller_id = extract_controller_identifier(volume.controller)
  # Get all drives on the controller
  drives = @idrac_client.drives(controller_id)
  # The IDRAC client drive entries include 'odata_id'; volumes include Links.Drives as '@odata.id'
  refs = nil
  raw = volume.adapter_data
  if raw.respond_to?(:[])
    refs = raw['drives'] || raw[:drives]
  elsif raw.respond_to?(:drives)
    refs = raw.drives
  end
  return [] unless refs && refs.respond_to?(:map)
  ref_ids = refs.map { |r| r['@odata.id'] || r[:'@odata.id'] }.compact
  matched = drives.select do |d|
    oid = if d.is_a?(Hash)
            d['odata_id'] || d[:odata_id] || d['@odata.id'] || d[:'@odata.id']
          elsif d.respond_to?(:[])
            d['odata_id'] || d['@odata.id']
          end
    oid && ref_ids.include?(oid)
  end
  matched.map { |drive| OpenStruct.new(drive) }
end

#volumes(controller) ⇒ Object

Raises:

  • (ArgumentError)


330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/radfish/idrac_adapter.rb', line 330

def volumes(controller)
  # The iDRAC gem requires a controller identifier; derive it from the controller
  raise ArgumentError, "Controller required" unless controller
  controller_id = extract_controller_identifier(controller)
  raise ArgumentError, "Controller identifier missing" unless controller_id
  
  volume_data = @idrac_client.volumes(controller_id)
  
  # Normalize key names for expected fields
  volume_data.each do |v|
    # Ensure raid_type/volume_type
    v["raid_type"] ||= v["raid_level"] || v["RAIDType"]
    v["volume_type"] ||= v["VolumeType"] if v["VolumeType"]
    # Operations mapping
    if v["Operations"]&.any?
      v["operation_percent_complete"] ||= v["Operations"].first["PercentageComplete"]
      v["operation_name"] ||= v["Operations"].first["OperationName"]
    end
    # Health normalization
    v["health"] ||= v.dig("Status", "Health")
    # OEM fallbacks
    oem = v.dig("Oem", "Dell", "DellVirtualDisk") || v.dig("Oem", "Dell", "DellVolume")
    if oem
      v["lock_status"] ||= oem["LockStatus"]
      v["stripe_size"] ||= oem["StripeSize"]
      v["operation_name"] ||= oem["OperationName"]
      v["operation_percent_complete"] ||= oem["OperationPercentComplete"]
      v["write_cache_policy"] ||= oem["WriteCachePolicy"]
      v["read_cache_policy"] ||= oem["ReadCachePolicy"]
    end
  end
  
  # Convert to OpenStruct for consistency
  volume_data.map { |volume| OpenStruct.new(volume) }
end

#wait_for_job(job_id, timeout: 600) ⇒ Object



592
593
594
# File 'lib/radfish/idrac_adapter.rb', line 592

def wait_for_job(job_id, timeout: 600)
  @idrac_client.wait_for_job(job_id, timeout: timeout)
end