idrac 0.7.1 → 0.7.2

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.
data/bin/idrac CHANGED
@@ -121,7 +121,8 @@ module IDRAC
121
121
  map "lifecycle:status" => :lifecycle_status
122
122
  def lifecycle_status
123
123
  with_idrac_client do |client|
124
- client.get_lifecycle_status
124
+ status = client.get_lifecycle_status
125
+ puts "Lifecycle Controller is #{status ? 'Enabled'.green : 'Disabled'.red}"
125
126
  end
126
127
  end
127
128
 
@@ -335,6 +336,75 @@ module IDRAC
335
336
  end
336
337
  end
337
338
 
339
+ desc "sessions_show", "Show active iDRAC sessions"
340
+ map "sessions:show" => :sessions_show
341
+ def sessions_show
342
+ with_idrac_client do |client|
343
+ begin
344
+ sessions_url = client.session.send(:determine_session_endpoint)
345
+ puts "Retrieving sessions from: #{sessions_url}".light_yellow if options[:debug]
346
+
347
+ response = client.authenticated_request(:get, sessions_url)
348
+
349
+ if response.status == 200
350
+ sessions_data = JSON.parse(response.body)
351
+ if sessions_data['Members'] && sessions_data['Members'].any?
352
+ puts "Active iDRAC Sessions: #{sessions_data['Members'].count}".green.bold
353
+
354
+ # Get details for each session
355
+ sessions_data['Members'].each_with_index do |session, index|
356
+ session_url = session['@odata.id']
357
+ puts "Retrieving session details from: #{session_url}".light_yellow if options[:debug]
358
+
359
+ session_response = client.authenticated_request(:get, session_url)
360
+
361
+ if session_response.status == 200
362
+ session_detail = JSON.parse(session_response.body)
363
+ puts "Session #{index + 1}:".light_cyan
364
+ puts " ID: #{session_detail['Id']}".light_cyan
365
+ puts " Username: #{session_detail['UserName']}".light_cyan
366
+ puts " Created: #{session_detail['Created']}".light_cyan
367
+ else
368
+ puts "Session #{index + 1}: Unable to retrieve details (Status: #{session_response.status})".light_red
369
+ end
370
+ end
371
+ else
372
+ puts "No active sessions found".yellow
373
+ end
374
+ else
375
+ puts "Failed to retrieve sessions. Status code: #{response.status}".red
376
+ puts "Error response: #{response.body[0..200]}...".light_red if response.body && !response.body.empty?
377
+ end
378
+ rescue Faraday::ConnectionFailed => e
379
+ puts "Connection to iDRAC failed: #{e.message}".red
380
+ rescue JSON::ParserError => e
381
+ puts "Failed to parse iDRAC response: #{e.message}".red
382
+ rescue => e
383
+ puts "Error: #{e.message}".red
384
+ end
385
+ end
386
+ end
387
+
388
+ desc "sessions_clear", "Clear all iDRAC sessions"
389
+ map "sessions:clear" => :sessions_clear
390
+ def sessions_clear
391
+ with_idrac_client do |client|
392
+ begin
393
+ puts "Attempting to clear all iDRAC sessions...".yellow
394
+ if client.session.force_clear_sessions
395
+ puts "Successfully cleared all iDRAC sessions".green
396
+ else
397
+ puts "Failed to clear iDRAC sessions".red
398
+ puts "You may need to manually delete sessions through the iDRAC web interface".yellow
399
+ end
400
+ rescue Faraday::ConnectionFailed => e
401
+ puts "Connection to iDRAC failed: #{e.message}".red
402
+ rescue => e
403
+ puts "Error: #{e.message}".red
404
+ end
405
+ end
406
+ end
407
+
338
408
  desc "power_state", "Get current server power state"
339
409
  map "power:state" => :power_state
340
410
  def power_state
@@ -384,11 +454,11 @@ module IDRAC
384
454
  license = client.license_info
385
455
  if license
386
456
  puts "\nLicense Information:".green.bold
