module IDRAC::Lifecycle
def clear_lifecycle!
def clear_lifecycle! path = '/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/Actions/DellLCService.SystemErase' payload = { "Component": ["LCData"] } response = authenticated_request( :post, path, body: payload.to_json, headers: { 'Content-Type' => 'application/json' } ) if response.status.between?(200, 299) debug "Lifecycle log cleared", 0, :green return true else debug "Failed to clear Lifecycle log", 0, :red error_message = "Failed to clear Lifecycle log. Status code: #{response.status}" begin error_data = JSON.parse(response.body) error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message'] rescue # Ignore JSON parsing errors end raise Error, error_message end end
def clear_system_event_logs!
def clear_system_event_logs! path = '/redfish/v1/Managers/iDRAC.Embedded.1/LogServices/Sel/Actions/LogService.ClearLog' response = authenticated_request(:post, path, body: {}.to_json, headers: { 'Content-Type' => 'application/json' }) if response.status.between?(200, 299) debug "System Event Logs cleared", 0, :green return true else debug "Failed to clear System Event Logs", 0, :red error_message = "Failed to clear System Event Logs. Status code: #{response.status}" begin error_data = JSON.parse(response.body) error_message += ", Message: #{error_data['error']['message']}" if error_data['error'] && error_data['error']['message'] rescue # Ignore JSON parsing errors end raise Error, error_message end end
def ensure_lifecycle_controller!
def ensure_lifecycle_controller! if !get_lifecycle_status debug "Lifecycle Controller is disabled, enabling...".yellow, 1 set_lifecycle_status(true) # Verify it was enabled if !get_lifecycle_status raise Error, "Failed to enable Lifecycle Controller" end debug "Lifecycle Controller successfully enabled".green, 1 else debug "Lifecycle Controller is already enabled".green, 1 end return true end
def get_lifecycle_status
They can do more than just the lifecycle, but that's what we need right now.
This follows from these Scripts "GetIdracLcSystemAttributesREDFISH.py" and "SetIdracLcSystemAttributesREDFISH.py"
def get_lifecycle_status # Check iDRAC version first to determine the right approach idrac_version = get_idrac_version rescue 0 debug "Detected iDRAC version: #{idrac_version}", 1 # Use version-specific methods if idrac_version > 9 debug "Using modern approach for iDRAC > 9", 1 return get_lifecycle_status_modern_firmware elsif idrac_version == 9 debug "Using registry approach for iDRAC 9", 1 return get_lifecycle_status_from_registry else debug "Using SCP approach for older iDRAC (v#{idrac_version})", 1 return get_lifecycle_status_from_scp end end
def get_lifecycle_status_from_registry
def get_lifecycle_status_from_registry # This big JSON explains all the attributes: path = "/redfish/v1/Registries/ManagerAttributeRegistry/ManagerAttributeRegistry.v1_0_0.json" response = authenticated_request(:get, path) if response.status != 200 debug "Failed to get any Lifecycle Controller Attributes".red, 1 return false end attributes = JSON.parse(response.body) # This is the attribute we want: target = attributes&.dig('RegistryEntries', 'Attributes')&.find {|q| q['AttributeName'] =~ /LCAttributes.1.LifecycleControllerState/ } # This is the FQDN of the attribute we want to get the value of: fqdn = target.dig('Id') # LifecycleController.Embedded.1#LCAttributes.1#LifecycleControllerState # This is the Current Value: response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/#{fqdn}") if response.status != 200 debug "Failed to get Lifecycle Controller Attributes".red, 1 return false end attributes = JSON.parse(response.body) # There is a ValueName and a Value Display Name (e.g. Enabled, Disabled, Recovery) display = attributes&.dig('Attributes','LCAttributes.1.LifecycleControllerState') value = target&.dig('Value')&.find { |v| v['ValueDisplayName'] == display }&.dig('ValueName')&.to_i value == 1 end
def get_lifecycle_status_from_scp
def get_lifecycle_status_from_scp debug "Exporting System Configuration Profile to check LifecycleController state...", 1 begin # Use the SCP export to get LifecycleController state scp = get_system_configuration_profile(target: "LifecycleController") # Check if we have data in the expected format if scp && scp["SystemConfiguration"] && scp["SystemConfiguration"]["Components"] # Find the LifecycleController component lc_component = scp["SystemConfiguration"]["Components"].find do |component| component["FQDD"] == "LifecycleController.Embedded.1" end if lc_component && lc_component["Attributes"] # Find the LifecycleControllerState attribute lc_state_attr = lc_component["Attributes"].find do |attr| attr["Name"] == "LCAttributes.1#LifecycleControllerState" end if lc_state_attr debug "Found LifecycleController state from SCP: #{lc_state_attr["Value"]}", 1 return lc_state_attr["Value"] == "Enabled" end end end debug "Could not find LifecycleController state in SCP export", 1, :yellow return false rescue => e debug "Error getting Lifecycle Controller status from SCP: #{e.message}", 1, :red debug e.backtrace.join("\n"), 3, :red return false end end
def get_lifecycle_status_modern_firmware
def get_lifecycle_status_modern_firmware # Try the standard Attributes endpoint first path = "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes" response = authenticated_request(:get, path) if response.status == 200 begin attributes_data = JSON.parse(response.body) if attributes_data["Attributes"] && attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"] lifecycle_state = attributes_data["Attributes"]["LCAttributes.1.LifecycleControllerState"] debug "Lifecycle Controller state (from Attributes): #{lifecycle_state}".light_cyan, 1 return lifecycle_state == "Enabled" end rescue JSON::ParserError debug "Failed to parse Attributes response".yellow, 1 # Fall through to registry method if parsing fails or attribute not found end else debug "Failed to get Attributes endpoint (Status: #{response.status}), trying registry method...".yellow, 1 end # Try getting the DellAttributes for LifecycleController directly # The key insight is that we need to use just the base path without the fragment attributes_path = "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/LifecycleController.Embedded.1" attributes_response = authenticated_request(:get, attributes_path) if attributes_response.status == 200 begin dell_attr_data = JSON.parse(attributes_response.body) if dell_attr_data["Attributes"] && dell_attr_data["Attributes"]["LCAttributes.1.LifecycleControllerState"] lifecycle_state = dell_attr_data["Attributes"]["LCAttributes.1.LifecycleControllerState"] debug "Lifecycle Controller state (from Dell Attributes): #{lifecycle_state}".light_cyan, 1 return lifecycle_state == "Enabled" end rescue JSON::ParserError debug "Failed to parse Dell Attributes response".yellow, 1 # Fall through to registry method if parsing fails or attribute not found end else debug "Failed to get Dell Attributes (Status: #{attributes_response.status}), trying registry method...".yellow, 1 end # Fallback to the registry method if both Attributes endpoints fail registry_response = authenticated_request( :get, "/redfish/v1/Registries/ManagerAttributeRegistry/ManagerAttributeRegistry.v1_0_0.json" ) if registry_response.status != 200 debug "Failed to get Lifecycle Controller Attributes Registry", 0, :red return false end begin registry_data = JSON.parse(registry_response.body) # This is the attribute we want: target = registry_data['RegistryEntries']['Attributes'].find {|q| q['AttributeName'] =~ /LCAttributes.1.LifecycleControllerState/ } if !target debug "Could not find LCAttributes.1.LifecycleControllerState in registry", 0, :red return false end debug "Found attribute in registry but couldn't access it via other endpoints".yellow, 1 return false rescue JSON::ParserError, NoMethodError, StandardError => e debug "Error during registry access: #{e.message}", 0, :red return false end end
def get_system_event_logs
def get_system_event_logs path = '/redfish/v1/Managers/iDRAC.Embedded.1/Logs/Sel?$expand=*($levels=1)' response = authenticated_request(:get, path) if response.status == 200 begin data = JSON.parse(response.body)['Members'].map do |entry| { id: entry['Id'], created: entry['Created'], message: entry['Message'], severity: entry['Severity'] } end return data # RecursiveOpenStruct.new(data, recurse_over_arrays: true) rescue JSON::ParserError raise Error, "Failed to parse system event logs response: #{response.body}" end else raise Error, "Failed to get system event logs. Status code: #{response.status}" end end
def set_lifecycle_status(status)
def set_lifecycle_status(status) payload = { "Attributes": { "LCAttributes.1.LifecycleControllerState": status ? 'Enabled' : 'Disabled' } } response = authenticated_request( :patch, "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellAttributes/LifecycleController.Embedded.1", body: payload.to_json, headers: { 'Content-Type': 'application/json' } ) code = response.status case code when 200..299 debug "Lifecycle Controller is now #{status ? 'Enabled' : 'Disabled'}".green, 1 when 400..499 debug "[#{code}] This iDRAC does not support Lifecycle Controller", 0, :red when 500..599 debug "[#{code}] iDRAC does not support Lifecycle Controller", 0, :red else end end
def update_status_message(status)
def update_status_message(status) debug "Lifecycle Controller is now #{status ? 'Enabled' : 'Disabled'}".green, 1 end