module IDRAC::Boot
def bios_error_prompt_disabled?
def bios_error_prompt_disabled? response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios") if response.status == 200 begin data = JSON.parse(response.body) if data["Attributes"] && data["Attributes"].has_key?("ErrPrompt") return data["Attributes"]["ErrPrompt"] == "Disabled" else debug "ErrPrompt attribute not found in BIOS settings", 1, :yellow return false end rescue JSON::ParserError debug "Failed to parse BIOS response", 0, :red return false end else debug "Failed to get BIOS information. Status code: #{response.status}", 0, :red return false end end
def bios_hdd_placeholder_enabled?
def bios_hdd_placeholder_enabled? case self.license_version.to_i when 8 # scp = usable_scp(get_system_configuration_profile(target: "BIOS")) # scp["BIOS.Setup.1-1"]["HddPlaceholder"] == "Enabled" true else response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios") json = JSON.parse(response.body) raise "Error reading HddPlaceholder setup" if json&.dig('SystemConfiguration').blank? json["Attributes"]["HddPlaceholder"] == "Enabled" end end
def bios_os_power_control_enabled?
def bios_os_power_control_enabled? case self.license_version.to_i when 8 scp = usable_scp(get_system_configuration_profile(target: "BIOS")) scp["BIOS.Setup.1-1"]["ProcCStates"] == "Enabled" && scp["BIOS.Setup.1-1"]["SysProfile"] == "PerfPerWattOptimizedOs" && scp["BIOS.Setup.1-1"]["ProcPwrPerf"] == "OsDbpm" else response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios") json = JSON.parse(response.body) raise "Error reading PowerControl setup" if json&.dig('SystemConfiguration').blank? json["Attributes"]["ProcCStates"] == "Enabled" && json["Attributes"]["SysProfile"] == "PerfPerWattOptimizedOs" && json["Attributes"]["ProcPwrPerf"] == "OsDbpm" end end
def configure_bios_settings(settings)
def configure_bios_settings(settings) response = authenticated_request( :patch, "/redfish/v1/Systems/System.Embedded.1/Bios/Settings", body: { "Attributes": settings }.to_json, headers: { 'Content-Type': 'application/json' } ) if response.status.between?(200, 299) puts "BIOS settings configured. A system reboot is required for changes to take effect.".green # Check if we need to wait for a job if response.headers["Location"] job_id = response.headers["Location"].split("/").last wait_for_job(job_id) end return true else error_message = "Failed to configure BIOS settings. Status code: #{response.status}" begin error_data = JSON.parse(response.body) if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] error_info = error_data["error"]["@Message.ExtendedInfo"].first error_message += ", Message: #{error_info['Message']}" end rescue # Ignore JSON parsing errors end raise Error, error_message end end
def create_scp_for_bios(settings)
def create_scp_for_bios(settings) attributes = [] settings.each do |key, value| attributes << { "Name": key.to_s, "Value": value, "Set On Import": "True" } end scp = { "SystemConfiguration": { "Components": [ { "FQDD": "BIOS.Setup.1-1", "Attributes": attributes } ] } } return scp end
def ensure_uefi_boot
def ensure_uefi_boot response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios") if response.status == 200 begin data = JSON.parse(response.body) if data["Attributes"]["BootMode"] == "Uefi" puts "System is already in UEFI boot mode".green return true else puts "System is not in UEFI boot mode. Setting to UEFI...".yellow # Create payload for UEFI boot mode payload = { "Attributes": { "BootMode": "Uefi" } } # If iDRAC 9, we need to enable HddPlaceholder if get_idrac_version == 9 payload[:Attributes][:HddPlaceholder] = "Enabled" end response = authenticated_request( :patch, "/redfish/v1/Systems/System.Embedded.1/Bios/Settings", body: payload.to_json, headers: { 'Content-Type': 'application/json' } ) wait_for_job(response.headers["location"]) end rescue JSON::ParserError raise Error, "Failed to parse BIOS response: #{response.body}" end else raise Error, "Failed to get BIOS information. Status code: #{response.status}" end end
def get_bios_boot_options
def get_bios_boot_options response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/BootSources") if response.status == 200 begin data = JSON.parse(response.body) if data["Attributes"]["UefiBootSeq"].blank? puts "Not in UEFI mode".red return false end boot_order = [] boot_options = [] data["Attributes"]["UefiBootSeq"].each do |seq| puts "#{seq["Name"]} > #{seq["Enabled"]}".yellow boot_options << seq["Name"] boot_order << seq["Name"] if seq["Enabled"] end return { boot_options: boot_options, boot_order: boot_order } rescue JSON::ParserError raise Error, "Failed to parse BIOS boot options response: #{response.body}" end else raise Error, "Failed to get BIOS boot options. Status code: #{response.status}" end end
def get_idrac_version
def get_idrac_version response = authenticated_request(:get, "/redfish/v1") if response.status == 200 begin data = JSON.parse(response.body) redfish = data["RedfishVersion"] server = response.headers["server"] case server.to_s.downcase when /appweb\/4.5.4/, /idrac\/8/ return 8 when /apache/, /idrac\/9/ return 9 else # Try to determine by RedfishVersion as fallback if redfish == "1.4.0" return 8 elsif redfish == "1.18.0" return 9 else raise Error, "Unknown iDRAC version: #{server} / #{redfish}" end end rescue JSON::ParserError raise Error, "Failed to parse iDRAC response: #{response.body}" end else raise Error, "Failed to get iDRAC information. Status code: #{response.status}" end end
def import_system_configuration(scp, target: "ALL", reboot: false)
def import_system_configuration(scp, target: "ALL", reboot: false) params = { "ImportBuffer": JSON.pretty_generate(scp), "ShareParameters": { "Target": target } } # Configure shutdown behavior params["ShutdownType"] = "Forced" params["HostPowerState"] = reboot ? "On" : "Off" response = authenticated_request( :post, "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager.ImportSystemConfiguration", body: params.to_json, headers: { 'Content-Type': 'application/json' } ) task = wait_for_task(response.headers["location"]) debugger return task end
def override_boot_source
This sets boot to HD but before that it sets the one-time boot to CD
def override_boot_source # For now try with all iDRAC versions if self.license_version.to_i == 9 set_boot_order_hd_first() set_one_time_virtual_media_boot() else scp = {"FQDD"=>"iDRAC.Embedded.1", "Attributes"=> [{"Name"=>"ServerBoot.1#BootOnce", "Value"=>"Enabled", "Set On Import"=>"True"}, {"Name"=>"ServerBoot.1#FirstBootDevice", "Value"=>"VCD-DVD", "Set On Import"=>"True"}]} # set_uefi_boot_cd_once_then_hd # scp = self.set_bios_boot_cd_first # get_bios_boot_options # Make sure we know if the OS is calling it Unknown or RAID # {"FQDD"=>"BIOS.Setup.1-1", "Attributes"=> # [{"Name"=>"ServerBoot.1#BootOnce", "Value"=>"Enabled", "Set On Import"=>"True"}, # {"Name"=>"ServerBoot.1#FirstBootDevice", "Value"=>"VCD-DVD", "Set On Import"=>"True"}, # {"Name"=>"BootSeqRetry", "Value"=>"Disabled", "Set On Import"=>"True"}, # {"Name"=>"UefiBootSeq", "Value"=>"Unknown.Unknown.1-1,NIC.PxeDevice.1-1,Floppy.iDRACVirtual.1-1,Optical.iDRACVirtual.1-1", # "Set On Import"=>"True"}]} # 3.3.0 :018 > scp1 = {"FQDD"=>"BIOS.Setup.1-1", "Attributes"=> [{"Name"=>"OneTimeUefiBootSeq", "Value"=>"VCD-DVD", "Set On Import"=>"True"}, {"Name"=>"BootSeqRetry", "Value"=>"Disabled", "Set On Import"=>"True"}, {"Name"=>"UefiBootSeq", "Value"=>"Unknown.Unknown.1-1,NIC.PxeDevice.1-1", "Set On Import"=>"True"}]} set_system_configuration_profile(scp) # This will cycle power and leave the device off. end end
def scp_boot_mode_uefi(idrac_license_version: 9)
def scp_boot_mode_uefi(idrac_license_version: 9) opts = { "BootMode" => 'Uefi' } # If we're iDRAC 9, we need enable a placeholder, otherwise we can't order the # boot order until we've switched to UEFI mode. # Read [about it](https://dl.dell.com/manuals/all-products/esuprt_software/esuprt_it_ops_datcentr_mgmt/dell-management-solution-resources_white-papers12_en-us.pdf). # ...administrators may wish to reserve a boot entry for a fixed disk in the UEFI Boot Sequence before an OS is installed or before a physical or # virtual drive has been formatted. When a HardDisk Drive Placeholder is set to Enabled, the BIOS will create a boot option for the PERC RAID # (Integrated or in a PCIe slot) disk if a partition is found, even if there is no FAT filesystem present... this allows the Integrated RAID controller # to be moved in the UEFI Boot Sequence prior to the OS installation opts["HddPlaceholder"] = "Enabled" if idrac_license_version.to_i == 9 self.make_scp(fqdd: "BIOS.Setup.1-1", attributes: opts) end
def set_bios(hash)
What triggers a reboot?
def set_bios(hash) scp = self.make_scp(fqdd: "BIOS.Setup.1-1", attributes: hash) res = self.set_system_configuration_profile(scp) if res[:status] == :success self.get_bios_boot_options end res end
def set_bios_ignore_errors(value = true)
def set_bios_ignore_errors(value = true) configure_bios_settings({ "ErrPrompt": value ? "Disabled" : "Enabled" }) end
def set_bios_os_power_control
def set_bios_os_power_control settings = { "ProcCStates": "Enabled", # Processor C-States "SysProfile": "PerfPerWattOptimizedOs", "ProcPwrPerf": "OsDbpm", # OS Power Management "PcieAspmL1": "Enabled" # PCIe Active State Power Management } configure_bios_settings(settings) end
def set_boot_order_hd_first
def set_boot_order_hd_first # First ensure we're in UEFI mode ensure_uefi_boot # Get available boot options boot_options_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/BootOptions?$expand=*($levels=1)") if boot_options_response.status == 200 begin data = JSON.parse(boot_options_response.body) puts "Available boot options:" data["Members"].each { |m| puts "\t#{m['DisplayName']} -> #{m['Id']}" } # Find RAID controller or HD device = data["Members"].find { |m| m["DisplayName"] =~ /RAID Controller/ } # Sometimes it's named differently device ||= data["Members"].find { |m| m["DisplayName"] =~ /ubuntu/i } device ||= data["Members"].find { |m| m["DisplayName"] =~ /UEFI Hard Drive/i } device ||= data["Members"].find { |m| m["DisplayName"] =~ /Hard Drive/i } if device.nil? raise Error, "No bootable hard drive or RAID controller found in boot options" end boot_id = device["Id"] # Set boot order response = authenticated_request( :patch, "/redfish/v1/Systems/System.Embedded.1", body: { "Boot": { "BootOrder": [boot_id] } }.to_json, headers: { 'Content-Type': 'application/json' } ) if response.status.between?(200, 299) puts "Boot order set to HD first".green return true else error_message = "Failed to set boot order. Status code: #{response.status}" begin error_data = JSON.parse(response.body) if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] error_info = error_data["error"]["@Message.ExtendedInfo"].first error_message += ", Message: #{error_info['Message']}" end rescue # Ignore JSON parsing errors end raise Error, error_message end rescue JSON::ParserError raise Error, "Failed to parse boot options response: #{response.body}" end else raise Error, "Failed to get boot options. Status code: #{boot_options_response.status}" end end
def set_uefi_boot_cd_once_then_hd
def set_uefi_boot_cd_once_then_hd boot_options = get_bios_boot_options[:boot_options] # Note may have to put device into # self.set_bios( { "BootMode" => 'Uefi' } ) # self.reboot! # And then reboot before you can make the following call: raid_name = boot_options.include?("RAID.Integrated.1-1") ? "RAID.Integrated.1-1" : "Unknown.Unknown.1-1" raise "No RAID HD in boot options" unless boot_options.include?(raid_name) bios = { "BootMode" => 'Uefi', "BootSeqRetry" => "Disabled", # "UefiTargetBootSourceOverride" => 'Cd', # "BootSourceOverrideTarget" => 'UefiTarget', # "OneTimeBootMode" => "OneTimeUefiBootSeq", # One time boot order # "OneTimeHddSeqDev" => "Optical.iDRACVirtual.1-1", # "OneTimeBiosBootSeqDev" => "Optical.iDRACVirtual.1-1", # "OneTimeUefiBootSeqDev" => "Optical.iDRACVirtual.1-1", # Enabled/Disabled Options # "SetBootOrderDis" => "Disk.USBBack.1-1", # Don't boot to USB if it is plugged in "SetBootOrderEn" => raid_name, # "SetBootOrderFqdd1" => raid_name, # "SetLegacyHddOrderFqdd1" => raid_name, # "SetBootOrderFqdd2" => "Optical.iDRACVirtual.1-1", # Permanent Boot Order "HddSeq" => raid_name, "BiosBootSeq" => raid_name, "UefiBootSeq" => raid_name # This is likely redundant... } # The usb device will have 'usb' in it: usb_name = boot_options.select { |b| b =~ /usb/i } bios["SetBootOrderDis"] = usb_name if usb_name.present? set_bios(bios) end