387
- puts "Description: #{license.Description}".cyan
388
- puts "License Type: #{license.LicenseType}".cyan
457
+ puts "Description: #{license["Description"]}".cyan
458
+ puts "License Type: #{license["LicenseType"]}".cyan
389
459
  puts "License Version: #{client.license_version || 'Unknown'}".cyan
390
- puts "Removable: #{license.Removable}".cyan
391
- puts "Status: #{license&.Status&.Health || 'Unknown'}".cyan
460
+ puts "Removable: #{license["Removable"]}".cyan
461
+ puts "Status: #{license["Status"] && license["Status"]["Health"] || 'Unknown'}".cyan
392
462
  else
393
463
  puts "No license information available".yellow
394
464
  end
@@ -437,8 +507,8 @@ module IDRAC
437
507
 
438
508
  # Storage
439
509
  { name: "Storage Controller", method: -> (c) { c.controller } },
440
- { name: "Physical Drives", method: -> (c) { controller = c.controller; c.drives(controller) } },
441
- { name: "Virtual Disks", method: -> (c) { controller = c.controller; c.volumes(controller) } },
510
+ { name: "Physical Drives", method: -> (c) { controller = c.controller; c.drives(controller["@odata.id"]) } },
511
+ { name: "Virtual Disks", method: -> (c) { controller = c.controller; c.volumes(controller["@odata.id"]) } },
442
512
 
443
513
  # System components
444
514
  { name: "Memory Info", method: -> (c) { c.memory } },
@@ -553,14 +623,29 @@ module IDRAC
553
623
  # Storage commands
554
624
  desc "storage_controller", "Get storage controller information"
555
625
  map "storage:controller" => :storage_controller
626
+ method_option :name_pattern, type: :string, desc: "Filter controllers by name pattern (defaults to PERC)"
627
+ method_option :prefer_most_drives_by_count, type: :boolean, default: false, desc: "Prefer controller with the most drives"
628
+ method_option :prefer_most_drives_by_size, type: :boolean, default: false, desc: "Prefer controller with the largest total drive capacity"
556
629
  def storage_controller
557
630
  with_idrac_client do |client|
558
- controller = client.controller
631
+ find_controller_args = {}
632
+ find_controller_args[:name_pattern] = options[:name_pattern] if options[:name_pattern]
633
+ find_controller_args[:prefer_most_drives_by_count] = options[:prefer_most_drives_by_count]
634
+ find_controller_args[:prefer_most_drives_by_size] = options[:prefer_most_drives_by_size]
635
+
636
+ controller = client.find_controller(**find_controller_args)
637
+
638
+ if !controller
639
+ puts "No storage controllers found".red
640
+ return
641
+ end
642
+
559
643
  puts "\nStorage Controller Summary:".green.bold
560
- puts "Name: #{controller.name}".cyan
561
- puts "Model: #{controller.model}".cyan
562
- puts "Health: #{controller.status&.health || 'Unknown'}".cyan
563
- puts "Manufacturer: #{controller.manufacturer}".cyan
644
+ puts "Name: #{controller["name"]}".cyan
645
+ puts "Model: #{controller["model"]}".cyan
646
+ puts "Health: #{controller["status"] || 'Unknown'}".cyan
647
+ puts "Manufacturer: #{controller["manufacturer"]}".cyan
648
+ puts "Drives: #{controller["drives_count"]}".cyan
564
649
 
565
650
  if client.controller_encryption_capable?(controller)
566
651
  puts "Encryption Capable: Yes".green
@@ -581,17 +666,17 @@ module IDRAC
581
666
  puts "-" * 80
582
667
 
583
668
  controllers.each do |controller|
