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

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



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

def accounts
  @idrac_client.accounts
end

#all_seds?Boolean

Returns:

  • (Boolean)


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

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)


613
614
615
# File 'lib/radfish/idrac_adapter.rb', line 613

def bios_error_prompt_disabled?
  @idrac_client.bios_error_prompt_disabled?
end

#bios_hdd_placeholder_enabled?Boolean

Returns:

  • (Boolean)


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

def bios_hdd_placeholder_enabled?
  @idrac_client.bios_hdd_placeholder_enabled?
end

#bios_os_power_control_enabled?Boolean

Returns:

  • (Boolean)


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

def bios_os_power_control_enabled?
  @idrac_client.bios_os_power_control_enabled?
end

#bmc_infoObject



745
746
747
748
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
# File 'lib/radfish/idrac_adapter.rb', line 745

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



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

def boot
  boot_config
end

#boot_configObject

Boot configuration



505
506
507
508
# File 'lib/radfish/idrac_adapter.rb', line 505

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

#boot_optionsObject



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

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



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

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



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

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



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

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



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

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



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

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

#cancel_job(job_id) ⇒ Object



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

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

#clear_boot_overrideObject



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

def clear_boot_override
  @idrac_client.clear_boot_override
end

#clear_jobs!Object



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

def clear_jobs!
  @idrac_client.clear_jobs!
end

#clear_sel_logObject



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

def clear_sel_log
  @idrac_client.clear_sel_log
end

#controller_encryption_capable?Boolean

Additional storage helper methods available in iDRAC gem

Returns:

  • (Boolean)


839
840
841
# File 'lib/radfish/idrac_adapter.rb', line 839

def controller_encryption_capable?
  @idrac_client.controller_encryption_capable?
end

#controller_encryption_enabled?Boolean

Returns:

  • (Boolean)


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

def controller_encryption_enabled?
  @idrac_client.controller_encryption_enabled?
end

#cpusObject



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/radfish/idrac_adapter.rb', line 209

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



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

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

#create_virtual_disk(**options) ⇒ Object



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

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

#delete_account(username) ⇒ Object



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

def (username)
  @idrac_client.(username)
end

#delete_volume(volume_odata_id) ⇒ Object



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

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

#disable_local_key_management(controller_id:) ⇒ Object



825
826
827
# File 'lib/radfish/idrac_adapter.rb', line 825

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

#drives(controller) ⇒ Object

Raises:

  • (ArgumentError)


314
315
316
317
318
319
320
321
322
323
324
# File 'lib/radfish/idrac_adapter.rb', line 314

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



469
470
471
472
473
474
475
476
477
478
479
# File 'lib/radfish/idrac_adapter.rb', line 469

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



817
818
819
820
821
822
823
# File 'lib/radfish/idrac_adapter.rb', line 817

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



639
640
641
642
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
# File 'lib/radfish/idrac_adapter.rb', line 639

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



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

def ensure_uefi_boot
  @idrac_client.ensure_uefi_boot
end

#ensure_vendor_specific_bmc_ready!Object

BMC Management



606
607
608
609
# File 'lib/radfish/idrac_adapter.rb', line 606

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

#fansObject



254
255
256
257
258
259
260
261
# File 'lib/radfish/idrac_adapter.rb', line 254

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



793
794
795
# File 'lib/radfish/idrac_adapter.rb', line 793

def get_bmc_network
  @idrac_client.get_bmc_network
end

#get_boot_devicesObject



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

def get_boot_devices
  @idrac_client.get_boot_devices
end

#get_firmware_versionObject



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

def get_firmware_version
  @idrac_client.get_firmware_version
end

#insert_virtual_media(iso_url, device: nil) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/radfish/idrac_adapter.rb', line 444

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



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

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

#jobsObject

Jobs



580
581
582
# File 'lib/radfish/idrac_adapter.rb', line 580

def jobs
  @idrac_client.jobs
end

#jobs_summaryObject



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

def jobs_summary
  jobs
end

#license_infoObject



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

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



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

