diff --git a/config/meson.build b/config/meson.build index 4e464b6c5..b3d579aff 100644 --- a/config/meson.build +++ b/config/meson.build @@ -13,6 +13,7 @@ feature_options = [ 'experimental-redfish-multi-computer-system', 'google-api', 'host-serial-socket', + 'hw-isolation', 'hypervisor-computer-system', 'hypervisor-serial-socket', 'ibm-led-extensions', diff --git a/meson.options b/meson.options index 1b422b962..85971a6fb 100644 --- a/meson.options +++ b/meson.options @@ -578,3 +578,11 @@ option( description: '''Enable hypervisor serial console WebSocket. Path is \'/console1\'.''', ) + +# BMCWEB_HW_ISOLATION +option( + 'hw-isolation', + type: 'feature', + value: 'enabled', + description: 'Enable the Hardware Isolation feature', +) diff --git a/redfish-core/include/registries/openbmc.json b/redfish-core/include/registries/openbmc.json index 430454471..4718aea9f 100644 --- a/redfish-core/include/registries/openbmc.json +++ b/redfish-core/include/registries/openbmc.json @@ -459,6 +459,24 @@ "Resolution": "None.", "Severity": "Critical" }, + "GuardRecord": { + "Description": "Indicates this resource is isolated.", + "Message": "%1 Guard Record %2", + "MessageSeverity": "OK", + "NumberOfArgs": 2, + "ParamTypes": ["string", "string"], + "Resolution": "None.", + "Severity": "OK" + }, + "HardwareIsolationReason": { + "Description": "Indicates the condition that affects the health of this resource.", + "Message": "The reason for the resource isolation: %1", + "MessageSeverity": "OK", + "NumberOfArgs": 1, + "ParamTypes": ["string"], + "Resolution": "None.", + "Severity": "Critical" + }, "IPMIWatchdog": { "Description": "Indicates that there is a host watchdog event.", "Message": "Host Watchdog Event: %1", diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp index 05a7bd807..3c88ac2af 100644 --- a/redfish-core/include/registries/openbmc_message_registry.hpp +++ b/redfish-core/include/registries/openbmc_message_registry.hpp @@ -637,6 +637,31 @@ constexpr std::array registry = }, "None.", }}, + MessageEntry{ + "GuardRecord", + { + "Indicates this resource is isolated.", + "%1 Guard Record %2", + "OK", + 2, + { + "string", + "string", + }, + "None.", + }}, + MessageEntry{ + "HardwareIsolationReason", + { + "Indicates the condition that affects the health of this resource.", + "The reason for the resource isolation: %1", + "OK", + 1, + { + "string", + }, + "None.", + }}, MessageEntry{ "IPMIWatchdog", { @@ -2439,147 +2464,149 @@ enum class Index firmwareUpdateStaged = 49, firmwareUpdateStarted = 50, generalFirmwareSecurityViolation = 51, - iPMIWatchdog = 52, - intelUPILinkWidthReducedToHalf = 53, - intelUPILinkWidthReducedToQuarter = 54, - invalidLoginAttempted = 55, - invalidUpload = 56, - inventoryAdded = 57, - inventoryRemoved = 58, - lanLost = 59, - lanRegained = 60, - legacyPCIPERR = 61, - legacyPCISERR = 62, - mEAutoConfigFailed = 63, - mEAutoConfigSuccess = 64, - mEBootGuardHealthEvent = 65, - mECpuDebugCapabilityDisabled = 66, - mEDirectFlashUpdateRequested = 67, - mEExceptionDuringShutdown = 68, - mEFactoryResetError = 69, - mEFactoryRestore = 70, - mEFirmwareException = 71, - mEFirmwarePanicReason = 72, - mEFirmwareRecoveryReason = 73, - mEFirmwareResiliencyError = 74, - mEFlashEraseError = 75, - mEFlashStateInformation = 76, - mEFlashStateInformationWritingEnabled = 77, - mEFlashVerificationError = 78, - mEFlashWearOutWarning = 79, - mEImageExecutionFailed = 80, - mEInternalError = 81, - mEManufacturingError = 82, - mEMctpInterfaceError = 83, - mEMultiPchModeMisconfig = 84, - mEPeciOverDmiError = 85, - mEPttHealthEvent = 86, - mERecoveryGpioForced = 87, - mERestrictedMode = 88, - mESmbusLinkFailure = 89, - mEUmaError = 90, - mEUnsupportedFeature = 91, - manufacturingModeEntered = 92, - manufacturingModeExited = 93, - memoryECCCorrectable = 94, - memoryECCUncorrectable = 95, - memoryParityCommandAndAddress = 96, - memoryParityNotKnown = 97, - memoryRASConfigurationDisabled = 98, - memoryRASConfigurationEnabled = 99, - memoryRASModeDisabled = 100, - memoryRASModeEnabled = 101, - memoryThermTrip = 102, - mirroringRedundancyDegraded = 103, - mirroringRedundancyFull = 104, - nMIButtonPressed = 105, - nMIDiagnosticInterrupt = 106, - pCIeCorrectableAdvisoryNonFatal = 107, - pCIeCorrectableBadDLLP = 108, - pCIeCorrectableBadTLP = 109, - pCIeCorrectableHeaderLogOverflow = 110, - pCIeCorrectableInternal = 111, - pCIeCorrectableLinkBWChanged = 112, - pCIeCorrectableReceiverError = 113, - pCIeCorrectableReplayNumRollover = 114, - pCIeCorrectableReplayTimerTimeout = 115, - pCIeCorrectableUnspecifiedAERError = 116, - pCIeFatalACSViolation = 117, - pCIeFatalAtomicEgressBlocked = 118, - pCIeFatalCompleterAbort = 119, - pCIeFatalCompletionTimeout = 120, - pCIeFatalDataLinkLayerProtocol = 121, - pCIeFatalECRCError = 122, - pCIeFatalFlowControlProtocol = 123, - pCIeFatalMCBlockedTLP = 124, - pCIeFatalMalformedTLP = 125, - pCIeFatalPoisonedTLP = 126, - pCIeFatalReceivedErrNonFatalMessage = 127, - pCIeFatalReceivedFatalMessageFromDownstream = 128, - pCIeFatalReceiverBufferOverflow = 129, - pCIeFatalSurpriseLinkDown = 130, - pCIeFatalTLPPrefixBlocked = 131, - pCIeFatalUncorrectableInternal = 132, - pCIeFatalUnexpectedCompletion = 133, - pCIeFatalUnspecifiedNonAERFatalError = 134, - pCIeFatalUnsupportedRequest = 135, - powerButtonLongPressed = 136, - powerButtonPressed = 137, - powerRestorePolicyApplied = 138, - powerSupplyConfigurationError = 139, - powerSupplyConfigurationErrorRecovered = 140, - powerSupplyFailed = 141, - powerSupplyFailurePredicted = 142, - powerSupplyFanFailed = 143, - powerSupplyFanRecovered = 144, - powerSupplyInserted = 145, - powerSupplyPowerGoodFailed = 146, - powerSupplyPowerLost = 147, - powerSupplyPowerRestored = 148, - powerSupplyPredictedFailureRecovered = 149, - powerSupplyRecovered = 150, - powerSupplyRemoved = 151, - powerUnitDegradedFromNonRedundant = 152, - powerUnitDegradedFromRedundant = 153, - powerUnitNonRedundantFromInsufficient = 154, - powerUnitNonRedundantInsufficient = 155, - powerUnitNonRedundantSufficient = 156, - powerUnitRedundancyDegraded = 157, - powerUnitRedundancyLost = 158, - powerUnitRedundancyRegained = 159, - resetButtonPressed = 160, - sELEntryAdded = 161, - securityBoot2ndFlashEnabled = 162, - securityP2aBridgeEnabled = 163, - securityUartPortDebugEnabled = 164, - securityUserNonRootUidZeroAssigned = 165, - securityUserNonRootUidZeroRemoved = 166, - securityUserRootDisabled = 167, - securityUserRootEnabled = 168, - securityUserStrongHashAlgoRestored = 169, - securityUserUnsupportedShellEnabled = 170, - securityUserUnsupportedShellRemoved = 171, - securityUserWeakHashAlgoEnabled = 172, - sensorThresholdCriticalHighGoingHigh = 173, - sensorThresholdCriticalHighGoingLow = 174, - sensorThresholdCriticalLowGoingHigh = 175, - sensorThresholdCriticalLowGoingLow = 176, - sensorThresholdWarningHighGoingHigh = 177, - sensorThresholdWarningHighGoingLow = 178, - sensorThresholdWarningLowGoingHigh = 179, - sensorThresholdWarningLowGoingLow = 180, - serviceFailure = 181, - serviceStarted = 182, - sparingRedundancyDegraded = 183, - sparingRedundancyFull = 184, - ssbThermalTrip = 185, - systemInterfaceDisabledProvisioned = 186, - systemInterfaceUnprovisioned = 187, - systemInterfaceWhitelistProvisioned = 188, - systemPowerGoodFailed = 189, - systemPowerLost = 190, - systemPowerOffFailed = 191, - systemPowerOnFailed = 192, - voltageRegulatorOverheated = 193, + guardRecord = 52, + hardwareIsolationReason = 53, + iPMIWatchdog = 54, + intelUPILinkWidthReducedToHalf = 55, + intelUPILinkWidthReducedToQuarter = 56, + invalidLoginAttempted = 57, + invalidUpload = 58, + inventoryAdded = 59, + inventoryRemoved = 60, + lanLost = 61, + lanRegained = 62, + legacyPCIPERR = 63, + legacyPCISERR = 64, + mEAutoConfigFailed = 65, + mEAutoConfigSuccess = 66, + mEBootGuardHealthEvent = 67, + mECpuDebugCapabilityDisabled = 68, + mEDirectFlashUpdateRequested = 69, + mEExceptionDuringShutdown = 70, + mEFactoryResetError = 71, + mEFactoryRestore = 72, + mEFirmwareException = 73, + mEFirmwarePanicReason = 74, + mEFirmwareRecoveryReason = 75, + mEFirmwareResiliencyError = 76, + mEFlashEraseError = 77, + mEFlashStateInformation = 78, + mEFlashStateInformationWritingEnabled = 79, + mEFlashVerificationError = 80, + mEFlashWearOutWarning = 81, + mEImageExecutionFailed = 82, + mEInternalError = 83, + mEManufacturingError = 84, + mEMctpInterfaceError = 85, + mEMultiPchModeMisconfig = 86, + mEPeciOverDmiError = 87, + mEPttHealthEvent = 88, + mERecoveryGpioForced = 89, + mERestrictedMode = 90, + mESmbusLinkFailure = 91, + mEUmaError = 92, + mEUnsupportedFeature = 93, + manufacturingModeEntered = 94, + manufacturingModeExited = 95, + memoryECCCorrectable = 96, + memoryECCUncorrectable = 97, + memoryParityCommandAndAddress = 98, + memoryParityNotKnown = 99, + memoryRASConfigurationDisabled = 100, + memoryRASConfigurationEnabled = 101, + memoryRASModeDisabled = 102, + memoryRASModeEnabled = 103, + memoryThermTrip = 104, + mirroringRedundancyDegraded = 105, + mirroringRedundancyFull = 106, + nMIButtonPressed = 107, + nMIDiagnosticInterrupt = 108, + pCIeCorrectableAdvisoryNonFatal = 109, + pCIeCorrectableBadDLLP = 110, + pCIeCorrectableBadTLP = 111, + pCIeCorrectableHeaderLogOverflow = 112, + pCIeCorrectableInternal = 113, + pCIeCorrectableLinkBWChanged = 114, + pCIeCorrectableReceiverError = 115, + pCIeCorrectableReplayNumRollover = 116, + pCIeCorrectableReplayTimerTimeout = 117, + pCIeCorrectableUnspecifiedAERError = 118, + pCIeFatalACSViolation = 119, + pCIeFatalAtomicEgressBlocked = 120, + pCIeFatalCompleterAbort = 121, + pCIeFatalCompletionTimeout = 122, + pCIeFatalDataLinkLayerProtocol = 123, + pCIeFatalECRCError = 124, + pCIeFatalFlowControlProtocol = 125, + pCIeFatalMCBlockedTLP = 126, + pCIeFatalMalformedTLP = 127, + pCIeFatalPoisonedTLP = 128, + pCIeFatalReceivedErrNonFatalMessage = 129, + pCIeFatalReceivedFatalMessageFromDownstream = 130, + pCIeFatalReceiverBufferOverflow = 131, + pCIeFatalSurpriseLinkDown = 132, + pCIeFatalTLPPrefixBlocked = 133, + pCIeFatalUncorrectableInternal = 134, + pCIeFatalUnexpectedCompletion = 135, + pCIeFatalUnspecifiedNonAERFatalError = 136, + pCIeFatalUnsupportedRequest = 137, + powerButtonLongPressed = 138, + powerButtonPressed = 139, + powerRestorePolicyApplied = 140, + powerSupplyConfigurationError = 141, + powerSupplyConfigurationErrorRecovered = 142, + powerSupplyFailed = 143, + powerSupplyFailurePredicted = 144, + powerSupplyFanFailed = 145, + powerSupplyFanRecovered = 146, + powerSupplyInserted = 147, + powerSupplyPowerGoodFailed = 148, + powerSupplyPowerLost = 149, + powerSupplyPowerRestored = 150, + powerSupplyPredictedFailureRecovered = 151, + powerSupplyRecovered = 152, + powerSupplyRemoved = 153, + powerUnitDegradedFromNonRedundant = 154, + powerUnitDegradedFromRedundant = 155, + powerUnitNonRedundantFromInsufficient = 156, + powerUnitNonRedundantInsufficient = 157, + powerUnitNonRedundantSufficient = 158, + powerUnitRedundancyDegraded = 159, + powerUnitRedundancyLost = 160, + powerUnitRedundancyRegained = 161, + resetButtonPressed = 162, + sELEntryAdded = 163, + securityBoot2ndFlashEnabled = 164, + securityP2aBridgeEnabled = 165, + securityUartPortDebugEnabled = 166, + securityUserNonRootUidZeroAssigned = 167, + securityUserNonRootUidZeroRemoved = 168, + securityUserRootDisabled = 169, + securityUserRootEnabled = 170, + securityUserStrongHashAlgoRestored = 171, + securityUserUnsupportedShellEnabled = 172, + securityUserUnsupportedShellRemoved = 173, + securityUserWeakHashAlgoEnabled = 174, + sensorThresholdCriticalHighGoingHigh = 175, + sensorThresholdCriticalHighGoingLow = 176, + sensorThresholdCriticalLowGoingHigh = 177, + sensorThresholdCriticalLowGoingLow = 178, + sensorThresholdWarningHighGoingHigh = 179, + sensorThresholdWarningHighGoingLow = 180, + sensorThresholdWarningLowGoingHigh = 181, + sensorThresholdWarningLowGoingLow = 182, + serviceFailure = 183, + serviceStarted = 184, + sparingRedundancyDegraded = 185, + sparingRedundancyFull = 186, + ssbThermalTrip = 187, + systemInterfaceDisabledProvisioned = 188, + systemInterfaceUnprovisioned = 189, + systemInterfaceWhitelistProvisioned = 190, + systemPowerGoodFailed = 191, + systemPowerLost = 192, + systemPowerOffFailed = 193, + systemPowerOnFailed = 194, + voltageRegulatorOverheated = 195, }; } // namespace redfish::registries::openbmc diff --git a/redfish-core/include/utils/error_log_utils.hpp b/redfish-core/include/utils/error_log_utils.hpp new file mode 100644 index 000000000..cd13396e1 --- /dev/null +++ b/redfish-core/include/utils/error_log_utils.hpp @@ -0,0 +1,82 @@ +#pragma once +#include + +#include +#include + +namespace redfish +{ +namespace error_log_utils +{ + +static void getHiddenPropertyValue( + const std::shared_ptr& asyncResp, + const std::string& entryId, std::function&& callback) +{ + sdbusplus::asio::getProperty( + *crow::connections::systemBus, "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging/entry/" + entryId, + "org.open_power.Logging.PEL.Entry", "Hidden", + [callback = std::move(callback), asyncResp, + entryId](const boost::system::error_code& ec, bool hidden) { + if (ec) + { + BMCWEB_LOG_ERROR( + "Failed to get DBUS property 'Hidden' for entry {}: {}", + entryId, ec); + messages::internalError(asyncResp->res); + return; + } + callback(hidden); + }); +} + +/* + * @brief The helper API to set the Redfish error log URI in the given + * Redfish property JSON path based on the Hidden property + * which will be present in the given error log D-Bus object. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] errorLogObjPath - The error log D-Bus object path. + * @param[in] errorLogPropPath - The Redfish property json path to fill URI. + * @param[in] isLink - The boolean to add URI as a Redfish link. + * + * @return NULL + * + * @note The "isLink" parameter is used to add the URI as a link (i.e with + * "@odata.id"). If passed as "false" then, the suffix will be added + * as "/attachment" along with the URI. + * + * This API won't fill the given "errorLogPropPath" property if unable + * to process the given error log D-Bus object since the error log + * might delete by the user via Redfish but, we should not throw + * internal error in that case, just log trace and return. + */ +inline void setErrorLogUri( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& errorLogObjPath, + const nlohmann::json::json_pointer& errorLogPropPath, const bool isLink) +{ + std::string entryID = errorLogObjPath.filename(); + auto updateErrorLogPath = + [asyncResp, entryID, errorLogPropPath, isLink](bool hidden) { + std::string logPath = "EventLog"; + if (hidden) + { + logPath = "CELog"; + } + std::string linkAttachment; + if (!isLink) + { + linkAttachment = "/attachment"; + } + asyncResp->res.jsonValue[errorLogPropPath]["@odata.id"] = + boost::urls::format( + "/redfish/v1/Systems/system/LogServices/{}/Entries/{}{}", + logPath, entryID, linkAttachment); + }; + getHiddenPropertyValue(asyncResp, entryID, updateErrorLogPath); +} + +} // namespace error_log_utils +} // namespace redfish diff --git a/redfish-core/include/utils/hw_isolation.hpp b/redfish-core/include/utils/hw_isolation.hpp new file mode 100644 index 000000000..6fca8cd52 --- /dev/null +++ b/redfish-core/include/utils/hw_isolation.hpp @@ -0,0 +1,690 @@ +#pragma once + +#include "async_resp.hpp" +#include "dbus_singleton.hpp" +#include "dbus_utility.hpp" +#include "error_messages.hpp" +#include "logging.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace redfish +{ +namespace hw_isolation_utils +{ +/** + * @brief API used to return ChassisPowerStateOffRequiredError with + * the Chassis id that will get by using the given resource + * object to perform some PATCH operation on the resource object. + * + * @param[in] asyncResp - The redfish response to return to the caller. + * @param[in] resourceObjPath - The redfish resource dbus object path. + * + * @return The redfish response in given response buffer. + */ +inline void retChassisPowerStateOffRequiredError( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& resourceObjPath) +{ + crow::connections::systemBus->async_method_call( + [asyncResp, resourceObjPath]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetAncestorsResponse& ancestors) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get parent Chassis id for the given resource object [{}]", + ec.value(), ec.message(), resourceObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + if (ancestors.empty()) + { + BMCWEB_LOG_ERROR( + "The given resource object [{}] is not the child of the Chassis so failed return ChassisPowerStateOffRequiredError in the response", + resourceObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + if (ancestors.size() > 1) + { + // Should not happen since GetAncestors returs parent object + // from the given child object path and we are just looking + // for parent chassis object id alone, so we should get one + // element. + BMCWEB_LOG_ERROR( + "The given resource object [{}] is contains more than one Chassis as parent so failed return ChassisPowerStateOffRequiredError in the response", + resourceObjPath.str); + messages::internalError(asyncResp->res); + return; + } + messages::chassisPowerStateOffRequired( + asyncResp->res, + sdbusplus::message::object_path(ancestors.begin()->first) + .filename()); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetAncestors", resourceObjPath.str, + std::array{ + "xyz.openbmc_project.Inventory.Item.Chassis"}); +} + +/** + * @brief API used to isolate the given resource + * + * @param[in] asyncResp - The redfish response to return to the caller. + * @param[in] resourceName - The redfish resource name which trying to isolate. + * @param[in] resourceId - The redfish resource id which trying to isolate. + * @param[in] resourceObjPath - The redfish resource dbus object path. + * @param[in] hwIsolationDbusName - The HardwareIsolation dbus name which is + * hosting isolation dbus interfaces. + * + * @return The redfish response in given response buffer. + * + * @note This function will return the appropriate error based on the isolation + * dbus "Create" interface error. + */ +inline void isolateResource( + const std::shared_ptr& asyncResp, + const std::string& resourceName, const std::string& resourceId, + const sdbusplus::message::object_path& resourceObjPath, + const std::string& hwIsolationDbusName) +{ + crow::connections::systemBus->async_method_call( + [asyncResp, resourceName, resourceId, + resourceObjPath](const boost::system::error_code& ec, + const sdbusplus::message::message& msg) { + if (!ec) + { + messages::success(asyncResp->res); + return; + } + + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to isolate the given resource: {}", + ec.value(), ec.message(), resourceObjPath.str); + + const sd_bus_error* dbusError = msg.get_error(); + if (dbusError == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + + // The Enabled property value will be "false" to isolate. + constexpr bool enabledPropVal = false; + + BMCWEB_LOG_ERROR("DBus ErrorName: {} ErrorMsg: {}", dbusError->name, + dbusError->message); + + if (std::string_view( + "xyz.openbmc_project.Common.Error.InvalidArgument") == + dbusError->name) + { + messages::propertyValueExternalConflict( + asyncResp->res, "Enabled", + std::to_string(static_cast(enabledPropVal))); + } + else if (std::string_view( + "xyz.openbmc_project.Common.Error.NotAllowed") == + dbusError->name) + { + retChassisPowerStateOffRequiredError(asyncResp, + resourceObjPath); + } + else if ( + std::string_view( + "xyz.openbmc_project.HardwareIsolation.Error.IsolatedAlready") == + dbusError->name) + { + messages::resourceAlreadyExists( + asyncResp->res, resourceName, "Enabled", + std::to_string(static_cast(enabledPropVal))); + } + else if (std::string_view( + "xyz.openbmc_project.Common.Error.TooManyResources") == + dbusError->name) + { + messages::createLimitReachedForResource(asyncResp->res); + } + else + { + BMCWEB_LOG_ERROR( + "DBus Error is unsupported so returning as Internal Error"); + messages::internalError(asyncResp->res); + } + return; + }, + hwIsolationDbusName, "/xyz/openbmc_project/hardware_isolation", + "xyz.openbmc_project.HardwareIsolation.Create", "Create", + resourceObjPath, + "xyz.openbmc_project.HardwareIsolation.Entry.Type.Manual"); +} + +/** + * @brief API used to deisolate the given resource + * + * @param[in] asyncResp - The redfish response to return to the caller. + * @param[in] resourceObjPath - The redfish resource dbus object path. + * @param[in] hwIsolationDbusName - The HardwareIsolation dbus name which is + * hosting isolation dbus interfaces. + * + * @return The redfish response in given response buffer. + * + * @note - This function will try to identify the hardware isolated dbus entry + * from associations endpoints by using the given resource dbus object + * of "isolated_hw_entry". + */ +inline void deisolateResource( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& resourceObjPath, + const std::string& hwIsolationDbusName) +{ + // Get the HardwareIsolation entry by using the given resource + // associations endpoints + dbus::utility::getAssociationEndPoints( + resourceObjPath.str + "/isolated_hw_entry", + [asyncResp, resourceObjPath, hwIsolationDbusName]( + const boost::system::error_code& ec, + const dbus::utility::MapperEndPoints& vEndpoints) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBus response error [{} : {}] when tried to get the hardware isolation entry for the given resource dbus object path: ", + ec.value(), ec.message(), resourceObjPath.str); + // The error code (53 == Invalid request descriptor) will be + // returned if dbus doesn't contains "isolated_hw_entry" for + // the given resource i.e it is not isolated to deisolate. + // This case might occur when resource are in the certain state + if (ec.value() == EBADR) + { + messages::propertyValueConflict(asyncResp->res, "Enabled", + "Status.State"); + } + else + { + messages::internalError(asyncResp->res); + } + return; + } + + std::string resourceIsolatedHwEntry; + resourceIsolatedHwEntry = vEndpoints.back(); + + // De-isolate the given resource + crow::connections::systemBus->async_method_call( + [asyncResp, resourceIsolatedHwEntry, + resourceObjPath](const boost::system::error_code& ec1, + const sdbusplus::message::message& msg) { + if (!ec1) + { + messages::success(asyncResp->res); + return; + } + + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to isolate the given resource: {}", + ec1.value(), ec1.message(), resourceIsolatedHwEntry); + + const sd_bus_error* dbusError = msg.get_error(); + + if (dbusError == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + + BMCWEB_LOG_ERROR("DBus ErrorName: {} ErrorMsg: {}", + dbusError->name, dbusError->message); + + if (std::string_view( + "xyz.openbmc_project.Common.Error.NotAllowed") == + dbusError->name) + { + retChassisPowerStateOffRequiredError(asyncResp, + resourceObjPath); + } + else if ( + std::string_view( + "xyz.openbmc_project.Common.Error.InsufficientPermission") == + dbusError->name) + { + messages::resourceCannotBeDeleted(asyncResp->res); + } + else + { + BMCWEB_LOG_ERROR( + "DBus Error is unsupported so returning as Internal Error"); + messages::internalError(asyncResp->res); + } + return; + }, + hwIsolationDbusName, resourceIsolatedHwEntry, + "xyz.openbmc_project.Object.Delete", "Delete"); + }); +} + +/** + * @brief API used to process hardware (aka resource) isolation request + * This API can be used to any redfish resource if that redfish + * supporting isolation feature (the resource can be isolate + * from system boot) + * + * @param[in] asyncResp - The redfish response to return to the caller. + * @param[in] resourceName - The redfish resource name which trying to isolate. + * @param[in] resourceId - The redfish resource id which trying to isolate. + * @param[in] enabled - The redfish resource "Enabled" property value. + * @param[in] interfaces - The redfish resource dbus interfaces which will use + * to get the given resource dbus objects from + * the inventory. + * @param[in] parentSubtreePath - The resource parent subtree path to get + * the resource object path. + * + * @return The redfish response in given response buffer. + * + * @note - This function will identify the given resource dbus object from + * the inventory by using the given resource dbus interfaces along + * with "Object:Enable" interface (which is used to map the "Enabled" + * redfish property to dbus "Enabled" property - The "Enabled" is + * used to do isolate the resource from system boot) and the given + * redfish resource "Id". + * - This function will do either isolate or deisolate based on the + * given "Enabled" property value. + */ +inline void processHardwareIsolationReq( + const std::shared_ptr& asyncResp, + const std::string& resourceName, const std::string& resourceId, + bool enabled, const std::vector& interfaces, + const std::string& parentSubtreePath = "/xyz/openbmc_project/inventory") +{ + std::vector resourceIfaces(interfaces.begin(), + interfaces.end()); + resourceIfaces.emplace_back("xyz.openbmc_project.Object.Enable"); + + // Make sure the given resourceId is present in inventory + crow::connections::systemBus->async_method_call( + [asyncResp, resourceName, resourceId, + enabled](boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& objects) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBus response error [{} : {}] when tried to check the given resource is present in the inventory", + ec.value(), ec.message()); + messages::internalError(asyncResp->res); + return; + } + + sdbusplus::message::object_path resourceObjPath; + for (const auto& object : objects) + { + sdbusplus::message::object_path path(object); + if (path.filename() == resourceId) + { + resourceObjPath = path; + break; + } + } + + if (resourceObjPath.str.empty()) + { + messages::resourceNotFound(asyncResp->res, resourceName, + resourceId); + return; + } + + // Get the HardwareIsolation DBus name + crow::connections::systemBus->async_method_call( + [asyncResp, resourceObjPath, enabled, resourceName, + resourceId](const boost::system::error_code& ec1, + const dbus::utility::MapperGetObject& objType) { + if (ec1) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation dbus name to isolate: ", + ec1.value(), ec1.message(), resourceObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented HardwareIsolation"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved HardwareIsolation dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Make sure whether need to isolate or de-isolate + // the given resource + if (!enabled) + { + isolateResource(asyncResp, resourceName, resourceId, + resourceObjPath, objType[0].first); + } + else + { + deisolateResource(asyncResp, resourceObjPath, + objType[0].first); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/hardware_isolation", + std::array{ + "xyz.openbmc_project.HardwareIsolation.Create"}); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + parentSubtreePath, 0, resourceIfaces); +} + +/* + * @brief The helper API to set the Redfish severity level base on + * the given severity. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] objPath - The D-Bus object path of the given severity. + * @param[in] severityPropPath - The Redfish severity property json path. + * @param[in] severityVal - The D-Bus object severity. + * + * @return True on success + * False on failure and set the error in the redfish response. + */ +inline bool setSeverity(const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& objPath, + const nlohmann::json::json_pointer& severityPropPath, + const std::string& severityVal) +{ + if (severityVal == + "xyz.openbmc_project.Logging.Event.SeverityLevel.Critical") + { + asyncResp->res.jsonValue[severityPropPath] = "Critical"; + } + else if ((severityVal == + "xyz.openbmc_project.Logging.Event.SeverityLevel.Warning") || + (severityVal == + "xyz.openbmc_project.Logging.Event.SeverityLevel.Unknown")) + { + asyncResp->res.jsonValue[severityPropPath] = "Warning"; + } + else if (severityVal == + "xyz.openbmc_project.Logging.Event.SeverityLevel.Ok") + { + asyncResp->res.jsonValue[severityPropPath] = "OK"; + } + else + { + BMCWEB_LOG_ERROR("Unsupported Severity[{}] from object: {}", + severityVal, objPath.str); + messages::internalError(asyncResp->res); + return false; + } + return true; +} + +static void assembleEventProperties( + const std::shared_ptr& asyncResp, + const dbus::utility::DBusPropertiesMap& properties, + nlohmann::json& condition, const std::string& path) +{ + using AssociationsValType = + std::vector>; + const AssociationsValType* associations = nullptr; + const uint64_t* timestamp = nullptr; + const std::string* msgPropVal = nullptr; + const std::string* severity = nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "Associations", + associations, "Timestamp", timestamp, "Message", msgPropVal, "Severity", + severity); + + if (!success) + { + messages::internalError(asyncResp->res); + BMCWEB_LOG_ERROR("Could not read one or more properties from {}", path); + return; + } + + if (associations != nullptr) + { + for (const auto& assoc : *associations) + { + if (std::get<0>(assoc) == "error_log") + { + sdbusplus::message::object_path errPath = std::get<2>(assoc); + // we have only one condition + nlohmann::json::json_pointer logEntryPropPath( + "/Status/Conditions/0/LogEntry"); + error_log_utils::setErrorLogUri(asyncResp, errPath, + logEntryPropPath, true); + } + } + } + + if (timestamp != nullptr) + { + condition["Timestamp"] = redfish::time_utils::getDateTimeStdtime( + static_cast(*timestamp)); + } + + if (msgPropVal != nullptr) + { + // Host recovered even if there is hardware + // isolation entry so change the state. + if (*msgPropVal == "Recovered") + { + asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + } + + const redfish::registries::Message* msgReg = + registries::getMessage("OpenBMC.0.2.HardwareIsolationReason"); + + if (msgReg == nullptr) + { + BMCWEB_LOG_ERROR( + "Failed to get the HardwareIsolationReason message registry to add in the condition"); + messages::internalError(asyncResp->res); + return; + } + + // Prepare MessageArgs as per defined in the + // MessageRegistries + std::vector messageArgs{*msgPropVal}; + + // Fill the "msgPropVal" as reason + std::string message = msgReg->message; + int i = 0; + for (const std::string& messageArg : messageArgs) + { + std::string argIndex = "%" + std::to_string(++i); + size_t argPos = message.find(argIndex); + if (argPos != std::string::npos) + { + message.replace(argPos, argIndex.length(), messageArg); + } + } + // Severity will be added based on the event + // object property + condition["Message"] = message; + condition["MessageArgs"] = messageArgs; + condition["MessageId"] = "OpenBMC.0.2.HardwareIsolationReason"; + } + + if (severity != nullptr) + { + // we have only one condition + nlohmann::json::json_pointer severityPropPath( + "/Status/Conditions/0/Severity"); + if (!setSeverity(asyncResp, path, severityPropPath, *severity)) + { + // Failed to set the severity + return; + } + } +} + +/* + * @brief The helper API to set the Redfish Status conditions based on + * the given resource event log association. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] resourceObjPath - The resource D-Bus object object. + * + * @return NULL + */ +inline void getHwIsolationStatus( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& resourceObjPath) +{ + dbus::utility::getAssociationEndPoints( + resourceObjPath.str + "/event_log", + [asyncResp, + resourceObjPath](const boost::system::error_code& ec, + const dbus::utility::MapperEndPoints& vEndpoints) { + if (ec) + { + if (ec.value() == EBADR) + { + // No event so the respective hardware status doesn't need + // any Redfish status condition + return; + } + BMCWEB_LOG_ERROR( + "DBus response error [{} : {}] when tried to get the hardware status event for the given resource dbus object path: {} EBADR: {}", + ec.value(), ec.message(), resourceObjPath.str, EBADR); + messages::internalError(asyncResp->res); + return; + } + + bool found = false; + std::string hwStatusEventObj; + for (const auto& endpoint : vEndpoints) + { + if (sdbusplus::message::object_path(endpoint) + .parent_path() + .filename() == "hw_isolation_status") + { + hwStatusEventObj = endpoint; + found = true; + break; + } + } + + if (!found) + { + // No event so the respective hardware status doesn't need + // any Redfish status condition + return; + } + + // Get the dbus service name of the hardware status event object + crow::connections::systemBus->async_method_call( + [asyncResp, hwStatusEventObj]( + const boost::system::error_code& ec1, + const dbus::utility::MapperGetObject& objType) { + if (ec1) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the dbus name of the hardware status event object {}", + ec1.value(), ec1.message(), hwStatusEventObj); + messages::internalError(asyncResp->res); + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the same hardware status event object {}", + hwStatusEventObj); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved hardware status event object dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Get event properties and fill into status conditions + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, objType[0].first, + hwStatusEventObj, "", + [asyncResp, hwStatusEventObj]( + const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& + properties) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the hardware status event object properties {}", + ec2.value(), ec2.message(), + hwStatusEventObj); + messages::internalError(asyncResp->res); + return; + } + + // Event is exist and that will get created when + // the respective hardware is not functional so + // set the state as "Disabled". + asyncResp->res.jsonValue["Status"]["State"] = + "Disabled"; + + nlohmann::json& conditions = + asyncResp->res + .jsonValue["Status"]["Conditions"]; + conditions = nlohmann::json::array(); + conditions.push_back(nlohmann::json::object()); + nlohmann::json& condition = conditions.back(); + + assembleEventProperties(asyncResp, properties, + condition, + hwStatusEventObj); + }); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + hwStatusEventObj, + std::array{ + "xyz.openbmc_project.Logging.Event"}); + }); +} + +} // namespace hw_isolation_utils +} // namespace redfish diff --git a/redfish-core/lib/assembly.hpp b/redfish-core/lib/assembly.hpp index 0f7edbe4c..bea86d431 100644 --- a/redfish-core/lib/assembly.hpp +++ b/redfish-core/lib/assembly.hpp @@ -27,13 +27,16 @@ #include #include #include +#include #include +#include #include #include #include #include #include #include +#include #include #include @@ -696,6 +699,304 @@ inline void handleChassisAssemblyPatch( }); } +namespace assembly +{ +/** + * @brief API used to fill the Assembly id of the assembled object that + * assembled in the given assembly parent object path. + * + * bmcweb using the sequential numeric value by sorting the + * assembled objects instead of the assembled object dbus id + * for the Redfish Assembly implementation. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] assemblyParentServ - The assembly parent dbus service name. + * @param[in] assemblyParentObjPath - The assembly parent dbus object path. + * @param[in] assemblyParentIface - The assembly parent dbus interface name + * to valid the supports in the bmcweb. + * @param[in] assemblyUriPropPath - The redfish property path to fill with id. + * @param[in] assembledObjPath - The assembled object that need to fill with + * its id. Used to check in the parent assembly + * associations. + * @param[in] assembledUriVal - The assembled object redfish uri value that + * need to replace with its id. + * + * @return The redfish response with assembled object id in the given + * redfish property path if success else returns the error. + */ +inline void fillWithAssemblyId( + const std::shared_ptr& asyncResp, + const std::string& assemblyParentServ, + const sdbusplus::message::object_path& assemblyParentObjPath, + const std::string& assemblyParentIface, + const nlohmann::json::json_pointer& assemblyUriPropPath, + const sdbusplus::message::object_path& assembledObjPath, + const std::string& assembledUriVal) +{ + if (assemblyParentIface != "xyz.openbmc_project.Inventory.Item.Chassis") + { + // Currently, bmcweb supporting only chassis assembly uri so return + // error if unsupported assembly uri interface was given + BMCWEB_LOG_ERROR( + "Unsupported interface [{}] was given to fill assembly id. Please add support in the bmcweb", + assemblyParentIface); + messages::internalError(asyncResp->res); + return; + } + + using associationList = + std::vector>; + + sdbusplus::asio::getProperty(*crow::connections::systemBus, + assemblyParentServ, + assemblyParentObjPath.str, + "xyz.openbmc_project.Association.Definitions", + "Associations", + [asyncResp, + assemblyUriPropPath, + assemblyParentObjPath, + assembledObjPath, + assembledUriVal]( + const boost::system:: + error_code& ec, + const associationList& + associations) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the Associations from [{}] to fill Assembly id of the assembled object [{}]", + ec.value(), + ec.message(), + assemblyParentObjPath + .str, + assembledObjPath + .str); + messages:: + internalError( + asyncResp + ->res); + return; + } + + std::vector + assemblyAssoc; + for (const auto& + association : + associations) + { + if (std::get<0>( + association) != + "assembly") + { + continue; + } + assemblyAssoc.emplace_back( + std::get<2>( + association)); + } + + if (assemblyAssoc.empty()) + { + BMCWEB_LOG_ERROR( + "No assembly associations in the [{}] to fill Assembly id of the assembled object [{}]", + assemblyParentObjPath + .str, + assembledObjPath + .str); + messages:: + internalError( + asyncResp + ->res); + return; + } + + // Mak sure whether the + // retrieved assembly + // associations are + // implemented before + // finding the assembly id + // as per bmcweb Assembly + // design. + crow::connections:: + systemBus + ->async_method_call( + [asyncResp, + assemblyUriPropPath, + assemblyParentObjPath, + assembledObjPath, + assemblyAssoc, + assembledUriVal]( + const boost::system::error_code& ec1, const dbus::utility:: + MapperGetSubTreeResponse& + objects) { + if (ec1) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the subtree to check assembled objects implementation of the [{}] to find assembled object id of the [{}] to fill in the URI property", + ec1.value(), + ec1.message(), + assemblyParentObjPath + .str, + assembledObjPath + .str); + messages::internalError( + asyncResp + ->res); + return; + } + + if (objects + .empty()) + { + BMCWEB_LOG_ERROR( + "No objects in the [{}] to check assembled objects implementation to fill the assembled object [{}] id in the URI property", + assemblyParentObjPath + .str, + assembledObjPath + .str); + messages::internalError( + asyncResp + ->res); + return; + } + + std::vector< + std:: + string> + implAssemblyAssocs; + for ( + const auto& + object : + objects) + { + auto it = std:: + ranges::find( + assemblyAssoc, + object + .first); + if (it != + assemblyAssoc + .end()) + { + implAssemblyAssocs + .emplace_back( + *it); + } + } + + if (implAssemblyAssocs + .empty()) + { + BMCWEB_LOG_ERROR( + "The assembled objects of the [{}] are not implemented so unable to fill the assembled object [{}] id in the URI property", + assemblyParentObjPath + .str, + assembledObjPath + .str); + messages::internalError( + asyncResp + ->res); + return; + } + + // sort + // the + // implemented + // assemply + // object + // as per + // bmcweb + // design + // to + // match + // with + // Assembly + // GET and + // PATCH + // handler. + std::ranges:: + sort( + implAssemblyAssocs); + + auto assembledObjectIt = + std::ranges::find( + implAssemblyAssocs, + assembledObjPath + .str); + + if (assembledObjectIt == + implAssemblyAssocs + .end()) + { + BMCWEB_LOG_ERROR( + "The assembled object [{}] in the object [{}] is not implemented so unable to fill assembled object id in the URI property", + assembledObjPath + .str, + assemblyParentObjPath + .str); + messages::internalError( + asyncResp + ->res); + return; + } + + auto assembledObjectId = + std::distance( + implAssemblyAssocs + .begin(), + assembledObjectIt); + + std::string::size_type assembledObjectNamePos = + assembledUriVal + .rfind( + assembledObjPath + .filename()); + + if (assembledObjectNamePos == + std::string:: + npos) + { + BMCWEB_LOG_ERROR( + "The assembled object name [{}] is not found in the redfish property value [{}] to replace with assembled object id [{}]", + assembledObjPath + .filename(), + assembledUriVal, + assembledObjectId); + messages::internalError( + asyncResp + ->res); + return; + } + std::string + uriValwithId( + assembledUriVal); + uriValwithId + .replace( + assembledObjectNamePos, + assembledObjPath + .filename() + .length(), + std::to_string( + assembledObjectId)); + + asyncResp + ->res + .jsonValue + [assemblyUriPropPath] = + uriValwithId; + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", + "GetSubTree", + "/xyz/openbmc_project/inventory", + int32_t(0), + chassisAssemblyInterfaces); + }); +} + +} // namespace assembly + /** * Systems derived class for delivering Assembly Schema. */ diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 51f8832a8..99a1270a7 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -27,6 +27,7 @@ #include "task_messages.hpp" #include "utils/dbus_event_log_entry.hpp" #include "utils/dbus_utils.hpp" +#include "utils/error_log_utils.hpp" #include "utils/json_utils.hpp" #include "utils/query_param.hpp" #include "utils/time_utils.hpp" @@ -1263,6 +1264,16 @@ inline void requestRoutesSystemLogServiceCollection(App& app) "/redfish/v1/Systems/system/LogServices/AuditLog"; logServiceArray.push_back(std::move(auditLog)); } + if constexpr (BMCWEB_HW_ISOLATION) + { + nlohmann::json& logServiceArrayLocal = + asyncResp->res.jsonValue["Members"]; + logServiceArrayLocal.push_back( + {{"@odata.id", "/redfish/v1/Systems/system/" + "LogServices/HardwareIsolation"}}); + asyncResp->res.jsonValue["Members@odata.count"] = + logServiceArrayLocal.size(); + } }); } @@ -1748,27 +1759,6 @@ inline void handleSystemsLogServiceEventLogEntriesGet( messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); } -inline void getHiddenPropertyValue( - const std::shared_ptr& asyncResp, - const std::string& entryId, - std::function&& callback) -{ - dbus::utility::getProperty( - "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging/entry/" + entryId, - "org.open_power.Logging.PEL.Entry", "Hidden", - [callback = std::move(callback), - asyncResp](const boost::system::error_code& ec, bool hidden) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - callback(hidden); - }); -} - inline void requestRoutesJournalEventLogEntry(App& app) { BMCWEB_ROUTE( @@ -2396,7 +2386,7 @@ inline void handleDBusEventLogEntryDownloadGet( return; } - getHiddenPropertyValue( + redfish::error_log_utils::getHiddenPropertyValue( asyncResp, entryID, [asyncResp, entryID, systemName, dumpType, hidden](bool hiddenPropVal) { if (hiddenPropVal != hidden) @@ -2571,7 +2561,7 @@ inline void requestRoutesDBusEventLogEntryDownloadPelJson(App& app) std::string entryID = param; dbus::utility::escapePathForDbus(entryID); - getHiddenPropertyValue( + redfish::error_log_utils::getHiddenPropertyValue( asyncResp, entryID, [asyncResp, entryID](bool hiddenPropVal) { if (hiddenPropVal) diff --git a/redfish-core/lib/memory.hpp b/redfish-core/lib/memory.hpp index 02007b06c..0a9f2ec92 100644 --- a/redfish-core/lib/memory.hpp +++ b/redfish-core/lib/memory.hpp @@ -7,6 +7,7 @@ #include "app.hpp" #include "async_resp.hpp" +#include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "generated/enums/memory.hpp" @@ -19,6 +20,7 @@ #include "utils/collection.hpp" #include "utils/dbus_utils.hpp" #include "utils/hex_utils.hpp" +#include "utils/hw_isolation.hpp" #include "utils/json_utils.hpp" #include "utils/name_utils.hpp" @@ -28,6 +30,7 @@ #include #include #include +#include #include #include @@ -45,6 +48,10 @@ namespace redfish { +// Interfaces which imply a D-Bus object represents a Memory +constexpr std::array dimmInterfaces = { + "xyz.openbmc_project.Inventory.Item.Dimm"}; + inline std::string translateMemoryTypeToRedfish(const std::string& memoryType) { if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR") @@ -727,6 +734,39 @@ inline void getDimmPartitionData(std::shared_ptr asyncResp, ); } +/** + * @brief API used to get the Object.Enable interface properties value + * for Memory + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] service - The dbus service name which is hosting the given path. + * @param[in] path - The given Memory resource inventory dbus object path. + * + * @return The redfish response in the given buffer. + * + * @note - The "Enabled" member of the Memory (aka DIMM) is mapped with + * "xyz.openbmc_project.Object.Enable::Enabled" dbus property. + */ +inline void getObjectEnable(std::shared_ptr asyncResp, + const std::string& service, const std::string& path) +{ + sdbusplus::asio::getProperty( + *crow::connections::systemBus, service, path, + "xyz.openbmc_project.Object.Enable", "Enabled", + [asyncResp{std::move(asyncResp)}](const boost::system::error_code& ec, + const bool enabled) { + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error [{} : {}]", ec.value(), + ec.message()); + messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.jsonValue["Enabled"] = enabled; + }); +} + inline void afterGetDimmData( const std::shared_ptr& asyncResp, const std::string& dimmId, const boost::system::error_code& ec, @@ -749,6 +789,7 @@ inline void afterGetDimmData( bool dimmInterface = false; bool associationInterface = false; + bool objectEnable = false; /* Note: Multiple D-Bus objects can provide details for the Memory * object: 1) Dimm is the primary object 2) Additional partitions could * exist per Dimm. Only consider the object found if the Dimm is found. @@ -788,6 +829,10 @@ inline void afterGetDimmData( // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2 getDimmPartitionData(asyncResp, serviceName, objectPath); } + else if (interface == "xyz.openbmc_project.Object.Enable") + { + objectEnable = true; + } } /* If a Dimm has an Association check if it has a LED */ @@ -795,6 +840,11 @@ inline void afterGetDimmData( { getLocationIndicatorActive(asyncResp, objectPath); } + + if (dimmInterface && objectEnable) + { + getObjectEnable(asyncResp, serviceName, objectPath); + } } } @@ -828,6 +878,34 @@ inline void getDimmData(const std::shared_ptr& asyncResp, }); } +/** + * @brief API used to process the Memory "Enabled" member which is + * patched to do appropriate action. + * + * @param[in] resp - The redfish response to return. + * @param[in] dimmId - The patched Memory (aka DIMM) resource id. + * @param[in] enabled - The patched "Enabled" member value. + * + * @return The redfish response in the given buffer. + * + * @note - The "Enabled" member of the Memory (aka DIMM) is used to enable + * (aka isolate) or disable (aka deisolate) the resource from the + * system boot so this function will call "processHardwareIsolationReq" + * function which is used to handle the resource isolation request. + * - The "Enabled" member of the Memory is mapped with + * "xyz.openbmc_project.Object.Enable::Enabled" dbus property. + */ + +inline void patchMemberEnabled( + const std::shared_ptr& asynResp, + const std::string& dimmId, const bool enabled) +{ + redfish::hw_isolation_utils::processHardwareIsolationReq( + asynResp, "Memory", dimmId, enabled, + std::vector(dimmInterfaces.begin(), + dimmInterfaces.end())); +} + inline void handleSetDimmData( const std::shared_ptr& asyncResp, bool locationIndicatorActive, const std::string& dimmPath, @@ -932,9 +1010,12 @@ inline void handleMemoryPatch( } std::optional locationIndicatorActive; - if (!json_util::readJsonPatch( // - req, asyncResp->res, // - "LocationIndicatorActive", locationIndicatorActive // + + std::optional enabled; + if (!json_util::readJsonPatch( // + req, asyncResp->res, // + "LocationIndicatorActive", locationIndicatorActive, "Enabled", + enabled // )) { return; @@ -946,6 +1027,11 @@ inline void handleMemoryPatch( std::bind_front(handleSetDimmData, asyncResp, *locationIndicatorActive)); } + + if (enabled.has_value()) + { + patchMemberEnabled(asyncResp, dimmId, *enabled); + } } inline void handleMemoryGet(App& app, const crow::Request& req, diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp index e7d2d23f2..1382df66b 100644 --- a/redfish-core/lib/processor.hpp +++ b/redfish-core/lib/processor.hpp @@ -7,6 +7,7 @@ #include "app.hpp" #include "async_resp.hpp" +#include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "generated/enums/processor.hpp" @@ -19,6 +20,7 @@ #include "utils/collection.hpp" #include "utils/dbus_utils.hpp" #include "utils/hex_utils.hpp" +#include "utils/hw_isolation.hpp" #include "utils/json_utils.hpp" #include "utils/name_utils.hpp" @@ -699,8 +701,7 @@ inline void getCpuConfigData( // config. dbus::utility::getProperty( service, dbusPath, - "xyz.openbmc_project.Inventory.Item.Cpu." - "OperatingConfig", + "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", "BaseSpeedPrioritySettings", [asyncResp](const boost::system::error_code& ec2, const BaseSpeedPrioritySettingsProperty& @@ -999,6 +1000,122 @@ inline void getProcessorPaths( }); } +inline void getCpuCoreDataByService( + const std::shared_ptr& asyncResp, + const std::string& service, const std::string& objPath) +{ + BMCWEB_LOG_DEBUG("Get available system cpu core resources by service."); + + asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + + crow::connections::systemBus->async_method_call( + [objPath, asyncResp](const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& dbusData) { + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error, ec: {}", ec.value()); + messages::internalError(asyncResp->res); + return; + } + + for (const auto& [path, interfaces] : dbusData) + { + if (path != objPath) + { + continue; + } + + bool present = true; + bool functional = true; + + for (const auto& [interface, properties] : interfaces) + { + if (interface == + "xyz.openbmc_project.State.Decorator.OperationalStatus") + { + for (const auto& [propName, propValue] : properties) + { + if (propName == "Functional") + { + const bool* value = + std::get_if(&propValue); + if (value == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + functional = *value; + break; + } + } + } + else if (interface == "xyz.openbmc_project.Inventory.Item") + { + for (const auto& [propName, propValue] : properties) + { + if (propName == "Present") + { + const bool* value = + std::get_if(&propValue); + if (value == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + present = *value; + } + else if (propName == "PrettyName") + { + const std::string* prettyName = + std::get_if(&propValue); + if (prettyName == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Name"] = *prettyName; + } + } + } + else if (interface == "xyz.openbmc_project.Object.Enable") + { + for (const auto& [propName, propValue] : properties) + { + if (propName == "Enabled") + { + const bool* enabled = + std::get_if(&propValue); + if (enabled == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Enabled"] = *enabled; + break; + } + } + } + } + + if (!present) + { + asyncResp->res.jsonValue["Status"]["State"] = "Absent"; + } + else + { + if (!functional) + { + asyncResp->res.jsonValue["Status"]["Health"] = + "Critical"; + } + } + } + }, + service, "/xyz/openbmc_project/inventory", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); +} + inline void getOperationalStatusData( const std::shared_ptr& asyncResp, const std::string& service, const std::string& objPath, @@ -1083,6 +1200,11 @@ inline void getSubProcessorsCoreData( "/Name"_json_pointer); } } + if constexpr (BMCWEB_HW_ISOLATION) + { + // Check for the hardware status event + hw_isolation_utils::getHwIsolationStatus(asyncResp, corePath); + } } } @@ -1142,6 +1264,76 @@ inline void getSubProcessorsCoreObject( }); } +inline void getSubProcessorData( + const std::shared_ptr& asyncResp, + const std::string& processorId, const std::string& coreId) +{ + BMCWEB_LOG_DEBUG("Get available system sub processor resources."); + + auto callback = [asyncResp, processorId, + coreId](const std::string& cpuPath) { + crow::connections::systemBus->async_method_call( + [asyncResp, processorId, + coreId](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error, ec: {}", ec.value()); + // No processor objects found by mapper + if (ec.value() == boost::system::errc::io_error) + { + messages::resourceNotFound( + asyncResp->res, "#Processor.v1_18_0.Processor", + processorId); + return; + } + + messages::internalError(asyncResp->res); + return; + } + + for (const auto& object : subtree) + { + if (sdbusplus::message::object_path(object.first) + .filename() != coreId) + { + continue; + } + + asyncResp->res.jsonValue["@odata.type"] = + "#Processor.v1_18_0.Processor"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/system/Processors/{}/SubProcessors/{}", + processorId, coreId); + asyncResp->res.jsonValue["Name"] = "SubProcessor"; + asyncResp->res.jsonValue["Id"] = coreId; + + for (const auto& service : object.second) + { + getCpuCoreDataByService(asyncResp, service.first, + object.first); + break; + } + return; + } + + if (!subtree.empty()) + { + // Object not found + messages::resourceNotFound( + asyncResp->res, "#Processor.v1_18_0.Processor", coreId); + return; + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", cpuPath, 0, + procCoreInterfaces); + }; + + getProcessorPaths(asyncResp, processorId, std::move(callback)); +} + inline void getSubProcessorMembers( const std::shared_ptr& asyncResp, const std::string& processorId, const std::string& cpuPath) @@ -1293,9 +1485,10 @@ inline void patchAppliedOperatingConfig( const std::string* controlService = nullptr; for (const auto& [serviceName, interfaceList] : serviceMap) { - if (std::ranges::find(interfaceList, - "xyz.openbmc_project.Control.Processor." - "CurrentOperatingConfig") != interfaceList.end()) + if (std::ranges::find( + interfaceList, + "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig") != + interfaceList.end()) { controlService = &serviceName; break; @@ -1768,6 +1961,89 @@ inline void requestRoutesProcessor(App& app) .methods(boost::beast::http::verb::patch)( std::bind_front(handleProcessorPatch, std::ref(app))); } +/** + * @brief API used to process the Processor Core "Enabled" member which is + * patched to do appropriate action. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] coreId - The patched Processor Core resource id. + * @param[in] enabled - The patched "Enabled" member value. + * + * @return The redfish response in the given buffer. + * + * @note - The "Enabled" member of the Processor Core is used to enable + * (aka isolate) or disable (aka deisolate) the resource from the + * system boot so this function will call "processHardwareIsolationReq" + * function which is used to handle the resource isolation request. + * - The "Enabled" member of the Processor Core is mapped with + * "xyz.openbmc_project.Object.Enable::Enabled" dbus property. + */ +inline void patchCpuCoreMemberEnabled( + const std::shared_ptr& resp, + const std::string& procObjPath, const std::string& coreId, + const bool enabled) +{ + redfish::hw_isolation_utils::processHardwareIsolationReq( + resp, "Core", coreId, enabled, + std::vector(procCoreInterfaces.begin(), + procCoreInterfaces.end()), + procObjPath); +} + +/** + * @brief API used to process the Processor Core members which are tried to + * patch. + * + * @param[in] req - The redfish patched request to identify the patched members + * @param[in] asyncResp - The redfish response to return. + * @param[in] processorId - The patched Core Processor resource id (unused now) + * @param[in] coreId - The patched Processor Core resource id. + * + * @return The redfish response in the given buffer. + * + * @note This function will call the appropriate function to handle the patched + * members of the Processor Core. + */ +inline void patchCpuCoreMembers( + const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& processorId, const std::string& coreId) +{ + std::optional enabled; + + if (!json_util::readJsonPatch(req, asyncResp->res, "Enabled", enabled)) + { + return; + } + + auto callback = [asyncResp, coreId, enabled](const std::string& cpuPath) { + // Handle patched Enabled Redfish property + if (enabled.has_value()) + { + patchCpuCoreMemberEnabled(asyncResp, cpuPath, coreId, *enabled); + } + }; + + getProcessorPaths(asyncResp, processorId, std::move(callback)); +} + +inline void requestRoutesSubProcessorsCore(App& app) +{ + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/system/Processors//SubProcessors/") + .privileges(redfish::privileges::getProcessor) + .methods(boost::beast::http::verb::get)( + [](const crow::Request&, + const std::shared_ptr& asyncResp, + const std::string& processorId, const std::string& coreId) { + getSubProcessorData(asyncResp, processorId, coreId); + }); + + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/system/Processors//SubProcessors/") + .privileges(redfish::privileges::patchProcessor) + .methods(boost::beast::http::verb::patch)(patchCpuCoreMembers); +} inline void handleSubProcessorGet( App& app, const crow::Request& req, diff --git a/redfish-core/lib/systems_logservices_celog.hpp b/redfish-core/lib/systems_logservices_celog.hpp index cf1d12837..9079fa508 100644 --- a/redfish-core/lib/systems_logservices_celog.hpp +++ b/redfish-core/lib/systems_logservices_celog.hpp @@ -15,6 +15,7 @@ #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/dbus_utils.hpp" +#include "utils/error_log_utils.hpp" #include "utils/json_utils.hpp" #include "utils/time_utils.hpp" @@ -243,7 +244,7 @@ inline void dBusCELogEntryPatch( return; } - getHiddenPropertyValue( + error_log_utils::getHiddenPropertyValue( asyncResp, entryId, [resolved, managementSystemAck, asyncResp, entryId](bool hiddenPropVal) { @@ -263,7 +264,7 @@ inline void dBusCELogEntryDelete( BMCWEB_LOG_DEBUG("Do delete single event entries."); dbus::utility::escapePathForDbus(entryID); - getHiddenPropertyValue( + error_log_utils::getHiddenPropertyValue( asyncResp, entryID, [asyncResp, entryID](bool hiddenPropVal) { if (!hiddenPropVal) { @@ -399,7 +400,7 @@ inline void requestRoutesDBusCELogEntryDownloadPelJson(App& app) std::string entryID = param; dbus::utility::escapePathForDbus(entryID); - getHiddenPropertyValue( + error_log_utils::getHiddenPropertyValue( asyncResp, entryID, [asyncResp, entryID](bool hiddenPropVal) { if (!hiddenPropVal) diff --git a/redfish-core/lib/systems_logservices_hwisolation.hpp b/redfish-core/lib/systems_logservices_hwisolation.hpp new file mode 100644 index 000000000..a5a816e5f --- /dev/null +++ b/redfish-core/lib/systems_logservices_hwisolation.hpp @@ -0,0 +1,1206 @@ +#pragma once + +#include "bmcweb_config.h" + +#include "app.hpp" +#include "assembly.hpp" +#include "async_resp.hpp" +#include "dbus_singleton.hpp" +#include "dbus_utility.hpp" +#include "error_messages.hpp" +#include "http_request.hpp" +#include "logging.hpp" +#include "registries/privilege_registry.hpp" +#include "utils/error_log_utils.hpp" +#include "utils/time_utils.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace redfish +{ +/**************************************************** + * Redfish HardwareIsolationLog interfaces + ******************************************************/ + +constexpr std::array hwIsolationEntryIfaces = { + "xyz.openbmc_project.HardwareIsolation.Entry", + "xyz.openbmc_project.Association.Definitions", + "xyz.openbmc_project.Time.EpochTime"}; + +using RedfishResourceDBusInterfaces = std::string; +using RedfishResourceCollectionUri = std::string; +using RedfishUriListType = std::unordered_map; + +static const RedfishUriListType redfishUriList = { + {"xyz.openbmc_project.Inventory.Item.Cpu", + "/redfish/v1/Systems/system/Processors"}, + {"xyz.openbmc_project.Inventory.Item.Dimm", + "/redfish/v1/Systems/system/Memory"}, + {"xyz.openbmc_project.Inventory.Item.CpuCore", + "/redfish/v1/Systems/system/Processors//SubProcessors"}, + {"xyz.openbmc_project.Inventory.Item.Chassis", "/redfish/v1/Chassis"}, + {"xyz.openbmc_project.Inventory.Item.Tpm", + "/redfish/v1/Chassis//Assembly#/Assemblies"}, + {"xyz.openbmc_project.Inventory.Item.Board.Motherboard", + "/redfish/v1/Chassis//Assembly#/Assemblies"}}; + +using AssociationsValType = + std::vector>; +using GetManagedPropertyType = boost::container::flat_map< + std::string, + std::variant>; + +using GetManagedObjectsType = boost::container::flat_map< + sdbusplus::message::object_path, + boost::container::flat_map>; + +/** + * @brief API Used to add the supported HardwareIsolation LogServices Members + * + * @param[in] req - The HardwareIsolation redfish request (unused now). + * @param[in] asyncResp - The redfish response to return. + * + * @return The redfish response in the given buffer. + */ +inline void getSystemHardwareIsolationLogService( + const crow::Request& /* req */, + const std::shared_ptr& asyncResp) +{ + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HardwareIsolation", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["Name"] = "Hardware Isolation LogService"; + asyncResp->res.jsonValue["Description"] = + "Hardware Isolation LogService for system owned devices"; + asyncResp->res.jsonValue["Id"] = "HardwareIsolation"; + + asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HardwareIsolation/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + + asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] + ["target"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HardwareIsolation/Actions/LogService.ClearLog", + BMCWEB_REDFISH_SYSTEM_URI_NAME); +} + +/** + * @brief Workaround to handle DCM (Dual-Chip Module) package for Redfish + * + * This API will make sure processor modeled as dual chip module, If yes then, + * replace the redfish processor id as "dcmN-cpuN" because redfish currently + * does not support chip module concept. + * + * @param[in] dbusObjPath - The D-Bus object path to return the object instance + * + * @return the object instance with it parent instance id if the given object + * is a processor else the object instance alone. + */ +inline std::string getIsolatedHwItemId( + const sdbusplus::message::object_path& dbusObjPath) +{ + std::string isolatedHwItemId; + + if ((dbusObjPath.filename().find("cpu") != std::string::npos) && + (dbusObjPath.parent_path().filename().find("dcm") != std::string::npos)) + { + isolatedHwItemId = + std::format("{}-{}", dbusObjPath.parent_path().filename(), + dbusObjPath.filename()); + } + else + { + isolatedHwItemId = dbusObjPath.filename(); + } + return isolatedHwItemId; +} + +/** + * @brief API used to get redfish uri of the given dbus object and fill into + * "OriginOfCondition" property of LogEntry schema. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] dbusObjPath - The DBus object path which represents redfishUri. + * @param[in] entryJsonIdx - The json entry index to add isolated hardware + * details in the appropriate entry json object. + * + * @return The redfish response with "OriginOfCondition" property of + * LogEntry schema if success else return the error + */ +inline void getRedfishUriByDbusObjPath( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& dbusObjPath, + const size_t entryJsonIdx) +{ + crow::connections::systemBus->async_method_call( + [asyncResp, dbusObjPath, + entryJsonIdx](const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& objType) { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the RedfishURI of isolated hareware: {}", + ec.value(), ec.message(), dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + RedfishUriListType::const_iterator redfishUriIt; + for (const auto& service : objType) + { + for (const auto& interface : service.second) + { + redfishUriIt = redfishUriList.find(interface); + if (redfishUriIt != redfishUriList.end()) + { + // Found the Redfish URI of the isolated hardware unit. + break; + } + } + if (redfishUriIt != redfishUriList.end()) + { + // No need to check in the next service interface list + break; + } + } + + if (redfishUriIt == redfishUriList.end()) + { + BMCWEB_LOG_ERROR( + "The object[{}] interface is not found in the Redfish URI list. Please add the respective D-Bus interface name", + dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + // Fill the isolated hardware object id along with the Redfish URI + std::string redfishUri = + redfishUriIt->second + "/" + getIsolatedHwItemId(dbusObjPath); + + // Make sure whether no need to fill the parent object id in the + // isolated hardware Redfish URI. + const std::string uriIdPattern{""}; + size_t uriIdPos = redfishUri.rfind(uriIdPattern); + if (uriIdPos == std::string::npos) + { + if (entryJsonIdx > 0) + { + asyncResp->res + .jsonValue["Members"][entryJsonIdx - 1]["Links"] + ["OriginOfCondition"]["@odata.id"] = + redfishUri; + } + else + { + asyncResp->res + .jsonValue["Links"]["OriginOfCondition"]["@odata.id"] = + redfishUri; + } + return; + } + bool isChassisAssemblyUri = false; + std::string::size_type assemblyStartPos = + redfishUri.rfind("/Assembly#/Assemblies"); + if (assemblyStartPos != std::string::npos) + { + // Redfish URI using path segment like DBus object path + // so using object_path type + if (sdbusplus::message::object_path( + redfishUri.substr(0, assemblyStartPos)) + .parent_path() + .filename() != "Chassis") + { + // Currently, bmcweb supporting only chassis + // assembly uri so return error if unsupported + // assembly uri added in the redfishUriList. + BMCWEB_LOG_ERROR( + "Unsupported Assembly URI [{}] to fill in the OriginOfCondition. Please add support in the bmcweb", + redfishUri); + messages::internalError(asyncResp->res); + return; + } + isChassisAssemblyUri = true; + } + // Fill the all parents Redfish URI id. + // For example, the processors id for the core. + // "/redfish/v1/Systems/system/Processors//SubProcessors/core0" + std::vector> + ancestorsIfaces; + while (uriIdPos != std::string::npos) + { + std::string parentRedfishUri = + redfishUri.substr(0, uriIdPos - 1); + RedfishUriListType::const_iterator parentRedfishUriIt = + std::ranges::find_if( + redfishUriList, [parentRedfishUri](const auto& ele) { + return parentRedfishUri == ele.second; + }); + + if (parentRedfishUriIt == redfishUriList.end()) + { + BMCWEB_LOG_ERROR( + "Failed to fill Links:OriginOfCondition because unable to get parent Redfish URI [{}] DBus interface for the identified Redfish URI: {} of the given DBus object path: {}", + parentRedfishUri, redfishUri, dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + ancestorsIfaces.emplace_back(parentRedfishUriIt->first, + uriIdPos); + size_t tempUriIdPos = uriIdPos - uriIdPattern.length(); + uriIdPos = redfishUri.rfind(uriIdPattern, tempUriIdPos); + } + + // GetAncestors only accepts "as" for the interface list + std::vector ancestorsIfacesOnly; + std::transform( + ancestorsIfaces.begin(), ancestorsIfaces.end(), + std::back_inserter(ancestorsIfacesOnly), + [](const std::pair& p) { + return p.first; + }); + crow::connections::systemBus->async_method_call( + [asyncResp, dbusObjPath, entryJsonIdx, redfishUri, uriIdPattern, + ancestorsIfaces, isChassisAssemblyUri]( + const boost::system::error_code& ec1, + const dbus::utility::MapperGetSubTreeResponse& + ancestors) mutable { + if (ec1) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to fill the parent objects id in the RedfishURI: {} of the isolated hareware: {}", + ec1.value(), ec1.message(), redfishUri, + dbusObjPath.str); + + messages::internalError(asyncResp->res); + return; + } + // tuple: assembly parent service name, object path, and + // interface + std::tuple + assemblyParent; + + for (const auto& ancestorIface : ancestorsIfaces) + { + bool foundAncestor = false; + for (const auto& obj : ancestors) + { + for (const auto& service : obj.second) + { + auto interfaceIter = std::ranges::find( + service.second, ancestorIface.first); + if (interfaceIter != service.second.end()) + { + foundAncestor = true; + redfishUri.replace( + ancestorIface.second, + uriIdPattern.length(), + getIsolatedHwItemId( + sdbusplus::message::object_path( + obj.first))); + + if (isChassisAssemblyUri && + ancestorIface.first == + "xyz.openbmc_project.Inventory.Item.Chassis") + { + assemblyParent = std::make_tuple( + service.first, + sdbusplus::message::object_path( + obj.first), + ancestorIface.first); + } + break; + } + } + if (foundAncestor) + { + break; + } + } + + if (!foundAncestor) + { + BMCWEB_LOG_ERROR( + "Failed to fill Links:OriginOfCondition because unable to get parent DBus path for the identified parent interface : {} of the given DBus object path: {}", + ancestorIface.first, dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + } + + if (entryJsonIdx > 0) + { + asyncResp->res + .jsonValue["Members"][entryJsonIdx - 1]["Links"] + ["OriginOfCondition"]["@odata.id"] = + redfishUri; + + if (isChassisAssemblyUri) + { + auto uriPropPath = "/Members"_json_pointer; + uriPropPath /= entryJsonIdx - 1; + uriPropPath /= "Links/OriginOfCondition/@odata.id"; + + assembly::fillWithAssemblyId( + asyncResp, std::get<0>(assemblyParent), + std::get<1>(assemblyParent), + std::get<2>(assemblyParent), uriPropPath, + dbusObjPath, redfishUri); + } + } + else + { + asyncResp->res.jsonValue["Links"]["OriginOfCondition"] + ["@odata.id"] = redfishUri; + + if (isChassisAssemblyUri) + { + auto uriPropPath = + "/Links/OriginOfCondition/@odata.id"_json_pointer; + + assembly::fillWithAssemblyId( + asyncResp, std::get<0>(assemblyParent), + std::get<1>(assemblyParent), + std::get<2>(assemblyParent), uriPropPath, + dbusObjPath, redfishUri); + } + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetAncestors", + dbusObjPath.str, ancestorsIfacesOnly); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", dbusObjPath.str, + std::array{}); +} + +/** + * @brief API used to load the message and Message Args for the HW Isolation + * Entries + * + * @param[in] ec - error code from the dbus call. + * @param[in] prettyName - The prettyname fetched from the dbus object. + * @param[in] asyncResp - The redfish response to return. + * @param[in] path - D-bus object path to find the pretty name + * @param[in] entryJsonIdx - The json entry index to add isolated hardware + * details in the appropriate entry json object. + * @param[in] guardType - guardType value to be used in the message + * + * @return The redfish response with "Message" property of + * Hw Isolation Entry schema if success else return the error + */ +static void loadHwIsolationMessage( + const boost::system::error_code& ec, const std::string& prettyName, + const std::shared_ptr& asyncResp, + const std::string& path, const size_t entryJsonIdx, + const std::string& guardType) +{ + // Determine base path based on entryJsonIdx + nlohmann::json::json_pointer msgPropPath; + nlohmann::json::json_pointer msgArgPropPath; + if (entryJsonIdx > 0) + { + msgPropPath = nlohmann::json::json_pointer( + "/Members/" + std::to_string(entryJsonIdx - 1) + "/Message"); + msgArgPropPath = nlohmann::json::json_pointer( + "/Members/" + std::to_string(entryJsonIdx - 1) + "/MessageArgs"); + } + else + { + msgPropPath = nlohmann::json::json_pointer("/Message"); + msgArgPropPath = nlohmann::json::json_pointer("/MessageArgs"); + } + std::vector messageArgs; + messageArgs.push_back(guardType); + if (ec || prettyName.empty()) + { + messageArgs.push_back(path); + } + else + { + messageArgs.push_back(prettyName); + } + const redfish::registries::Message* msgReg = + registries::getMessage("OpenBMC.0.6.GuardRecord"); + if (msgReg == nullptr) + { + BMCWEB_LOG_ERROR( + "Failed to get the GuardRecord message registry to add in the condition"); + messages::internalError(asyncResp->res); + return; + } + std::string msg = + redfish::registries::fillMessageArgs(messageArgs, msgReg->message); + if (msg.empty()) + { + messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.jsonValue[msgPropPath] = msg; + asyncResp->res.jsonValue[msgArgPropPath] = messageArgs; +} + +/** + * @brief Read the Pretty Name property using the dbus call and load the message + * property + * + * @param[in,out] asyncResp Async response object + * @param[in] path D-bus object path to find the pretty name + * @param[in] services List of services to exporting the D-bus object path + * @param[in] entryJsonIdx The json entry index to add isolated hardware + * details in the appropriate entry json object. + * @param[in] guardType The guardtype value as a string. To be used in + * the redfish message property + * + * @return void + */ +inline void updateHwIsolationMessage( + const std::shared_ptr& asyncResp, + const std::string& path, const dbus::utility::MapperServiceMap& services, + const size_t entryJsonIdx, const std::string& guardType) +{ + // Ensure we only got one service back + if (services.size() != 1) + { + BMCWEB_LOG_ERROR("Invalid Service Size {}", services.size()); + for (const auto& service : services) + { + BMCWEB_LOG_ERROR("Invalid Service Name: {}", service.first); + } + if (asyncResp) + { + messages::internalError(asyncResp->res); + } + return; + } + sdbusplus::asio::getProperty( + *crow::connections::systemBus, services[0].first, path, + "xyz.openbmc_project.Inventory.Item", "PrettyName", + [asyncResp, path, entryJsonIdx, + guardType](const boost::system::error_code& ec, + const std::string& prettyName) { + loadHwIsolationMessage(ec, prettyName, asyncResp, path, + entryJsonIdx, guardType); + }); +} + +/** + * @brief API used to get "PrettyName" by using the given dbus object path + * and fill into "Message" property of LogEntry schema. + * + * @param[in] asyncResp . The redfish response to return. + * @param[in] dbusObjPath The DBus object path which represents redfishUri. + * @param[in] entryJsonIdx The json entry index to add isolated hardware + * details in the appropriate entry json object. + * @param[in] guardType The guardtype value as a string. To be used in + * the redfish message property + * @return void + */ + +inline void getPrettyNameByDbusObjPath( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& dbusObjPath, + const size_t entryJsonIdx, const std::string& guardType) +{ + constexpr std::array interface = { + "xyz.openbmc_project.Inventory.Item"}; + dbus::utility::getDbusObject( + dbusObjPath.str, interface, + [asyncResp, dbusObjPath, entryJsonIdx, + guardType](const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& objType) mutable { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the dbus name of isolated hareware: {}", + ec.value(), ec.message(), dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the xyz.openbmc_project.Inventory.Item interface to get the PrettyName"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved dbus name is empty for the given dbus object: {}", + dbusObjPath.str); + messages::internalError(asyncResp->res); + return; + } + + updateHwIsolationMessage(asyncResp, dbusObjPath.str, objType, + entryJsonIdx, guardType); + }); +} + +/** + * @brief API used to fill the isolated hardware details into LogEntry schema + * by using the given isolated dbus object which is present in + * xyz.openbmc_project.Association.Definitions::Associations of the + * HardwareIsolation dbus entry object. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] dbusObjPath - The DBus object path which represents redfishUri. + * @param[in] entryJsonIdx - The json entry index to add isolated hardware + * details in the appropriate entry json object. + * + * @return The redfish response with appropriate redfish properties of the + * isolated hardware details into LogEntry schema if success else + * nothing in redfish response. + */ +inline void fillIsolatedHwDetailsByObjPath( + const std::shared_ptr& asyncResp, + const sdbusplus::message::object_path& dbusObjPath, + const size_t entryJsonIdx) +{ + // Fill Redfish uri of isolated hardware into "OriginOfCondition" + if (dbusObjPath.filename().find("unit") != std::string::npos) + { + // If Isolated Hardware object name contain "unit" then that unit + // is not modelled in inventory and redfish so the "OriginOfCondition" + // should filled with it's parent (aka FRU of unit) path. + getRedfishUriByDbusObjPath(asyncResp, dbusObjPath.parent_path(), + entryJsonIdx); + } + else + { + getRedfishUriByDbusObjPath(asyncResp, dbusObjPath, entryJsonIdx); + } + + // Fill PrettyName of isolated hardware into "Message" + getPrettyNameByDbusObjPath(asyncResp, dbusObjPath, entryJsonIdx, guardType); +} + +/** + * @brief API used to fill isolated hardware details into LogEntry schema + * by using the given isolated dbus object. + * + * @param[in] asyncResp - The redfish response to return. + * @param[in] entryJsonIdx - The json entry index to add isolated hardware + * details. If passing less than or equal 0 then, + * it will assume the given asyncResp jsonValue as + * a single entry json object. If passing greater + * than 0 then, it will assume the given asyncResp + * jsonValue contains "Members" to fill in the + * appropriate entry json object. + * @param[in] dbusObjIt - The DBus object which contains isolated hardware + details. + * + * @return The redfish response with appropriate redfish properties of the + * isolated hardware details into LogEntry schema if success else + * failure response. + */ +inline void fillSystemHardwareIsolationLogEntry( + const std::shared_ptr& asyncResp, + const size_t entryJsonIdx, GetManagedObjectsType::const_iterator& dbusObjIt) +{ + nlohmann::json& entryJson = + (entryJsonIdx > 0 + ? asyncResp->res.jsonValue["Members"][entryJsonIdx - 1] + : asyncResp->res.jsonValue); + + std::string guardType; + // We need the severity details before getting the associations + // to fill the message details. + + for (const auto& interface : dbusObjIt->second) + { + if (interface.first == "xyz.openbmc_project.HardwareIsolation.Entry") + { + for (const auto& property : interface.second) + { + if (property.first == "Severity") + { + const std::string* severity = + std::get_if(&property.second); + if (severity == nullptr) + { + BMCWEB_LOG_ERROR( + "Failed to get the Severity from object: {}", + dbusObjIt->first.str); + messages::internalError(asyncResp->res); + break; + } + + const std::string& severityString = *severity; + guardType = + severityString.substr(severityString.rfind('.') + 1); + entryJson["Severity"] = guardType; + // Manual and Spare guard type map to severity "OK" + if (severityString == + "xyz.openbmc_project.HardwareIsolation.Entry.Type.Manual" || + severityString == + "xyz.openbmc_project.HardwareIsolation.Entry.Type.Spare") + { + entryJson["Severity"] = "OK"; + } + } + } + break; + } + } + + for (const auto& interface : dbusObjIt->second) + { + if (interface.first == "xyz.openbmc_project.Time.EpochTime") + { + for (const auto& property : interface.second) + { + if (property.first == "Elapsed") + { + const uint64_t* elapsdTime = + std::get_if(&property.second); + if (elapsdTime == nullptr) + { + BMCWEB_LOG_ERROR( + "Failed to get the Elapsed time from object: {}", + dbusObjIt->first.str); + messages::internalError(asyncResp->res); + break; + } + entryJson["Created"] = + redfish::time_utils::getDateTimeUint((*elapsdTime)); + } + } + } + else if (interface.first == + "xyz.openbmc_project.Association.Definitions") + { + for (const auto& property : interface.second) + { + if (property.first == "Associations") + { + const AssociationsValType* associations = + std::get_if(&property.second); + if (associations == nullptr) + { + BMCWEB_LOG_ERROR( + "Failed to get the Associations from object: {}", + dbusObjIt->first.str); + messages::internalError(asyncResp->res); + break; + } + for (const auto& assoc : *associations) + { + if (std::get<0>(assoc) == "isolated_hw") + { + fillIsolatedHwDetailsByObjPath( + asyncResp, + sdbusplus::message::object_path( + std::get<2>(assoc)), + entryJsonIdx, guardType); + } + else if (std::get<0>(assoc) == "isolated_hw_errorlog") + { + sdbusplus::message::object_path errPath = + std::get<2>(assoc); + + std::string entryID = errPath.filename(); + + auto updateAdditionalDataURI = [asyncResp, + entryJsonIdx, + entryID]( + bool hidden) { + nlohmann::json& entryJsonToupdateURI = + (entryJsonIdx > 0 + ? asyncResp->res + .jsonValue["Members"] + [entryJsonIdx - 1] + : asyncResp->res.jsonValue); + std::string logPath = "EventLog"; + + if (hidden) + { + logPath = "CELog"; + } + entryJsonToupdateURI["AdditionalDataURI"] = + boost::urls::format( + "/redfish/v1/Systems/system/LogServices/{}/Entries/{}/attachment", + logPath, entryID); + }; + error_log_utils::getHiddenPropertyValue( + asyncResp, entryID, updateAdditionalDataURI); + } + } + } + } + } + } + + entryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + entryJson["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/system/LogServices/HardwareIsolation/Entries/{}", + dbusObjIt->first.filename()); + entryJson["Id"] = dbusObjIt->first.filename(); + entryJson["MessageId"] = "OpenBMC.0.6.GuardRecord"; + entryJson["Name"] = "Hardware Isolation Entry"; + entryJson["EntryType"] = "Event"; +} + +/** + * @brief API Used to add the supported HardwareIsolation LogEntry Entries id + * + * @param[in] req - The HardwareIsolation redfish request (unused now). + * @param[in] asyncResp - The redfish response to return. + * + * @return The redfish response in the given buffer. + * + * @note This function will return the available entries dbus object which are + * created by HardwareIsolation manager. + */ +inline void getSystemHardwareIsolationLogEntryCollection( + const crow::Request& /* req */, + const std::shared_ptr& asyncResp) +{ + auto getManagedObjectsHandler = [asyncResp]( + const boost::system::error_code& ec, + const GetManagedObjectsType& mgtObjs) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation managed objects", + ec.value(), ec.message()); + messages::internalError(asyncResp->res); + return; + } + + nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"]; + entriesArray = nlohmann::json::array(); + + for (auto dbusObjIt = mgtObjs.begin(); dbusObjIt != mgtObjs.end(); + dbusObjIt++) + { + if (dbusObjIt->second.find( + "xyz.openbmc_project.HardwareIsolation.Entry") == + dbusObjIt->second.end()) + { + // The retrieved object is not hardware isolation entry + continue; + } + entriesArray.push_back(nlohmann::json::object()); + + fillSystemHardwareIsolationLogEntry(asyncResp, entriesArray.size(), + dbusObjIt); + } + + asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); + + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/Systems/system/LogServices/HardwareIsolation/Entries"; + asyncResp->res.jsonValue["Name"] = "Hardware Isolation Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of System Hardware Isolation Entries"; + }; + + // Get the DBus name of HardwareIsolation service + crow::connections::systemBus->async_method_call( + [asyncResp, getManagedObjectsHandler]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& objType) { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation dbus name", + ec.value(), ec.message()); + messages::internalError(asyncResp->res); + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the HardwareIsolation service"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved HardwareIsolation dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Fill the Redfish LogEntry schema for the retrieved + // HardwareIsolation entries + crow::connections::systemBus->async_method_call( + getManagedObjectsHandler, objType[0].first, + "/xyz/openbmc_project/hardware_isolation", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/hardware_isolation", + std::array{ + "xyz.openbmc_project.HardwareIsolation.Create"}); +} + +/** + * @brief API Used to fill LogEntry schema by using the HardwareIsolation dbus + * entry object which will get by using the given entry id in redfish + * uri. + * + * @param[in] req - The HardwareIsolation redfish request (unused now). + * @param[in] asyncResp - The redfish response to return. + * @param[in] entryId - The entry id of HardwareIsolation entries to retrieve + * the corresponding isolated hardware details. + * + * @return The redfish response in the given buffer with LogEntry schema + * members if success else will error. + */ +inline void getSystemHardwareIsolationLogEntryById( + const crow::Request& /* req */, + const std::shared_ptr& asyncResp, + const std::string& entryId) +{ + sdbusplus::message::object_path entryObjPath(std::format( + "/xyz/openbmc_project/hardware_isolation/entry/{}", entryId)); + + auto getManagedObjectsRespHandler = [asyncResp, entryObjPath]( + const boost::system::error_code& ec, + const GetManagedObjectsType& + mgtObjs) { + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{}:{}] when tried to get the HardwareIsolation managed objects", + ec.value(), ec.message()); + messages::internalError(asyncResp->res); + return; + } + + bool entryIsPresent = false; + for (auto dbusObjIt = mgtObjs.begin(); dbusObjIt != mgtObjs.end(); + dbusObjIt++) + { + if (dbusObjIt->first == entryObjPath) + { + entryIsPresent = true; + fillSystemHardwareIsolationLogEntry(asyncResp, 0, dbusObjIt); + break; + } + } + + if (!entryIsPresent) + { + messages::resourceNotFound(asyncResp->res, "Entry", + entryObjPath.filename()); + return; + } + }; + + auto getObjectRespHandler = [asyncResp, entryId, entryObjPath, + getManagedObjectsRespHandler]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& + objType) { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation dbus name the given object path: {}", + ec.value(), ec.message(), entryObjPath.str); + + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, "Entry", entryId); + } + else + { + messages::internalError(asyncResp->res); + } + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the HardwareIsolation service"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved HardwareIsolation dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Fill the Redfish LogEntry schema for the identified entry dbus object + crow::connections::systemBus->async_method_call( + getManagedObjectsRespHandler, objType[0].first, + "/xyz/openbmc_project/hardware_isolation", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }; + + // Make sure the given entry id is present in hardware isolation + // dbus entries and get the DBus name of that entry to fill LogEntry + crow::connections::systemBus->async_method_call( + getObjectRespHandler, "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", entryObjPath.str, + hwIsolationEntryIfaces); +} + +/** + * @brief API Used to deisolate the given HardwareIsolation entry. + * + * @param[in] req - The HardwareIsolation redfish request (unused now). + * @param[in] asyncResp - The redfish response to return. + * @param[in] entryId - The entry id of HardwareIsolation entries to deisolate. + * + * @return The redfish response in the given buffer. + */ +inline void deleteSystemHardwareIsolationLogEntryById( + const crow::Request& /* req */, + const std::shared_ptr& asyncResp, + const std::string& entryId) +{ + sdbusplus::message::object_path entryObjPath( + std::string("/xyz/openbmc_project/hardware_isolation/entry") + "/" + + entryId); + + // Make sure the given entry id is present in hardware isolation + // entries and get the DBus name of that entry + crow::connections::systemBus->async_method_call( + [asyncResp, entryId, + entryObjPath](const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& objType) { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation dbus name the given object path: ", + ec.value(), ec.message(), entryObjPath.str); + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, "Entry", + entryId); + } + else + { + messages::internalError(asyncResp->res); + } + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the HardwareIsolation service"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved HardwareIsolation dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Delete the respective dbus entry object + crow::connections::systemBus->async_method_call( + [asyncResp, + entryObjPath](const boost::system::error_code& ec1, + const sdbusplus::message::message& msg) { + if (!ec1) + { + messages::success(asyncResp->res); + return; + } + + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to delete the given object path: ", + ec1.value(), ec1.message(), entryObjPath.str); + + const sd_bus_error* dbusError = msg.get_error(); + + if (dbusError == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + + BMCWEB_LOG_ERROR("DBus ErrorName: {} ErrorMsg: {}", + dbusError->name, dbusError->message); + + if (std::string_view( + "xyz.openbmc_project.Common.Error.NotAllowed") == + dbusError->name) + { + messages::chassisPowerStateOffRequired(asyncResp->res, + "chassis"); + } + else if ( + std::string_view( + "xyz.openbmc_project.Common.Error.InsufficientPermission") == + dbusError->name) + { + messages::resourceCannotBeDeleted(asyncResp->res); + } + else + { + BMCWEB_LOG_ERROR( + "DBus Error is unsupported so returning as Internal Error"); + messages::internalError(asyncResp->res); + } + return; + }, + objType[0].first, entryObjPath.str, + "xyz.openbmc_project.Object.Delete", "Delete"); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", entryObjPath.str, + hwIsolationEntryIfaces); +} +/** + * @brief API Used to deisolate the all HardwareIsolation entries. + * + * @param[in] req - The HardwareIsolation redfish request (unused now). + * @param[in] asyncResp - The redfish response to return. + * + * @return The redfish response in the given buffer. + */ +inline void postSystemHardwareIsolationLogServiceClearLog( + const crow::Request& /* req */, + const std::shared_ptr& asyncResp) +{ + // Get the DBus name of HardwareIsolation service + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& objType) { + if (ec || objType.empty()) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to get the HardwareIsolation dbus name", + ec.value(), ec.message()); + messages::internalError(asyncResp->res); + return; + } + + if (objType.size() > 1) + { + BMCWEB_LOG_ERROR( + "More than one dbus service implemented the HardwareIsolation service"); + messages::internalError(asyncResp->res); + return; + } + + if (objType[0].first.empty()) + { + BMCWEB_LOG_ERROR( + "The retrieved HardwareIsolation dbus name is empty"); + messages::internalError(asyncResp->res); + return; + } + + // Delete all HardwareIsolation entries + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec1) { + if (ec1) + { + BMCWEB_LOG_ERROR( + "DBUS response error [{} : {}] when tried to delete all HardwareIsolation entries", + ec1.value(), ec1.message()); + messages::internalError(asyncResp->res); + return; + } + messages::success(asyncResp->res); + }, + objType[0].first, "/xyz/openbmc_project/hardware_isolation", + "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/hardware_isolation", + std::array{"xyz.openbmc_project.Collection.DeleteAll"}); +} +/** + * @brief API used to route the handler for HardwareIsolation Redfish + * LogServices URI + * + * @param[in] app - Crow app on which Redfish will initialize + * + * @return The appropriate redfish response for the given redfish request. + */ +inline void requestRoutesSystemHardwareIsolationLogService(App& app) +{ + BMCWEB_ROUTE(app, + "/redfish/v1/Systems/system/LogServices/HardwareIsolation/") + .privileges(redfish::privileges::getLogService) + .methods(boost::beast::http::verb::get)( + getSystemHardwareIsolationLogService); + + BMCWEB_ROUTE( + app, + "/redfish/v1/Systems/system/LogServices/HardwareIsolation/Entries/") + .privileges(redfish::privileges::getLogEntryCollection) + .methods(boost::beast::http::verb::get)( + getSystemHardwareIsolationLogEntryCollection); + + BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/" + "HardwareIsolation/Entries//") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)( + getSystemHardwareIsolationLogEntryById); + + BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/" + "HardwareIsolation/Entries//") + .privileges(redfish::privileges::deleteLogEntry) + .methods(boost::beast::http::verb::delete_)( + deleteSystemHardwareIsolationLogEntryById); + + BMCWEB_ROUTE(app, + "/redfish/v1/Systems/system/LogServices/HardwareIsolation/" + "Actions/LogService.ClearLog/") + .privileges(redfish::privileges::postLogService) + .methods(boost::beast::http::verb::post)( + postSystemHardwareIsolationLogServiceClearLog); +} + +} // namespace redfish