584
- puts "Controller: #{controller.name}".cyan.bold
585
- puts " Model: #{controller.model}"
586
- puts " Status: #{controller.status}"
587
- puts " Drives: #{controller.drives_count}"
588
- puts " Firmware: #{controller.firmware_version}"
589
- puts " Type: #{controller.controller_type}"
590
- puts " PCI Slot: #{controller.pci_slot}"
669
+ puts "Controller: #{controller["name"]}".cyan.bold
670
+ puts " Model: #{controller["model"]}"
671
+ puts " Status: #{controller["status"]}"
672
+ puts " Drives: #{controller["drives_count"]}"
673
+ puts " Firmware: #{controller["firmware_version"]}"
674
+ puts " Type: #{controller["controller_type"]}"
675
+ puts " PCI Slot: #{controller["pci_slot"]}"
591
676
 
592
- if controller.encryption_capability
593
- puts " Encryption: #{controller.encryption_capability}"
594
- puts " Encryption Mode: #{controller.encryption_mode || 'Disabled'}"
677
+ if controller["encryption_capability"]
678
+ puts " Encryption: #{controller["encryption_capability"]}"
679
+ puts " Encryption Mode: #{controller["encryption_mode"] || 'Disabled'}"
595
680
  end
596
681
 
597
682
  puts
@@ -601,26 +686,43 @@ module IDRAC
601
686
  end
602
687
  end
603
688
 
604
- desc "storage_drives", "Get physical drive information"
689
+ desc "storage_drives", "Get physical drive information across all controllers"
605
690
  map "storage:drives" => :storage_drives
691
+ method_option :name_pattern, type: :string, desc: "Filter controllers by name pattern (defaults to PERC)"
692
+ method_option :prefer_most_drives_by_count, type: :boolean, default: false, desc: "Prefer controller with more drives"
693
+ method_option :prefer_most_drives_by_size, type: :boolean, default: false, desc: "Prefer controller with the largest total drive capacity"
606
694
  def storage_drives
607
695
  with_idrac_client do |client|
608
- controller = client.controller
609
- drives = client.drives(controller)
696
+ find_controller_args = {}
697
+ find_controller_args[:name_pattern] = options[:name_pattern] if options[:name_pattern]
698
+ find_controller_args[:prefer_most_drives_by_count] = options[:prefer_most_drives_by_count]
699
+ find_controller_args[:prefer_most_drives_by_size] = options[:prefer_most_drives_by_size]
700
+
701
+ controller = client.find_controller(**find_controller_args)
702
+
703
+ if !controller
704
+ puts "No storage controllers found".red
705
+ return
706
+ end
707
+
708
+ drives = client.drives(controller["@odata.id"])
610
709
 
611
710
  puts "\nPhysical Drives (#{drives.size}):".green.bold
612
711
  drives.each do |drive|
613
- capacity_gb = drive.capacity_bytes.to_f / (1024**3)
614
- health_color = drive.health == "OK" ? :green : :red
712
+ capacity_gb = drive["capacity_bytes"].to_f / (1024**3)
713
+ health_color = drive["health"] == "OK" ? :green : :red
615
714
 
616
- puts "#{drive.name}:".bold
617
- puts " Model: #{drive.model}".cyan
618
- puts " Health: #{drive.health.send(health_color)}"
715
+ puts "#{drive["name"]}:".bold
716
+ puts " Model: #{drive["model"]}".cyan
717
+ puts " Health: #{drive["health"].send(health_color)}"
619
718
  puts " Capacity: #{capacity_gb.round(2)} GB".cyan
620
- puts " Media Type: #{drive.media_type}".cyan
621
- puts " Serial: #{drive.serial}".cyan
622
- if drive.encryption_ability
623
- puts " Encryption: #{drive.encryption_ability}".cyan
719
+ puts " Media Type: #{drive["media_type"]}".cyan
720
+ if drive["life_left_percent"]
721
+ puts " Life Left: #{drive["life_left_percent"]}%".cyan
722
+ end
723
+ puts " Serial: #{drive["serial"]}".cyan
724
+ if drive["encryption_ability"]
725
+ puts " Encryption: #{drive["encryption_ability"]}".cyan
624
726
  end
625
727
  puts ""
626
728
  end
@@ -629,53 +731,84 @@ module IDRAC
629
731
 
630
732
  desc "storage_volumes", "Get virtual disk information"