def make
  "Dell"
end

#memoryObject



233
234
235
236
237
238
239
# File 'lib/radfish/idrac_adapter.rb', line 233

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



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

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



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

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

#nicsObject



241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/radfish/idrac_adapter.rb', line 241

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



567
568
569
570
571
572
573
574
575
576
# File 'lib/radfish/idrac_adapter.rb', line 567

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



559
560
561
562
563
564
565
# File 'lib/radfish/idrac_adapter.rb', line 559

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



278
279
280
281
282
283
# File 'lib/radfish/idrac_adapter.rb', line 278

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

#power_consumption_wattsObject



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

def power_consumption_watts
  @idrac_client.get_power_usage_watts
end

#power_cycle(wait: true) ⇒ Object



151
152
153
154
155
156
# File 'lib/radfish/idrac_adapter.rb', line 151

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



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/radfish/idrac_adapter.rb', line 90

def power_off(type: "GracefulShutdown", wait: true)
  # Use the type parameter directly - it already uses Redfish standard values
  result = @idrac_client.power_off(kind: type)
  
  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



269
270
271
272
273
274
275
276
# File 'lib/radfish/idrac_adapter.rb', line 269

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



114
115
116
117
118
119
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
# File 'lib/radfish/idrac_adapter.rb', line 114

def reboot(type: "GracefulRestart", wait: true)
  # Use the type parameter - iDRAC's power_off can handle restart types
  begin
    result = @idrac_client.power_off(kind: type)
  rescue => e
    # If graceful restart fails, fall back to force restart
    if type == "GracefulRestart"
      debug "Graceful restart failed, using force restart", 1, :yellow
      result = @idrac_client.reboot  # This is ForceRestart
    else
      raise e
    end
  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



158
159
160
161
# File 'lib/radfish/idrac_adapter.rb', line 158

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



783
784
785
# File 'lib/radfish/idrac_adapter.rb', line 783

def screenshot
  @idrac_client.screenshot
end

#sed_ready?Boolean

Returns:

  • (Boolean)


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

def sed_ready?
  @idrac_client.sed_ready?
end

#sel_logObject

Utility



705
706
707
# File 'lib/radfish/idrac_adapter.rb', line 705

def sel_log
  @idrac_client.sel_log
end

#sel_summary(limit: 10) ⇒ Object



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

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

#serialObject



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

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

#service_infoObject



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

def service_info
  @idrac_client.service_info
end

#service_tagObject

Individual accessor methods for Core::System interface



190
191
192
# File 'lib/radfish/idrac_adapter.rb', line 190

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

#sessionsObject



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

def sessions
  @idrac_client.sessions
end

#set_bmc_dhcpObject



811
812
813
# File 'lib/radfish/idrac_adapter.rb', line 811

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



797
798
799
800
801
802
803
804
805
806
807
808
809
# File 'lib/radfish/idrac_adapter.rb', line 797

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



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

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

#set_boot_order_hd_firstObject



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

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



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

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



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

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



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/radfish/idrac_adapter.rb', line 291

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



389
390
391
392
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
# File 'lib/radfish/idrac_adapter.rb', line 389

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



775
776
777
778
779
# File 'lib/radfish/idrac_adapter.rb', line 775

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



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/radfish/idrac_adapter.rb', line 165

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



263
264
265
266
267
# File 'lib/radfish/idrac_adapter.rb', line 263

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

#unmount_all_mediaObject



490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/radfish/idrac_adapter.rb', line 490

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



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

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



440
441
442
# File 'lib/radfish/idrac_adapter.rb', line 440

def virtual_media
  @idrac_client.virtual_media
end

#virtual_media_statusObject



481
482
483
# File 'lib/radfish/idrac_adapter.rb', line 481

def virtual_media_status
  @idrac_client.virtual_media
end

#volume_drives(volume) ⇒ Object

Raises:

  • (ArgumentError)


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

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)


326
327
328
329
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
# File 'lib/radfish/idrac_adapter.rb', line 326

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



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

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