module IDRAC::Utility
def accept_supportassist_eula
-
(Boolean)
- true if successful
def accept_supportassist_eula debug "Accepting SupportAssist EULA...", 1 response = authenticated_request( :post, "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistAcceptEULA", body: {}.to_json, headers: { 'Content-Type' => 'application/json' } ) if response.status.between?(200, 299) debug "SupportAssist EULA accepted successfully", 1, :green true else error_msg = parse_error_response(response) debug "Failed to accept EULA: #{error_msg}", 1, :red false end rescue => e debug "Error accepting EULA: #{e.message}", 1, :red false end
def download_tsr_from_location(location, output_file: nil)
-
(String, nil)
- Path to downloaded file or nil if failed
Parameters:
-
output_file
(String
) -- Path to save the TSR file (optional) -
location
(String
) -- URL location of the TSR file
def download_tsr_from_location(location, output_file: nil) debug "Downloading TSR from location: #{location}", 1 # Default output filename with timestamp output_file ||= "supportassist_#{@host}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip" # Download the file from the location file_response = authenticated_request(:get, location) if file_response.status == 200 && file_response.body File.open(output_file, 'wb') do |f| f.write(file_response.body) end debug "TSR saved to: #{output_file} (#{File.size(output_file)} bytes)", 1, :green return output_file else debug "Failed to download file from location. Status: #{file_response.status}", 1, :red nil end rescue => e debug "Error downloading TSR: #{e.message}", 1, :red nil end
def generate_and_download_tsr(output_file: nil, data_selector_values: nil, wait_timeout: 600)
-
(String, nil)
- Path to downloaded file or nil if failed
Parameters:
-
wait_timeout
(Integer
) -- Maximum time to wait for generation in seconds (default: 600) -
data_selector_values
(Array
) -- Array of log types to include (optional) -
output_file
(String
) -- Path to save the TSR file (optional)
def generate_and_download_tsr(output_file: nil, data_selector_values: nil, wait_timeout: 600) debug "Starting TSR generation and download process...", 1 output_file ||= "supportassist_#{@host}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.zip" # First, generate the TSR result = generate_tsr_logs(data_selector_values: data_selector_values) if result[:status] == :success && result[:job] debug "TSR generation completed successfully", 1, :green # Check if the job response has a location for the file if result[:location] return download_tsr_from_location(result[:location], output_file: output_file) else # Try alternative download methods based on Dell's Python script approach debug "Attempting to locate generated TSR file...", 1, :yellow # Wait a moment for the file to be available sleep 2 # Try known endpoints where the file might be available possible_locations = [ "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/ExportedFiles/SupportAssist", "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/ExportedFiles", "/downloads/supportassist_collection.zip", "/sysmgmt/2016/server/support_assist_collection" ] possible_locations.each do |location| debug "Trying location: #{location}", 2 file_response = authenticated_request(:get, location) if file_response.status == 200 && file_response.body && file_response.body.size > 1024 File.open(output_file, 'wb') do |f| f.write(file_response.body) end debug "TSR saved to: #{output_file} (#{File.size(output_file)} bytes)", 1, :green return output_file end end debug "Could not locate TSR file for direct download", 1, :yellow debug "The collection was generated but may require network share export", 1, :yellow end elsif result[:status] == :accepted debug "TSR generation was accepted but status unknown", 1, :yellow else debug "Failed to initiate TSR generation: #{result[:error]}", 1, :red end nil end
def generate_tsr_logs(data_selector_values: nil, share_type: nil, share_parameters: nil)
-
(Hash)
- Result hash with status and job/task information
Parameters:
-
data_selector_values
(Array
) -- Array of log types to include (optional)
def generate_tsr_logs(data_selector_values: nil, share_type: nil, share_parameters: nil) debug "Generating TSR/SupportAssist logs...", 1 # Check EULA status first eula_status = supportassist_eula_status if eula_status["EULAAccepted"] == false || eula_status["EULAAccepted"] == "false" puts "\n" + "="*80 puts "ERROR: SupportAssist EULA Not Accepted".red.bold puts "="*80 puts "" puts "The SupportAssist End User License Agreement (EULA) must be accepted".yellow puts "before you can generate TSR/SupportAssist collections.".yellow puts "" puts "To accept the EULA, run:".cyan puts " idrac tsr_accept_eula --host #{@host} --port #{@port}".green.bold puts "" puts "="*80 return { status: :failed, error: "SupportAssist EULA not accepted" } end # Default data selector values for comprehensive TSR # Valid values for SupportAssistCollection: "DebugLogs", "GPULogs", "HWData", "OSAppData", "TTYLogs", "TelemetryReports" data_selector_values ||= ["HWData", "OSAppData"] # Map numeric values to iDRAC expected strings if needed if data_selector_values.is_a?(Array) && data_selector_values.first.to_s =~ /^\d+$/ data_selector_values = data_selector_values.map do |val| case val.to_s when "0" then "HWData" when "1" then "OSAppData" when "2" then "TTYLogs" when "3" then "DebugLogs" else "HWData" # Default to HWData end end elsif data_selector_values.is_a?(String) data_selector_values = data_selector_values.split(',') end debug "Data selector values: #{data_selector_values.inspect}", 1 # Use SupportAssistCollection for local generation as it supports "Local" ShareType payload = { "ShareType" => "Local", "DataSelectorArrayIn" => data_selector_values, "Filter" => "No", # Don't filter PII "Transmit" => "No" # Don't transmit to Dell } debug "SupportAssist collection payload: #{payload.to_json}", 1 response = authenticated_request( :post, "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistCollection", body: payload.to_json, headers: { 'Content-Type' => 'application/json' } ) case response.status when 202 # Accepted - job created location = response.headers["location"] if location debug "TSR generation job created: #{location}", 1, :green job_id = location.split("/").last # Wait for job to complete and capture the file location job_result = wait_for_job_with_location(job_id) if job_result && (job_result["JobState"] == "Completed" || job_result["JobState"] == "CompletedWithErrors") result = { status: :success, job: job_result } # Check if we got a file location from the job completion result[:location] = job_result["FileLocation"] if job_result["FileLocation"] result else { status: :failed, error: "Job did not complete successfully" } end else { status: :accepted, message: "TSR generation initiated" } end when 200..299 debug "TSR generation completed immediately", 1, :green { status: :success } else error_msg = parse_error_response(response) debug "Failed to generate TSR: #{error_msg}", 1, :red { status: :failed, error: error_msg } end rescue => e debug "Error generating TSR: #{e.message}", 1, :red { status: :error, error: e.message } end
def parse_error_response(response)
def parse_error_response(response) begin data = JSON.parse(response.body) if data["error"] && data["error"]["@Message.ExtendedInfo"] data["error"]["@Message.ExtendedInfo"].first["Message"] elsif data["error"] && data["error"]["message"] data["error"]["message"] else "Status: #{response.status} - #{response.body}" end rescue "Status: #{response.status} - #{response.body}" end end
def reset!
def reset! debug "Resetting iDRAC controller...", 1 response = authenticated_request( :post, "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Manager.Reset", body: { "ResetType" => "GracefulRestart" }.to_json, headers: { 'Content-Type' => 'application/json' } ) if response.status.between?(200, 299) debug "Reset command accepted, waiting for iDRAC to restart...", 1, :green tries = 0 while true begin debug "Checking if iDRAC is back online...", 1 response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1") if response.status.between?(200, 299) debug "iDRAC is back online!", 1, :green break end sleep 30 rescue => e tries += 1 if tries > 5 debug "Failed to reconnect to iDRAC after 5 attempts", 1, :red return false end debug "No response from server... retry #{tries}/5", 1, :red sleep 2 ** tries end end else begin error_data = JSON.parse(response.body) if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"] message = error_data["error"]["@Message.ExtendedInfo"].first["Message"] debug "*" * 80, 1, :red debug message, 1, :red debug "*" * 80, 1, :red else debug "Failed to reset iDRAC. Status code: #{response.status}", 1, :red end rescue => e debug "Failed to reset iDRAC. Status code: #{response.status}", 1, :red debug "Error response: #{response.body}", 2, :red end return false end true end
def supportassist_eula_status
-
(Hash)
- EULA status information
def supportassist_eula_status debug "Checking SupportAssist EULA status...", 1 response = authenticated_request( :post, "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.SupportAssistGetEULAStatus", body: {}.to_json, headers: { 'Content-Type' => 'application/json' } ) if response.status.between?(200, 299) begin data = JSON.parse(response.body) debug "EULA status: #{data.to_json}", 2 return data rescue JSON::ParserError return { "EULAAccepted" => "Unknown" } end else error_msg = parse_error_response(response) debug "Failed to get EULA status: #{error_msg}", 1, :red return { "EULAAccepted" => "Error", "error" => error_msg } end rescue => e debug "Error checking EULA status: #{e.message}", 1, :red { "EULAAccepted" => "Error", "error" => e.message } end
def tsr_status
-
(Hash)
- Status information
def tsr_status debug "Checking SupportAssist collection status...", 1 response = authenticated_request( :get, "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService" ) if response.status == 200 data = JSON.parse(response.body) status = { available: data["Actions"]&.key?("#DellLCService.SupportAssistCollection"), export_available: data["Actions"]&.key?("#DellLCService.SupportAssistExportLastCollection"), collection_in_progress: false } # Check if there's an active collection job jobs_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs") if jobs_response.status == 200 jobs_data = JSON.parse(jobs_response.body) if jobs_data["Members"] jobs_data["Members"].each do |job| if job["Name"]&.include?("SupportAssist") || job["Name"]&.include?("TSR") status[:collection_in_progress] = true status[:job_id] = job["Id"] status[:job_state] = job["JobState"] break end end end end debug "SupportAssist status: #{status.to_json}", 2 status else debug "Failed to get SupportAssist status: #{response.status}", 1, :red { available: false, error: "Unable to determine status" } end rescue => e debug "Error checking SupportAssist status: #{e.message}", 1, :red { available: false, error: e.message } end
def wait_for_job_with_location(job_id, max_wait: 600)
-
(Hash, nil)
- Job data with FileLocation if available
Parameters:
-
max_wait
(Integer
) -- Maximum time to wait in seconds -
job_id
(String
) -- The job ID to wait for
def wait_for_job_with_location(job_id, max_wait: 600) debug "Waiting for job #{job_id} to complete...", 1 start_time = Time.now while (Time.now - start_time) < max_wait job_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/#{job_id}") if job_response.status == 200 job_data = JSON.parse(job_response.body) case job_data["JobState"] when "Completed", "CompletedWithErrors" debug "Job #{job_id} completed: #{job_data["JobState"]}", 1, :green # Check response headers for file location if job_response.headers["location"] job_data["FileLocation"] = job_response.headers["location"] debug "Found file location in headers: #{job_data["FileLocation"]}", 1, :green end # Also check the job data itself for output location if job_data["Oem"] && job_data["Oem"]["Dell"] && job_data["Oem"]["Dell"]["OutputLocation"] job_data["FileLocation"] = job_data["Oem"]["Dell"]["OutputLocation"] debug "Found file location in job data: #{job_data["FileLocation"]}", 1, :green end return job_data when "Failed", "Exception" debug "Job #{job_id} failed: #{job_data["Message"]}", 1, :red return job_data else debug "Job #{job_id} state: #{job_data["JobState"]} - #{job_data["PercentComplete"]}%", 2 sleep 5 end else debug "Failed to get job status: #{job_response.status}", 2 sleep 5 end end debug "Timeout waiting for job #{job_id}", 1, :red nil end