631
733
  map "storage:volumes" => :storage_volumes
734
+ method_option :name_pattern, type: :string, desc: "Filter controllers by name pattern (defaults to PERC)"
735
+ method_option :prefer_most_drives_by_count, type: :boolean, default: false, desc: "Prefer controller with the most drives"
736
+ method_option :prefer_most_drives_by_size, type: :boolean, default: false, desc: "Prefer controller with the largest total drive capacity"
632
737
  def storage_volumes
633
738
  with_idrac_client do |client|
634
- controller = client.controller
635
- volumes = client.volumes(controller)
739
+ find_controller_args = {}
740
+ find_controller_args[:name_pattern] = options[:name_pattern] if options[:name_pattern]
741
+ find_controller_args[:prefer_most_drives_by_count] = options[:prefer_most_drives_by_count]
742
+ find_controller_args[:prefer_most_drives_by_size] = options[:prefer_most_drives_by_size]
743
+
744
+ controller = client.find_controller(**find_controller_args)
745
+
746
+ if !controller
747
+ puts "No storage controllers found".red
748
+ return
749
+ end
750
+
751
+ volumes = client.volumes(controller["@odata.id"])
636
752
 
637
753
  puts "\nVirtual Disks (#{volumes.size}):".green.bold
638
754
  volumes.each do |volume|
639
- capacity_gb = volume.capacity_bytes.to_f / (1024**3)
640
- health_color = volume.health == "OK" ? :green : :yellow
755
+ capacity_gb = volume["capacity_bytes"].to_f / (1024**3)
756
+ health_color = volume["health"] == "OK" ? :green : :yellow
641
757
 
642
- puts "#{volume.name}:".bold
643
- puts " RAID Type: #{volume.raid_level || volume.volume_type}".cyan
644
- puts " Health: #{volume.health.send(health_color)}"
758
+ puts "#{volume["name"]}:".bold
759
+ puts " RAID Type: #{volume["raid_level"] || volume["volume_type"]}".cyan
760
+ puts " Health: #{volume["health"].send(health_color)}"
645
761
  puts " Capacity: #{capacity_gb.round(2)} GB".cyan
646
- puts " Stripe Size: #{volume.stripe_size}".cyan
647
- puts " Read Cache: #{volume.read_cache_policy}".cyan
648
- puts " Write Cache: #{volume.write_cache_policy}".cyan
649
- puts " FastPath: #{volume.fastpath == 'enabled' ? 'Enabled'.green : 'Disabled'.yellow}"
650
- puts " Encrypted: #{volume.encrypted ? 'Yes'.green : 'No'.yellow}" if volume.encrypted != nil
762
+ puts " Stripe Size: #{volume["stripe_size"]}".cyan
763
+ puts " Read Cache: #{volume["read_cache_policy"]}".cyan
764
+ puts " Write Cache: #{volume["write_cache_policy"]}".cyan
765
+ puts " FastPath: #{volume["fastpath"] == 'enabled' ? 'Enabled'.green : 'Disabled'.yellow}"
766
+ puts " Encrypted: #{volume["encrypted"] ? 'Yes'.green : 'No'.yellow}" if volume["encrypted"] != nil
651
767
 
652
- if volume.progress
653
- puts " Progress: #{volume.progress}%".cyan
654
- puts " Operation: #{volume.message}".cyan
768
+ if volume["progress"]
769
+ puts " Progress: #{volume["progress"]}%".cyan
770
+ puts " Operation: #{volume["message"]}".cyan
655
771
  end
656
772
  puts ""
657
773
  end
658
774
  end
659
775
  end
660
776
 
661
- desc "storage_create_volume", "Create a new virtual disk"
777
+ desc "storage_create_volume", "Create a new virtual disk, requires a controller"
662
778
  map "storage:create_volume" => :storage_create_volume
779
+ map "storage:create:volume" => :storage_create_volume
663
780
  method_option :name, type: :string, default: "vssd0", desc: "Volume name"
