idrac 0.1.92 → 0.3.1
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 +4 -4
- data/bin/idrac +751 -1
- data/lib/idrac/boot.rb +357 -0
- data/lib/idrac/client.rb +163 -68
- data/lib/idrac/core_ext.rb +100 -0
- data/lib/idrac/jobs.rb +0 -4
- data/lib/idrac/session.rb +61 -12
- data/lib/idrac/storage.rb +374 -0
- data/lib/idrac/system.rb +383 -0
- data/lib/idrac/version.rb +1 -1
- data/lib/idrac/virtual_media.rb +285 -0
- data/lib/idrac.rb +15 -10
- metadata +7 -2
data/bin/idrac
CHANGED
@@ -23,6 +23,8 @@ module IDRAC
|
|
23
23
|
class_option :no_ssl, type: :boolean, default: false, desc: "Disable SSL"
|
24
24
|
class_option :verify_ssl, type: :boolean, default: false, desc: "Enable SSL verification (not recommended for iDRAC's self-signed certificates)"
|
25
25
|
class_option :auto_delete_sessions, type: :boolean, default: true, desc: "Automatically delete sessions when maximum sessions are reached"
|
26
|
+
class_option :retries, type: :numeric, default: 3, desc: "Number of retries for API calls"
|
27
|
+
class_option :retry_delay, type: :numeric, default: 1, desc: "Initial delay in seconds between retries (increases exponentially)"
|
26
28
|
class_option :verbose, type: :boolean, default: false, aliases: '-v', desc: "Enable verbose output"
|
27
29
|
class_option :very_verbose, type: :boolean, default: false, aliases: '-vv', desc: "Enable very verbose output with detailed headers and requests"
|
28
30
|
class_option :debug, type: :boolean, default: false, aliases: '-vvv', desc: "Enable debug output with detailed stack traces and SSL info"
|
@@ -454,6 +456,752 @@ module IDRAC
|
|
454
456
|
end
|
455
457
|
end
|
456
458
|
|
459
|
+
# Storage commands
|
460
|
+
desc "storage_controller", "Get storage controller information"
|
461
|
+
map "storage:controller" => :storage_controller
|
462
|
+
def storage_controller
|
463
|
+
with_idrac_client do |client|
|
464
|
+
controller = client.controller
|
465
|
+
puts "\nStorage Controller Summary:".green.bold
|
466
|
+
puts "Name: #{controller['Name']}".cyan
|
467
|
+
puts "Model: #{controller['Model']}".cyan
|
468
|
+
puts "Health: #{controller['Status']['Health']}".cyan
|
469
|
+
puts "Manufacturer: #{controller['Manufacturer']}".cyan
|
470
|
+
|
471
|
+
if client.controller_encryption_capable?(controller)
|
472
|
+
puts "Encryption Capable: Yes".green
|
473
|
+
puts "Encryption Enabled: #{client.controller_encryption_enabled?(controller) ? 'Yes'.green : 'No'.yellow}"
|
474
|
+
else
|
475
|
+
puts "Encryption Capable: No".yellow
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
desc "storage_drives", "Get physical drive information"
|
481
|
+
map "storage:drives" => :storage_drives
|
482
|
+
def storage_drives
|
483
|
+
with_idrac_client do |client|
|
484
|
+
controller = client.controller
|
485
|
+
drives = client.drives(controller)
|
486
|
+
|
487
|
+
puts "\nPhysical Drives (#{drives.size}):".green.bold
|
488
|
+
drives.each do |drive|
|
489
|
+
capacity_gb = drive[:capacity_bytes].to_f / (1024**3)
|
490
|
+
health_color = drive[:health] == "OK" ? :green : :red
|
491
|
+
|
492
|
+
puts "#{drive[:name]}:".bold
|
493
|
+
puts " Model: #{drive[:model]}".cyan
|
494
|
+
puts " Health: #{drive[:health].send(health_color)}"
|
495
|
+
puts " Capacity: #{capacity_gb.round(2)} GB".cyan
|
496
|
+
puts " Media Type: #{drive[:media_type]}".cyan
|
497
|
+
puts " Serial: #{drive[:serial]}".cyan
|
498
|
+
if drive[:encryption_ability]
|
499
|
+
puts " Encryption: #{drive[:encryption_ability]}".cyan
|
500
|
+
end
|
501
|
+
puts ""
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
desc "storage_volumes", "Get virtual disk information"
|
507
|
+
map "storage:volumes" => :storage_volumes
|
508
|
+
def storage_volumes
|
509
|
+
with_idrac_client do |client|
|
510
|
+
controller = client.controller
|
511
|
+
volumes = client.volumes(controller)
|
512
|
+
|
513
|
+
puts "\nVirtual Disks (#{volumes.size}):".green.bold
|
514
|
+
volumes.each do |volume|
|
515
|
+
capacity_gb = volume[:capacity_bytes].to_f / (1024**3)
|
516
|
+
health_color = volume[:health] == "OK" ? :green : :yellow
|
517
|
+
|
518
|
+
puts "#{volume[:name]}:".bold
|
519
|
+
puts " RAID Type: #{volume[:raid_level] || volume[:volume_type]}".cyan
|
520
|
+
puts " Health: #{volume[:health].send(health_color)}"
|
521
|
+
puts " Capacity: #{capacity_gb.round(2)} GB".cyan
|
522
|
+
puts " Stripe Size: #{volume[:stripe_size]}".cyan
|
523
|
+
puts " Read Cache: #{volume[:read_cache_policy]}".cyan
|
524
|
+
puts " Write Cache: #{volume[:write_cache_policy]}".cyan
|
525
|
+
puts " FastPath: #{volume[:fastpath] == 'enabled' ? 'Enabled'.green : 'Disabled'.yellow}"
|
526
|
+
puts " Encrypted: #{volume[:encrypted] ? 'Yes'.green : 'No'.yellow}" if volume[:encrypted] != nil
|
527
|
+
|
528
|
+
if volume[:progress]
|
529
|
+
puts " Progress: #{volume[:progress]}%".cyan
|
530
|
+
puts " Operation: #{volume[:message]}".cyan
|
531
|
+
end
|
532
|
+
puts ""
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
desc "storage_create_volume", "Create a new virtual disk"
|
538
|
+
map "storage:create_volume" => :storage_create_volume
|
539
|
+
method_option :name, type: :string, default: "vssd0", desc: "Volume name"
|
540
|
+
method_option :raid, type: :string, default: "RAID5", desc: "RAID type (RAID0, RAID1, RAID5, RAID6, RAID10)"
|
541
|
+
def storage_create_volume
|
542
|
+
with_idrac_client do |client|
|
543
|
+
controller = client.controller
|
544
|
+
drives = client.drives(controller)
|
545
|
+
|
546
|
+
# Confirm with the user before proceeding
|
547
|
+
puts "This will create a new #{options[:raid]} volume named '#{options[:name]}' using #{drives.size} drives.".yellow
|
548
|
+
puts "All data on these drives will be lost!".red.bold
|
549
|
+
|
550
|
+
print "Do you want to continue? (y/n): "
|
551
|
+
confirmation = $stdin.gets.chomp.downcase
|
552
|
+
|
553
|
+
if confirmation == 'y'
|
554
|
+
client.create_virtual_disk(controller['Id'], drives, name: options[:name], raid_type: options[:raid])
|
555
|
+
puts "Volume created successfully".green
|
556
|
+
else
|
557
|
+
puts "Operation cancelled".yellow
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
desc "storage_delete_volume", "Delete a virtual disk"
|
563
|
+
map "storage:delete_volume" => :storage_delete_volume
|
564
|
+
def storage_delete_volume
|
565
|
+
with_idrac_client do |client|
|
566
|
+
controller = client.controller
|
567
|
+
volumes = client.volumes(controller)
|
568
|
+
|
569
|
+
if volumes.empty?
|
570
|
+
puts "No volumes found to delete".yellow
|
571
|
+
return
|
572
|
+
end
|
573
|
+
|
574
|
+
puts "Available volumes:".green
|
575
|
+
volumes.each_with_index do |vol, idx|
|
576
|
+
puts "#{idx+1}. #{vol[:name]} (#{vol[:raid_level] || vol[:volume_type]}, #{(vol[:capacity_bytes].to_f / (1024**3)).round(2)} GB)"
|
577
|
+
end
|
578
|
+
|
579
|
+
print "Enter the number of the volume to delete (or 'q' to quit): "
|
580
|
+
input = $stdin.gets.chomp
|
581
|
+
|
582
|
+
if input.downcase == 'q'
|
583
|
+
puts "Operation cancelled".yellow
|
584
|
+
return
|
585
|
+
end
|
586
|
+
|
587
|
+
index = input.to_i - 1
|
588
|
+
if index >= 0 && index < volumes.size
|
589
|
+
volume = volumes[index]
|
590
|
+
|
591
|
+
puts "You are about to delete '#{volume[:name]}'. All data will be lost!".red.bold
|
592
|
+
print "Type the volume name to confirm deletion: "
|
593
|
+
confirm = $stdin.gets.chomp
|
594
|
+
|
595
|
+
if confirm == volume[:name]
|
596
|
+
client.delete_volume(volume[:"@odata.id"])
|
597
|
+
puts "Volume deleted successfully".green
|
598
|
+
else
|
599
|
+
puts "Volume name did not match. Operation cancelled".yellow
|
600
|
+
end
|
601
|
+
else
|
602
|
+
puts "Invalid selection".red
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
desc "storage_encryption_enable", "Enable Self-Encrypting Drive (SED) support"
|
608
|
+
map "storage:encryption_enable" => :storage_encryption_enable
|
609
|
+
method_option :passphrase, type: :string, desc: "Encryption passphrase"
|
610
|
+
method_option :keyid, type: :string, desc: "Key identifier"
|
611
|
+
def storage_encryption_enable
|
612
|
+
with_idrac_client do |client|
|
613
|
+
controller = client.controller
|
614
|
+
drives = client.drives(controller)
|
615
|
+
|
616
|
+
if !client.controller_encryption_capable?(controller)
|
617
|
+
puts "This controller does not support encryption".red
|
618
|
+
return
|
619
|
+
end
|
620
|
+
|
621
|
+
if client.controller_encryption_enabled?(controller)
|
622
|
+
puts "Encryption is already enabled on this controller".yellow
|
623
|
+
return
|
624
|
+
end
|
625
|
+
|
626
|
+
if !client.all_seds?(drives)
|
627
|
+
puts "Not all drives are Self-Encrypting Drives (SEDs)".red
|
628
|
+
return
|
629
|
+
end
|
630
|
+
|
631
|
+
# Get passphrase if not provided
|
632
|
+
passphrase = options[:passphrase]
|
633
|
+
if passphrase.nil?
|
634
|
+
print "Enter encryption passphrase (min 8 characters): "
|
635
|
+
passphrase = $stdin.noecho(&:gets).chomp
|
636
|
+
puts
|
637
|
+
end
|
638
|
+
|
639
|
+
# Get keyid if not provided
|
640
|
+
keyid = options[:keyid] || "RAID-Key-#{Time.now.strftime('%Y%m%d')}"
|
641
|
+
|
642
|
+
client.enable_local_key_management(controller['Id'], passphrase: passphrase, keyid: keyid)
|
643
|
+
puts "Encryption enabled successfully".green
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
desc "storage_encryption_disable", "Disable Self-Encrypting Drive (SED) support"
|
648
|
+
map "storage:encryption_disable" => :storage_encryption_disable
|
649
|
+
def storage_encryption_disable
|
650
|
+
with_idrac_client do |client|
|
651
|
+
controller = client.controller
|
652
|
+
|
653
|
+
if !client.controller_encryption_enabled?(controller)
|
654
|
+
puts "Encryption is not enabled on this controller".yellow
|
655
|
+
return
|
656
|
+
end
|
657
|
+
|
658
|
+
puts "WARNING: Disabling encryption may prevent access to encrypted data!".red.bold
|
659
|
+
print "Type 'DISABLE' to confirm: "
|
660
|
+
confirmation = $stdin.gets.chomp
|
661
|
+
|
662
|
+
if confirmation == 'DISABLE'
|
663
|
+
client.disable_local_key_management(controller['Id'])
|
664
|
+
puts "Encryption disabled successfully".green
|
665
|
+
else
|
666
|
+
puts "Operation cancelled".yellow
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# System component commands
|
672
|
+
desc "system_memory", "Get memory/DIMM information"
|
673
|
+
map "system:memory" => :system_memory
|
674
|
+
def system_memory
|
675
|
+
with_idrac_client do |client|
|
676
|
+
memory = client.memory
|
677
|
+
|
678
|
+
puts "\nMemory Modules (#{memory.size}):".green.bold
|
679
|
+
memory.each do |dimm|
|
680
|
+
capacity_gb = dimm["capacity_bytes"].to_f / (1024**3)
|
681
|
+
health_color = dimm["health"] == "OK" ? :green : :red
|
682
|
+
|
683
|
+
puts "#{dimm['name']}:".bold
|
684
|
+
puts " Model: #{dimm['model']}".cyan
|
685
|
+
puts " Health: #{dimm['health'].send(health_color)}"
|
686
|
+
puts " Capacity: #{capacity_gb.round(2)} GB".cyan
|
687
|
+
puts " Speed: #{dimm['speed_mhz']} MHz".cyan
|
688
|
+
puts " Serial: #{dimm['serial']}".cyan
|
689
|
+
puts ""
|
690
|
+
end
|
691
|
+
|
692
|
+
# Show total memory
|
693
|
+
puts "Total Memory: #{client.total_memory_human(memory)}".green.bold
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
desc "system_psus", "Get power supply information"
|
698
|
+
map "system:psus" => :system_psus
|
699
|
+
def system_psus
|
700
|
+
with_idrac_client do |client|
|
701
|
+
psus = client.psus
|
702
|
+
|
703
|
+
puts "\nPower Supplies (#{psus.size}):".green.bold
|
704
|
+
psus.each do |psu|
|
705
|
+
health_color = psu["status"] == "OK" ? :green : :red
|
706
|
+
|
707
|
+
puts "#{psu['name']}:".bold
|
708
|
+
puts " Model: #{psu['model']}".cyan
|
709
|
+
puts " Health: #{psu['status'].send(health_color)}"
|
710
|
+
puts " Input: #{psu['voltage']} V (#{psu['voltage_human']})".cyan
|
711
|
+
puts " Output: #{psu['watts']} W".cyan
|
712
|
+
puts " Serial: #{psu['serial']}".cyan
|
713
|
+
puts ""
|
714
|
+
end
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
desc "system_fans", "Get fan information"
|
719
|
+
map "system:fans" => :system_fans
|
720
|
+
def system_fans
|
721
|
+
with_idrac_client do |client|
|
722
|
+
fans = client.fans
|
723
|
+
|
724
|
+
if fans.empty?
|
725
|
+
puts "No fan information available (system might be powered off)".yellow
|
726
|
+
return
|
727
|
+
end
|
728
|
+
|
729
|
+
puts "\nFans (#{fans.size}):".green.bold
|
730
|
+
fans.each do |fan|
|
731
|
+
health_color = fan["status"] == "OK" ? :green : :red
|
732
|
+
|
733
|
+
puts "#{fan['name']}:".bold
|
734
|
+
puts " Health: #{fan['status'].send(health_color)}"
|
735
|
+
puts " Speed: #{fan['rpm']} RPM".cyan
|
736
|
+
puts ""
|
737
|
+
end
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
desc "system_nics", "Get network adapter information"
|
742
|
+
map "system:nics" => :system_nics
|
743
|
+
def system_nics
|
744
|
+
with_idrac_client do |client|
|
745
|
+
nics = client.nics
|
746
|
+
pci = client.pci_devices
|
747
|
+
nics_with_pci = client.nics_to_pci(nics, pci)
|
748
|
+
|
749
|
+
puts "\nNetwork Adapters (#{nics_with_pci.size}):".green.bold
|
750
|
+
nics_with_pci.each do |nic|
|
751
|
+
puts "#{nic['name']}:".bold
|
752
|
+
puts " Manufacturer: #{nic['manufacturer']}".cyan
|
753
|
+
puts " Model: #{nic['model']}".cyan
|
754
|
+
|
755
|
+
if nic['ports'] && nic['ports'].any?
|
756
|
+
puts " Ports:".cyan
|
757
|
+
nic['ports'].each do |port|
|
758
|
+
status_color = port["status"] == "Up" ? :green : :yellow
|
759
|
+
|
760
|
+
puts " #{port['name']}:".bold
|
761
|
+
puts " Status: #{port['status'].send(status_color)}"
|
762
|
+
puts " MAC: #{port['mac']}".cyan
|
763
|
+
puts " Speed: #{port['speed_mbps']} Mbps".cyan
|
764
|
+
if port['pci']
|
765
|
+
puts " PCI Bus: #{port['pci']}".cyan
|
766
|
+
puts " Linux Device: #{port['linux_device']}".cyan
|
767
|
+
end
|
768
|
+
puts ""
|
769
|
+
end
|
770
|
+
end
|
771
|
+
puts ""
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
desc "system_idrac_network", "Get iDRAC network configuration"
|
777
|
+
map "system:idrac_network" => :system_idrac_network
|
778
|
+
def system_idrac_network
|
779
|
+
with_idrac_client do |client|
|
780
|
+
idrac = client.idrac_network
|
781
|
+
|
782
|
+
puts "\niDRAC Network Configuration:".green.bold
|
783
|
+
puts "Status: #{idrac['status']}".cyan
|
784
|
+
puts "IPv4: #{idrac['ipv4']}".cyan
|
785
|
+
puts "Subnet Mask: #{idrac['mask']}".cyan
|
786
|
+
puts "MAC Address: #{idrac['mac']}".cyan
|
787
|
+
puts "Origin: #{idrac['origin']}".cyan
|
788
|
+
puts "Speed: #{idrac['speed_mbps']} Mbps".cyan
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
desc "system_events", "Get system event logs"
|
793
|
+
map "system:events" => :system_events
|
794
|
+
def system_events
|
795
|
+
with_idrac_client do |client|
|
796
|
+
events = client.system_event_logs
|
797
|
+
|
798
|
+
puts "\nSystem Events (#{events.size}):".green.bold
|
799
|
+
events.each do |event|
|
800
|
+
severity_color = case event[:severity]
|
801
|
+
when "Critical" then :red
|
802
|
+
when "Warning" then :yellow
|
803
|
+
else :cyan
|
804
|
+
end
|
805
|
+
|
806
|
+
puts "#{event[:id]} - #{event[:created]} - #{event[:severity].send(severity_color)} - #{event[:message]}"
|
807
|
+
end
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
# Virtual media commands
|
812
|
+
desc "vmedia_status", "Get virtual media status"
|
813
|
+
map "vmedia:status" => :vmedia_status
|
814
|
+
def vmedia_status
|
815
|
+
with_idrac_client do |client|
|
816
|
+
media = client.virtual_media
|
817
|
+
|
818
|
+
puts "\nVirtual Media Status:".green.bold
|
819
|
+
|
820
|
+
if media.empty?
|
821
|
+
puts "No virtual media devices found".yellow
|
822
|
+
return
|
823
|
+
end
|
824
|
+
|
825
|
+
media.each do |m|
|
826
|
+
status = m[:inserted] ? "Inserted: #{m[:image]}".green : "Not Inserted".yellow
|
827
|
+
puts "#{m[:device]}: #{status}"
|
828
|
+
end
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
desc "vmedia_insert URL", "Insert virtual media from URL"
|
833
|
+
map "vmedia:insert" => :vmedia_insert
|
834
|
+
method_option :device, type: :string, default: "CD", desc: "Device type (CD or RemovableDisk)"
|
835
|
+
def vmedia_insert(url)
|
836
|
+
with_idrac_client do |client|
|
837
|
+
puts "Inserting media from #{url} into #{options[:device]}..."
|
838
|
+
client.insert_virtual_media(url, device: options[:device])
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
desc "vmedia_eject", "Eject virtual media"
|
843
|
+
map "vmedia:eject" => :vmedia_eject
|
844
|
+
method_option :device, type: :string, default: "CD", desc: "Device to eject (CD or RemovableDisk)"
|
845
|
+
def vmedia_eject
|
846
|
+
with_idrac_client do |client|
|
847
|
+
client.eject_virtual_media(device: options[:device])
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
desc "vmedia_boot_once", "Set system to boot from virtual media once"
|
852
|
+
map "vmedia:boot_once" => :vmedia_boot_once
|
853
|
+
def vmedia_boot_once
|
854
|
+
with_idrac_client do |client|
|
855
|
+
client.set_one_time_virtual_media_boot
|
856
|
+
puts "System configured to boot from virtual media on next restart"
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
# Boot management commands
|
861
|
+
desc "boot_options", "Get current boot options"
|
862
|
+
map "boot:options" => :boot_options
|
863
|
+
def boot_options
|
864
|
+
with_idrac_client do |client|
|
865
|
+
options = client.get_bios_boot_options
|
866
|
+
|
867
|
+
if options == false
|
868
|
+
puts "Failed to get boot options. System may not be in UEFI mode.".red
|
869
|
+
return
|
870
|
+
end
|
871
|
+
|
872
|
+
puts "\nBoot Options:".green.bold
|
873
|
+
|
874
|
+
# Display enabled boot options (current boot order)
|
875
|
+
puts "Current Boot Order:".cyan
|
876
|
+
options[:boot_order].each_with_index do |opt, idx|
|
877
|
+
puts " #{idx+1}. #{opt}"
|
878
|
+
end
|
879
|
+
|
880
|
+
# Display all available boot options
|
881
|
+
puts "\nAvailable Boot Options:".cyan
|
882
|
+
options[:boot_options].each do |opt|
|
883
|
+
enabled = options[:boot_order].include?(opt)
|
884
|
+
status = enabled ? "Enabled".green : "Disabled".yellow
|
885
|
+
puts " #{opt}: #{status}"
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
desc "boot_enable_uefi", "Enable UEFI boot mode"
|
891
|
+
map "boot:enable_uefi" => :boot_enable_uefi
|
892
|
+
def boot_enable_uefi
|
893
|
+
with_idrac_client do |client|
|
894
|
+
client.ensure_uefi_boot
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
desc "boot_set_hd_first", "Set boot order with hard drive first"
|
899
|
+
map "boot:set_hd_first" => :boot_set_hd_first
|
900
|
+
def boot_set_hd_first
|
901
|
+
with_idrac_client do |client|
|
902
|
+
client.set_boot_order_hd_first
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
desc "boot_ignore_errors", "Configure BIOS to ignore boot errors"
|
907
|
+
map "boot:ignore_errors" => :boot_ignore_errors
|
908
|
+
method_option :enable, type: :boolean, default: true, desc: "Enable or disable ignoring boot errors"
|
909
|
+
def boot_ignore_errors
|
910
|
+
with_idrac_client do |client|
|
911
|
+
client.set_bios_ignore_errors(options[:enable])
|
912
|
+
if options[:enable]
|
913
|
+
puts "BIOS configured to ignore boot errors".green
|
914
|
+
else
|
915
|
+
puts "BIOS configured to halt on boot errors".green
|
916
|
+
end
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
desc "boot_power_optimization", "Configure BIOS for OS-controlled power management"
|
921
|
+
map "boot:power_optimization" => :boot_power_optimization
|
922
|
+
def boot_power_optimization
|
923
|
+
with_idrac_client do |client|
|
924
|
+
client.set_bios_os_power_control
|
925
|
+
puts "BIOS configured for OS power management optimization".green
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
desc "boot_source_override", "Get current boot source override settings"
|
930
|
+
map "boot:source_override" => :boot_source_override
|
931
|
+
def boot_source_override
|
932
|
+
with_idrac_client do |client|
|
933
|
+
client.get_boot_source_override
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
# Test commands
|
938
|
+
desc "test_live", "Test all major functionality of the gem on a real system"
|
939
|
+
map "test:live" => :test_live
|
940
|
+
method_option :timeout, type: :numeric, default: 10, desc: "Connection timeout in seconds"
|
941
|
+
def test_live
|
942
|
+
test_results = {}
|
943
|
+
|
944
|
+
puts "Running comprehensive live test of idrac gem functionality...".green.bold
|
945
|
+
puts "=" * 80
|
946
|
+
puts "This will test all read-only operations of the gem. No changes will be made to your system.".yellow
|
947
|
+
puts "Using connection timeout of #{options[:timeout]} seconds (use --timeout=X to change)".yellow
|
948
|
+
puts "=" * 80
|
949
|
+
|
950
|
+
with_idrac_client do |client|
|
951
|
+
# Set higher verbosity temporarily for comprehensive logging
|
952
|
+
original_verbosity = client.verbosity
|
953
|
+
|
954
|
+
# Configure verbosity level based on user options
|
955
|
+
if options[:very_verbose] || options[:debug]
|
956
|
+
client.verbosity = options[:debug] ? 3 : 2
|
957
|
+
puts "HTTP request/response details will be shown (verbosity level: #{client.verbosity})".cyan
|
958
|
+
else
|
959
|
+
client.verbosity = options[:verbose] ? 1 : 0
|
960
|
+
puts "HTTP request/response details hidden (use -vv or -vvv for details)".cyan
|
961
|
+
|
962
|
+
# Silence Faraday logging for lower verbosity levels
|
963
|
+
if defined?(Faraday.default_connection) && Faraday.default_connection
|
964
|
+
begin
|
965
|
+
Faraday.default_connection.builder.handlers.each do |handler|
|
966
|
+
if defined?(Faraday::Response::Logger) && handler.klass == Faraday::Response::Logger &&
|
967
|
+
handler.instance_variable_defined?(:@logger) && handler.instance_variable_get(:@logger)
|
968
|
+
handler.instance_variable_get(:@logger).level = Logger::WARN
|
969
|
+
end
|
970
|
+
end
|
971
|
+
rescue => e
|
972
|
+
puts "Warning: Unable to configure logging: #{e.message}".yellow if options[:verbose]
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
# Also try to silence the built-in logger in the client
|
977
|
+
if client.respond_to?(:connection) && client.connection.respond_to?(:builder)
|
978
|
+
begin
|
979
|
+
client.connection.builder.handlers.each do |handler|
|
980
|
+
if defined?(Faraday::Response::Logger) && handler.klass == Faraday::Response::Logger &&
|
981
|
+
handler.instance_variable_defined?(:@logger) && handler.instance_variable_get(:@logger)
|
982
|
+
handler.instance_variable_get(:@logger).level = Logger::WARN
|
983
|
+
end
|
984
|
+
end
|
985
|
+
rescue => e
|
986
|
+
puts "Warning: Unable to configure client logging: #{e.message}".yellow if options[:verbose]
|
987
|
+
end
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
# Set timeout for connection - modify the connection object if it's available
|
992
|
+
begin
|
993
|
+
if client.respond_to?(:connection) && client.connection.respond_to?(:options)
|
994
|
+
client.connection.options.timeout = options[:timeout]
|
995
|
+
client.connection.options.open_timeout = options[:timeout]
|
996
|
+
end
|
997
|
+
rescue => e
|
998
|
+
puts "Warning: Unable to set timeout options: #{e.message}".yellow
|
999
|
+
end
|
1000
|
+
|
1001
|
+
begin
|
1002
|
+
# Establish a session that will be used for all tests
|
1003
|
+
# Force the creation of a new session and test it works
|
1004
|
+
if client.session.respond_to?(:delete)
|
1005
|
+
client.session.delete rescue nil
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
session_created = client.session.create
|
1009
|
+
if !session_created && !client.direct_mode
|
1010
|
+
puts "Failed to establish a session, falling back to direct mode".yellow
|
1011
|
+
client.direct_mode = true
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Basic connection test
|
1015
|
+
puts "\n[TEST] Basic connection".cyan.bold
|
1016
|
+
begin
|
1017
|
+
version = client.redfish_version
|
1018
|
+
puts "✓ Connection successful, Redfish version: #{version}".green
|
1019
|
+
test_results["connection"] = { status: "PASS", message: "Connected to iDRAC successfully" }
|
1020
|
+
rescue Faraday::TimeoutError => e
|
1021
|
+
puts "✗ Connection timed out after #{options[:timeout]} seconds. Check host and network.".red
|
1022
|
+
test_results["connection"] = { status: "FAIL", message: "Connection timeout: #{e.message}" }
|
1023
|
+
puts "No further tests will be run until connection is established.".red
|
1024
|
+
break
|
1025
|
+
rescue => e
|
1026
|
+
puts "✗ Connection failed: #{e.message}".red
|
1027
|
+
test_results["connection"] = { status: "FAIL", message: e.message }
|
1028
|
+
puts "No further tests will be run until connection is established.".red
|
1029
|
+
break
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# System info test
|
1033
|
+
puts "\n[TEST] System information".cyan.bold
|
1034
|
+
begin
|
1035
|
+
response = client.authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
|
1036
|
+
system_info = JSON.parse(response.body)
|
1037
|
+
puts "✓ System info: #{system_info['Model']} (#{system_info['SKU']})".green
|
1038
|
+
test_results["system_info"] = { status: "PASS", message: "Retrieved system information successfully" }
|
1039
|
+
rescue => e
|
1040
|
+
puts "✗ System info failed: #{e.message}".red
|
1041
|
+
test_results["system_info"] = { status: "FAIL", message: e.message }
|
1042
|
+
# If we can't get basic system info, stop testing
|
1043
|
+
break
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
# Power tests
|
1047
|
+
puts "\n[TEST] Power management".cyan.bold
|
1048
|
+
begin
|
1049
|
+
power_state = client.get_power_state
|
1050
|
+
puts "✓ Power state: #{power_state}".green
|
1051
|
+
|
1052
|
+
watts = client.get_power_usage_watts
|
1053
|
+
puts "✓ Power usage: #{watts} watts".green
|
1054
|
+
test_results["power"] = { status: "PASS", message: "Retrieved power information successfully" }
|
1055
|
+
rescue => e
|
1056
|
+
puts "✗ Power tests failed: #{e.message}".red
|
1057
|
+
test_results["power"] = { status: "FAIL", message: e.message }
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
# Storage tests
|
1061
|
+
puts "\n[TEST] Storage management".cyan.bold
|
1062
|
+
begin
|
1063
|
+
controller = client.controller
|
1064
|
+
puts "✓ Storage controller: #{controller['Name']} (#{controller['Model']})".green
|
1065
|
+
|
1066
|
+
drives = client.drives(controller)
|
1067
|
+
puts "✓ Found #{drives.size} physical drives".green
|
1068
|
+
|
1069
|
+
volumes = client.volumes(controller)
|
1070
|
+
puts "✓ Found #{volumes.size} virtual disks".green
|
1071
|
+
test_results["storage"] = { status: "PASS", message: "Retrieved storage information successfully" }
|
1072
|
+
rescue => e
|
1073
|
+
puts "✗ Storage tests failed: #{e.message}".red
|
1074
|
+
test_results["storage"] = { status: "FAIL", message: e.message }
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
# System component tests
|
1078
|
+
puts "\n[TEST] System components".cyan.bold
|
1079
|
+
begin
|
1080
|
+
begin
|
1081
|
+
memory = client.memory
|
1082
|
+
puts "✓ Memory: #{client.total_memory_human(memory)}".green
|
1083
|
+
memory_test_status = "PASS"
|
1084
|
+
memory_test_message = "Retrieved memory information successfully"
|
1085
|
+
rescue => e
|
1086
|
+
puts "✗ Memory test failed: #{e.message}".red
|
1087
|
+
memory_test_status = "FAIL"
|
1088
|
+
memory_test_message = e.message
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
begin
|
1092
|
+
psus = client.psus
|
1093
|
+
puts "✓ Found #{psus.size} power supplies".green
|
1094
|
+
psu_test_status = "PASS"
|
1095
|
+
psu_test_message = "Retrieved PSU information successfully"
|
1096
|
+
rescue => e
|
1097
|
+
puts "✗ PSU test failed: #{e.message}".red
|
1098
|
+
psu_test_status = "FAIL"
|
1099
|
+
psu_test_message = e.message
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
begin
|
1103
|
+
fans = client.fans
|
1104
|
+
puts "✓ Found #{fans.size} fans".green
|
1105
|
+
fan_test_status = "PASS"
|
1106
|
+
fan_test_message = "Retrieved fan information successfully"
|
1107
|
+
rescue => e
|
1108
|
+
puts "✗ Fan test failed: #{e.message}".red
|
1109
|
+
fan_test_status = "FAIL"
|
1110
|
+
fan_test_message = e.message
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
begin
|
1114
|
+
nics = client.nics
|
1115
|
+
puts "✓ Found #{nics.size} network interfaces".green
|
1116
|
+
nic_test_status = "PASS"
|
1117
|
+
nic_test_message = "Retrieved NIC information successfully"
|
1118
|
+
rescue => e
|
1119
|
+
puts "✗ NIC test failed: #{e.message}".red
|
1120
|
+
nic_test_status = "FAIL"
|
1121
|
+
nic_test_message = e.message
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
test_results["memory"] = { status: memory_test_status, message: memory_test_message }
|
1125
|
+
test_results["psus"] = { status: psu_test_status, message: psu_test_message }
|
1126
|
+
test_results["fans"] = { status: fan_test_status, message: fan_test_message }
|
1127
|
+
test_results["nics"] = { status: nic_test_status, message: nic_test_message }
|
1128
|
+
rescue => e
|
1129
|
+
puts "✗ System component tests failed: #{e.message}".red
|
1130
|
+
test_results["system_components"] = { status: "FAIL", message: e.message }
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Virtual media tests
|
1134
|
+
puts "\n[TEST] Virtual media".cyan.bold
|
1135
|
+
begin
|
1136
|
+
media = client.virtual_media
|
1137
|
+
puts "✓ Found #{media.size} virtual media devices".green
|
1138
|
+
test_results["virtual_media"] = { status: "PASS", message: "Retrieved virtual media information successfully" }
|
1139
|
+
rescue => e
|
1140
|
+
puts "✗ Virtual media test failed: #{e.message}".red
|
1141
|
+
test_results["virtual_media"] = { status: "FAIL", message: e.message }
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
# Boot management tests
|
1145
|
+
puts "\n[TEST] Boot management".cyan.bold
|
1146
|
+
begin
|
1147
|
+
boot_options = client.get_bios_boot_options
|
1148
|
+
puts "✓ Retrieved BIOS boot options".green
|
1149
|
+
|
1150
|
+
boot_override = client.get_boot_source_override
|
1151
|
+
puts "✓ Boot source override: #{boot_override}".green
|
1152
|
+
test_results["boot"] = { status: "PASS", message: "Retrieved boot information successfully" }
|
1153
|
+
rescue => e
|
1154
|
+
puts "✗ Boot management test failed: #{e.message}".red
|
1155
|
+
test_results["boot"] = { status: "FAIL", message: e.message }
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
# Jobs and tasks
|
1159
|
+
puts "\n[TEST] Jobs and tasks".cyan.bold
|
1160
|
+
begin
|
1161
|
+
jobs = client.jobs
|
1162
|
+
puts "✓ Retrieved job queue information".green
|
1163
|
+
test_results["jobs"] = { status: "PASS", message: "Retrieved job information successfully" }
|
1164
|
+
rescue => e
|
1165
|
+
puts "✗ Jobs test failed: #{e.message}".red
|
1166
|
+
test_results["jobs"] = { status: "FAIL", message: e.message }
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
rescue => e
|
1170
|
+
puts "\n✗ Unexpected error during testing: #{e.message}".red.bold
|
1171
|
+
puts e.backtrace.join("\n").red if client.verbosity >= 2
|
1172
|
+
ensure
|
1173
|
+
# Clean up - make sure we delete the session to avoid leaving orphaned sessions
|
1174
|
+
begin
|
1175
|
+
if client.session.respond_to?(:delete) && client.session.x_auth_token
|
1176
|
+
client.session.delete
|
1177
|
+
end
|
1178
|
+
rescue
|
1179
|
+
# Ignore errors during cleanup
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# Restore original verbosity
|
1183
|
+
client.verbosity = original_verbosity
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
# Print summary
|
1187
|
+
puts "\n" + "=" * 80
|
1188
|
+
puts "TEST SUMMARY:".green.bold
|
1189
|
+
puts "=" * 80
|
1190
|
+
|
1191
|
+
pass_count = test_results.count { |_, v| v[:status] == "PASS" }
|
1192
|
+
fail_count = test_results.count { |_, v| v[:status] == "FAIL" }
|
1193
|
+
|
1194
|
+
test_results.each do |test_name, result|
|
1195
|
+
status_color = result[:status] == "PASS" ? :green : :red
|
1196
|
+
puts "#{test_name.ljust(20)}: #{result[:status].send(status_color)} - #{result[:message]}"
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
puts "=" * 80
|
1200
|
+
puts "OVERALL: #{pass_count} passed, #{fail_count} failed".send(fail_count == 0 ? :green : :yellow).bold
|
1201
|
+
puts "=" * 80
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
|
457
1205
|
private
|
458
1206
|
|
459
1207
|
def with_idrac_client
|
@@ -509,7 +1257,9 @@ module IDRAC
|
|
509
1257
|
port: options[:port],
|
510
1258
|
use_ssl: !options[:no_ssl],
|
511
1259
|
verify_ssl: options[:verify_ssl],
|
512
|
-
auto_delete_sessions: options[:auto_delete_sessions]
|
1260
|
+
auto_delete_sessions: options[:auto_delete_sessions],
|
1261
|
+
retry_count: options[:retries],
|
1262
|
+
retry_delay: options[:retry_delay]
|
513
1263
|
)
|
514
1264
|
end
|
515
1265
|
end
|