664
781
  method_option :raid, type: :string, default: "RAID5", desc: "RAID type (RAID0, RAID1, RAID5, RAID6, RAID10)"
782
+ method_option :y, type: :boolean, default: false, desc: "Automatically confirm operation without prompting"
665
783
  def storage_create_volume
666
784
  with_idrac_client do |client|
667
- controller = client.controller
668
- drives = client.drives(controller)
785
+ # Use find_controller instead of controller
786
+ controller = client.find_controller
787
+
788
+ if !controller
789
+ puts "No storage controllers found".red
790
+ return
791
+ end
792
+
793
+ # Print system identification information
794
+ print_system_identification(client, "storage")
669
795
 
670
- # Confirm with the user before proceeding
796
+ drives = client.drives(controller["@odata.id"])
797
+
798
+ # Confirm with the user before proceeding, unless -y is specified
671
799
  puts "This will create a new #{options[:raid]} volume named '#{options[:name]}' using #{drives.size} drives.".yellow
672
800
  puts "All data on these drives will be lost!".red.bold
673
801
 
674
- print "Do you want to continue? (y/n): "
675
- confirmation = $stdin.gets.chomp.downcase
802
+ if options[:y]
803
+ puts "Automatically confirming operation (-y flag provided)".yellow
804
+ confirmation = 'y'
805
+ else
806
+ print "Do you want to continue? (y/n): "
807
+ confirmation = $stdin.gets.chomp.downcase
808
+ end
676
809
 
677
810
  if confirmation == 'y'
678
- client.create_virtual_disk(controller.odata_id, drives, name: options[:name], raid_type: options[:raid])
811
+ client.create_virtual_disk(controller_id: controller["@odata.id"], drives: drives, name: options[:name], raid_type: options[:raid])
679
812
  puts "Volume created successfully".green
680
813
  else
681
814
  puts "Operation cancelled".yellow
@@ -685,45 +818,52 @@ module IDRAC
685
818
 
686
819
  desc "storage_delete_volume", "Delete a virtual disk"
687
820
  map "storage:delete_volume" => :storage_delete_volume
821
+ method_option :y, type: :boolean, default: false, desc: "Automatically confirm operation without prompting"
688
822
  def storage_delete_volume
689
823
  with_idrac_client do |client|
690
- controller = client.controller
691
- volumes = client.volumes(controller)
824
+ # Use find_controller instead of controller
825
+ controller = client.find_controller
692
826
 
693
- if volumes.empty?
694
- puts "No volumes found to delete".yellow
827
+ if !controller
828
+ puts "No storage controllers found".red
695
829
  return
696
830
  end
697
831
 
698
- puts "Available volumes:".green
699
- volumes.each_with_index do |vol, idx|
700
- puts "#{idx+1}. #{vol.name} (#{vol.raid_level || vol.volume_type}, #{(vol.capacity_bytes.to_f / (1024**3)).round(2)} GB)"
832
+ # Print system identification information
833
+ print_system_identification(client, "storage")
834
+
835
+ volumes = client.volumes(controller["@odata.id"])
836
+
837
+ if volumes.empty?
838
+ puts "No volumes found".red
839
+ return
701
840
  end
702
841
 
703
- print "Enter the number of the volume to delete (or 'q' to quit): "
704
- input = $stdin.gets.chomp
842
+ volume_to_delete = volumes.find { |v| v["Id"] == options[:id] }
705
843
 
706
- if input.downcase == 'q'
707
- puts "Operation cancelled".yellow
844
+ if volume_to_delete.nil?
845
+ volume_ids = volumes.map { |v| v["Id"] }.join(", ")
846
+ puts "Volume with ID #{options[:id]} not found".red
847
+ puts "Available volume IDs: #{volume_ids}".yellow
708
848
  return
709
849
  end
710
850
 
711
- index = input.to_i - 1
712
- if index >= 0 && index < volumes.size
713
- volume = volumes[index]
714
-
715
- puts "You are about to delete '#{volume.name}'. All data will be lost!".red.bold
716
- print "Type the volume name to confirm deletion: "
717
- confirm = $stdin.gets.chomp
718
-
719
- if confirm == volume[:name]
720
- client.delete_volume(volume.data_id)
721
- puts "Volume deleted successfully".green
722
- else
723
- puts "Volume name did not match. Operation cancelled".yellow
724
- end
851
+ puts "This will delete volume '#{volume_to_delete["Name"]}' (ID: #{volume_to_delete["Id"]})".yellow
852
+ puts "All data on this volume will be lost!".red.bold
853
+
854
+ if options[:y]
855
+ puts "Automatically confirming operation (-y flag provided)".yellow
856
+ confirmation = 'y'
857
+ else
858
+ print "Do you want to continue? (y/n): "
859
+ confirmation = $stdin.gets.chomp.downcase
860
+ end
861
+
862
+ if confirmation == 'y'
863
+ client.delete_virtual_disk(volume_to_delete["@odata.id"])
864
+ puts "Volume deleted successfully".green
725
865
  else
726
- puts "Invalid selection".red
866
+ puts "Operation cancelled".yellow
727
867
  end
728
868
  end
729
869
  end
@@ -732,10 +872,19 @@ module IDRAC
732
872
  map "storage:encryption_enable" => :storage_encryption_enable
733
873
  method_option :passphrase, type: :string, desc: "Encryption passphrase"
734
874
  method_option :keyid, type: :string, desc: "Key identifier"
875
+ method_option :y, type: :boolean, default: false, desc: "Automatically confirm operation without prompting"
735
876
  def storage_encryption_enable
736
877
  with_idrac_client do |client|
737
- controller = client.controller
738
- drives = client.drives(controller)
878
+ controller = client.find_controller
879
+
880
+ if !controller
881
+ puts "No storage controllers found".red
882
+ return
883
+ end
884
+
885
+ print_system_identification(client, "storage encryption")
886
+
887
+ drives = client.drives(controller["@odata.id"])
739
888
 
740
889
  if !client.controller_encryption_capable?(controller)
741
890
  puts "This controller does not support encryption".red
@@ -755,6 +904,10 @@ module IDRAC
755
904
  # Get passphrase if not provided
756
905
  passphrase = options[:passphrase]
757
906
  if passphrase.nil?
907
+ if options[:y]
908
+ puts "Error: With -y flag, you must provide a passphrase with --passphrase".red
909
+ return
910
+ end
758
911
  print "Enter encryption passphrase (min 8 characters): "
759
912
  passphrase = $stdin.noecho(&:gets).chomp
760
913
  puts
@@ -763,16 +916,38 @@ module IDRAC
763
916
  # Get keyid if not provided
764
917
  keyid = options[:keyid] || "RAID-Key-#{Time.now.strftime('%Y%m%d')}"
765
918
 
766
- client.enable_local_key_management(controller.odata_id, passphrase: passphrase, keyid: keyid)
919
+ if options[:y]
920
+ puts "Automatically confirming operation (-y flag provided)".yellow
921
+ else
922
+ puts "About to enable encryption using key ID: #{keyid}".yellow
923
+ print "Do you want to continue? (y/n): "
924
+ confirmation = $stdin.gets.chomp.downcase
925
+ if confirmation != 'y'
926
+ puts "Operation cancelled".yellow
927
+ return
928
+ end
929
+ end
930
+
931
+ client.enable_local_key_management(controller_id: controller["@odata.id"], passphrase: passphrase, key_id: keyid)
767
932
  puts "Encryption enabled successfully".green
768
933
  end
769
934
  end
770
935
 
771
936
  desc "storage_encryption_disable", "Disable Self-Encrypting Drive (SED) support"
772
937
  map "storage:encryption_disable" => :storage_encryption_disable
938
+ method_option :y, type: :boolean, default: false, desc: "Automatically confirm operation without prompting"
773
939
  def storage_encryption_disable
774
940
  with_idrac_client do |client|
775
- controller = client.controller
941
+ controller = client.find_controller
942
+
943
+ if !controller
944
+ puts "No storage controllers found".red
945
+ return
946
+ end
947
+
948
+ print_system_identification(client, "storage encryption")
949
+
950
+ drives = client.drives(controller["@odata.id"])
776
951
 
777
952
  if !client.controller_encryption_enabled?(controller)
778
953
  puts "Encryption is not enabled on this controller".yellow
@@ -780,11 +955,17 @@ module IDRAC
780
955
  end
781
956
 
782
957
  puts "WARNING: Disabling encryption may prevent access to encrypted data!".red.bold
783
- print "Type 'DISABLE' to confirm: "
784
- confirmation = $stdin.gets.chomp
958
+
959
+ if options[:y]
960
+ puts "Automatically confirming operation (-y flag provided)".yellow
961
+ confirmation = 'DISABLE'
962
+ else
963
+ print "Type 'DISABLE' to confirm: "
964
+ confirmation = $stdin.gets.chomp
965
+ end
785
966
 
786
967
  if confirmation == 'DISABLE'
787
- client.disable_local_key_management(controller.odata_id)
968
+ client.disable_local_key_management(controller["@odata.id"])
788
969
  puts "Encryption disabled successfully".green
789
970
  else
790
971
  puts "Operation cancelled".yellow
@@ -905,6 +1086,7 @@ module IDRAC
905
1086
  puts " #{port['name']}:".bold
906
1087
  puts " Status: #{port['status'].send(status_color)}"
907
1088
  puts " MAC: #{port['mac']}".cyan
1089
+ puts " IP Address: #{port['ip_address'] || 'Not assigned'}".cyan
908
1090
  puts " Speed: #{port['speed_mbps']} Mbps".cyan
909
1091
  if port['pci']
910
1092
  puts " PCI Bus: #{port['pci']}".cyan
@@ -1085,6 +1267,7 @@ module IDRAC
1085
1267
 
1086
1268
  desc "boot_power_optimization", "Configure BIOS for OS-controlled power management"
1087
1269
  map "boot:power_optimization" => :boot_power_optimization
1270
+ map "boot:power:optimization" => :boot_power_optimization
1088
1271
  def boot_power_optimization
1089
1272
  with_idrac_client do |client|
1090
1273
  client.set_bios_os_power_control
@@ -1282,13 +1465,13 @@ module IDRAC
1282
1465
  # Storage tests
1283
1466
  puts "\n[TEST] Storage management".cyan.bold
1284
1467
  begin
1285
- controller = client.controller
1286
- puts "✓ Storage controller: #{controller.name} (#{controller.model})".green
1468
+ controller = client.find_controller
1469
+ puts "✓ Storage controller: #{controller["name"]} (#{controller["model"]})".green
1287
1470
 
1288
- drives = client.drives(controller)
1471
+ drives = client.drives(controller["@odata.id"])
1289
1472
  puts "✓ Found #{drives.size} physical drives".green
1290
1473
 
1291
- volumes = client.volumes(controller)
1474
+ volumes = client.volumes(controller["@odata.id"])
1292
1475
  puts "✓ Found #{volumes.size} virtual disks".green
1293
1476
  test_results["storage"] = { status: "PASS", message: "Retrieved storage information successfully" }
1294
1477
  rescue => e
@@ -1424,6 +1607,59 @@ module IDRAC
1424
1607
  end
1425
1608
  end
1426
1609
 
1610
+ desc "storage_raid_status", "Get storage RAID status"
1611
+ map "storage:raid_status" => :storage_raid_status
1612
+ def storage_raid_status
1613
+ with_idrac_client do |client|
1614
+ # Print system identification information
1615
+ print_system_identification(client, "storage", false)
1616
+
1617
+ controller = client.find_controller
1618
+
1619
+ if !controller
1620
+ puts "No storage controllers found".red
1621
+ return
1622
+ end
1623
+
1624
+ puts "Storage Controller: #{controller["Name"]} (#{controller["Model"]})".green
1625
+
1626
+ # Disks
1627
+ disks = client.drives(controller["@odata.id"])
1628
+
1629
+ puts "\nPhysical Disks (#{disks.size}):"
1630
+ if disks.empty?
1631
+ puts " No physical disks found".yellow
1632
+ else
1633
+ disks.each do |disk|
1634
+ status_color = disk["MediaType"] == "SSD" ? :green : :blue
1635
+ capacity = disk["CapacityBytes"] ? "#{disk["CapacityBytes"] / (1024**3)} GB" : "Unknown"
1636
+ puts " #{disk["Id"] || disk["name"] || "Unknown"}: #{disk["Name"] || disk["name"] || "Unknown"} (#{disk["MediaType"] || "Unknown"}, #{capacity})".send(status_color)
1637
+ end
1638
+ end
1639
+
1640
+ # Volumes
1641
+ volumes = client.volumes(controller["@odata.id"])
1642
+
1643
+ puts "\nVirtual Disks (#{volumes.size}):"
1644
+ if volumes.empty?
1645
+ puts " No virtual disks found".yellow
1646
+ else
1647
+ volumes.each do |vol|
1648
+ puts " #{vol["Id"]}: #{vol["Name"]} (#{vol["RAIDType"]}, #{vol["CapacityBytes"] / (1024**3)} GB)".green
1649
+ end
1650
+ end
1651
+ end
1652
+ end
1653
+
1654
+ desc "idrac_reset", "Reset the iDRAC controller (graceful restart)"
1655
+ map "idrac:reset" => :idrac_reset
1656
+ def idrac_reset
1657
+ with_idrac_client do |client|
1658
+ result = client.reset!
1659
+ puts result ? "iDRAC reset completed successfully" : "iDRAC reset failed"
1660
+ end
1661
+ end
1662
+
1427
1663
  private
1428
1664
 
1429
1665
  def with_idrac_client
@@ -1484,6 +1720,51 @@ module IDRAC
1484
1720
  retry_delay: options[:retry_delay]
1485
1721
  )
1486
1722
  end
1723
+
1724
+ # Get system identification information (service tag, model, iDRAC IP)
1725
+ # @param client [IDRAC::Client] The iDRAC client
1726
+ # @return [Hash] System identification information
1727
+ def get_system_identification(client)
1728
+ # Get system information
1729
+ system_info = client.get_basic_system_info rescue { model: "Unknown Model", sku: "Unknown" }
1730
+
1731
+ # Get iDRAC network info
1732
+ begin
1733
+ idrac_net = client.idrac_network
1734
+ idrac_ip = idrac_net["ipv4"]
1735
+ rescue
1736
+ idrac_ip = "Unknown IP"
1737
+ end
1738
+
1739
+ {
1740
+ service_tag: system_info[:sku],
1741
+ model: system_info[:model],
1742
+ idrac_ip: idrac_ip
1743
+ }
1744
+ end
1745
+
1746
+ # Print system identification information
1747
+ # @param client [IDRAC::Client] The iDRAC client
1748
+ # @param component_type [String] The type of operation (e.g., "storage", "encryption")
1749
+ # @param require_confirmation [Boolean] Whether to require confirmation
1750
+ # @return [Hash] System identification information
1751
+ def print_system_identification(client, component_type, require_confirmation = true)
1752
+ system_id = get_system_identification(client)
1753
+
1754
+ # Display system context
1755
+ puts "\nWARNING: About to modify #{component_type} on:".red.bold
1756
+ puts " Service Tag: #{system_id[:service_tag]}".yellow
1757
+ puts " Dell Model: #{system_id[:model]}".yellow
1758
+ puts " iDRAC IP: #{system_id[:idrac_ip]}".yellow
1759
+ puts
1760
+
1761
+ # Require confirmation if needed
1762
+ if require_confirmation && !options[:y]
1763
+ exit unless yes?("Continue? (y/n) ")
1764
+ end
1765
+
1766
+ system_id
1767
+ end
1487
1768
  end
1488
1769
  end
1489
1770