summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason M. Bills <jason.m.bills@intel.com>2022-03-24 19:15:20 +0300
committerJason M. Bills <jason.m.bills@intel.com>2022-03-24 21:47:43 +0300
commit45f1c88d8936cb0f8bb3ff0740ae00f4e13c681e (patch)
tree05795cfd7ff03d920c877f836f97e39518ec46ec
downloadopenbmc-bmcweb-patches.tar.xz
bmcweb patches on current bump to f7725d7bmcweb-patches
Signed-off-by: Jason M. Bills <jason.m.bills@intel.com>
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch691
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch74
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch60
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch158
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch67
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch59
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch75
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch95
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch44
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch78
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch74
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch120
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch85
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch390
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch29
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-to-ProcessorSummary.patch108
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0026-Revert-Delete-the-copy-constructor-on-the-Request.patch33
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0028-Add-Redfish-schema-files-for-OEM-PECI.patch216
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch874
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch148
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch53
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch117
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch46
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch963
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0007-Add-BiosAttributeRegistry-node-under-Registries.patch76
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket9
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch163
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch448
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch471
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch677
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch326
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch133
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch85
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch267
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch141
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch66
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0013-event-service-fix-added-Context-field-to-response.patch33
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README37
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch194
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch109
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch220
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch140
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch63
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch26
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-ref-use-url_view-for-telemetry-uris.patch193
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Fix-Trigger-GET-functionality.patch127
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-POST-on-TriggersCollection.patch848
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch559
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Add-PUT-and-PATCH-for-MetricReportDefinition.patch948
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-Add-Links-Triggers-to-MetricReportDefinition.patch107
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README24
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch61
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch215
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch28
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch175
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch38
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch35
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch369
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Return-404-for-POST-on-Proxy-InsertMedia.patch380
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend96
60 files changed, 12544 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
new file mode 100644
index 000000000..168c1b21c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
@@ -0,0 +1,691 @@
+From 4baa1fbf5d2deedfc4144e3fd36c8547c3902c98 Mon Sep 17 00:00:00 2001
+From: Vikram Bodireddy <vikram.bodireddy@intel.com>
+Date: Fri, 3 Dec 2021 05:25:02 +0530
+Subject: [PATCH] Firmware update configuration changes
+
+This commit will provide user to PATCH the below firmware update
+attributes before uploding the firmware image.
+
+1. This will have PATCH support for 'HttpPushUriTargets' and
+'HttpPushUriTargetsBusy' attributes. These attributes enables
+'HttpPushUri' to distinguish between the firmware update targets.
+
+2. ApplyOptions are used to specify firmware update specific options
+such as ClearConfig which is used while activating the updated
+firmware. This setting is maintained in a local static variable
+when set using PATCH method. Its used in activate image as input
+parameter. This attribute is added as Oem as the default
+UpdateService interface doesn't specify any relevant or appropriate
+attribute for this.
+
+Tested:
+ - GET on "/redfish/v1/UpdateService", got below response
+.........
+ "HttpPushUriTargets": [],
+ "HttpPushUriTargetsBusy": false
+........
+
+ - PATCH on "/redfish/v1/UpdateService" and works fine.
+{
+ "HttpPushUriTargets": ["bmc_recovery"],
+ "HttpPushUriTargetsBusy": true
+}
+
+ - Did Firmware update and verified end to end functionality
+ for both bmc active and backup images.
+
+ - Tested setting ClearConfig to true or false using PATCH
+ method.
+
+ - Successfully ran redfish validater with no new errors.
+
+Signed-off-by: Vikram Bodireddy <vikram.bodireddy@intel.com>
+---
+ redfish-core/lib/update_service.hpp | 343 ++++++++++++++++--
+ static/redfish/v1/$metadata/index.xml | 3 +
+ .../JsonSchemas/OemUpdateService/index.json | 69 ++++
+ .../redfish/v1/schema/OemUpdateService_v1.xml | 40 ++
+ 4 files changed, 426 insertions(+), 29 deletions(-)
+ create mode 100644 static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+ create mode 100644 static/redfish/v1/schema/OemUpdateService_v1.xml
+
+diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
+index 6c61e40..3161427 100644
+--- a/redfish-core/lib/update_service.hpp
++++ b/redfish-core/lib/update_service.hpp
+@@ -26,7 +26,9 @@
+
+ namespace redfish
+ {
+-
++// params for multiple firmware targets
++std::vector<std::string> httpPushUriTargets;
++bool httpPushUriTargetBusy = false;
+ // Match signals added on software path
+ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
+ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
+@@ -34,6 +36,17 @@ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
+ static bool fwUpdateInProgress = false;
+ // Timer for software available
+ static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
++static constexpr const char* versionIntf =
++ "xyz.openbmc_project.Software.Version";
++static constexpr const char* activationIntf =
++ "xyz.openbmc_project.Software.Activation";
++static constexpr const char* reqActivationPropName = "RequestedActivation";
++static constexpr const char* reqActivationsActive =
++ "xyz.openbmc_project.Software.Activation.RequestedActivations.Active";
++static constexpr const char* reqActivationsStandBySpare =
++ "xyz.openbmc_project.Software.Activation.RequestedActivations.StandbySpare";
++static constexpr const char* activationsStandBySpare =
++ "xyz.openbmc_project.Software.Activation.Activations.StandbySpare";
+
+ inline static void cleanUp()
+ {
+@@ -42,27 +55,120 @@ inline static void cleanUp()
+ fwUpdateErrorMatcher = nullptr;
+ }
+ inline static void activateImage(const std::string& objPath,
+- const std::string& service)
++ const std::string& service,
++ const std::vector<std::string>& imgUriTargets)
+ {
+ BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
++ // If targets is empty, it will apply to the active.
++ if (imgUriTargets.size() == 0)
++ {
++ crow::connections::systemBus->async_method_call(
++ [](const boost::system::error_code errorCode) {
++ if (errorCode)
++ {
++ BMCWEB_LOG_DEBUG
++ << "RequestedActivation failed: error_code = "
++ << errorCode;
++ BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
++ }
++ },
++ service, objPath, "org.freedesktop.DBus.Properties", "Set",
++ activationIntf, reqActivationPropName,
++ std::variant<std::string>(reqActivationsActive));
++ return;
++ }
++
++ // TODO: Now we support only one target becuase software-manager
++ // code support one activation per object. It will be enhanced
++ // to multiple targets for single image in future. For now,
++ // consider first target alone.
+ crow::connections::systemBus->async_method_call(
+- [](const boost::system::error_code errorCode) {
+- if (errorCode)
++ [objPath, service, imgTarget{imgUriTargets[0]}](
++ const boost::system::error_code ec,
++ const crow::openbmc_mapper::GetSubTreeType& subtree) {
++ if (ec || !subtree.size())
+ {
+- BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
+- BMCWEB_LOG_DEBUG << "error msg = " << errorCode.message();
++ return;
++ }
++
++ for (const auto& [invObjPath, invDict] : subtree)
++ {
++ std::size_t idPos = invObjPath.rfind("/");
++ if ((idPos == std::string::npos) ||
++ ((idPos + 1) >= invObjPath.size()))
++ {
++ BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
++ return;
++ }
++ std::string swId = invObjPath.substr(idPos + 1);
++
++ if (swId != imgTarget)
++ {
++ continue;
++ }
++
++ if (invDict.size() < 1)
++ {
++ continue;
++ }
++ BMCWEB_LOG_DEBUG << "Image target matched with object "
++ << invObjPath;
++ crow::connections::systemBus->async_method_call(
++ [objPath,
++ service](const boost::system::error_code error_code,
++ const std::variant<std::string> value) {
++ if (error_code)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Error in querying activation value";
++ // not all fwtypes are updateable,
++ // this is ok
++ return;
++ }
++ std::string activationValue =
++ std::get<std::string>(value);
++ BMCWEB_LOG_DEBUG << "Activation Value: "
++ << activationValue;
++ std::string reqActivation = reqActivationsActive;
++ if (activationValue == activationsStandBySpare)
++ {
++ reqActivation = reqActivationsStandBySpare;
++ }
++ BMCWEB_LOG_DEBUG
++ << "Setting RequestedActivation value as "
++ << reqActivation << " for " << service << " "
++ << objPath;
++ crow::connections::systemBus->async_method_call(
++ [](const boost::system::error_code error_code) {
++ if (error_code)
++ {
++ BMCWEB_LOG_DEBUG
++ << "RequestedActivation failed: ec = "
++ << error_code;
++ }
++ return;
++ },
++ service, objPath, "org.freedesktop.DBus.Properties",
++ "Set", activationIntf, reqActivationPropName,
++ std::variant<std::string>(reqActivation));
++ },
++ invDict[0].first,
++ "/xyz/openbmc_project/software/" + imgTarget,
++ "org.freedesktop.DBus.Properties", "Get", activationIntf,
++ "Activation");
+ }
+ },
+- service, objPath, "org.freedesktop.DBus.Properties", "Set",
+- "xyz.openbmc_project.Software.Activation", "RequestedActivation",
+- dbus::utility::DbusVariantType(
+- "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"));
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
++ static_cast<int32_t>(0), std::array<const char*, 1>{versionIntf});
+ }
+
+ // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
+ // then no asyncResp updates will occur
+ static void
+ softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::vector<std::string> imgUriTargets,
+ sdbusplus::message::message& m,
+ task::Payload&& payload)
+ {
+@@ -75,23 +181,26 @@ static void
+
+ m.read(objPath, interfacesProperties);
+
+- BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
++ BMCWEB_LOG_DEBUG << "Software Interface Added. obj path = " << objPath.str;
+ for (auto& interface : interfacesProperties)
+ {
+ BMCWEB_LOG_DEBUG << "interface = " << interface.first;
+
+- if (interface.first == "xyz.openbmc_project.Software.Activation")
++ if (interface.first == activationIntf)
+ {
+ // Retrieve service and activate
+ crow::connections::systemBus->async_method_call(
+- [objPath, asyncResp, payload(std::move(payload))](
++ [objPath, asyncResp, imgTargets{imgUriTargets},
++ payload(std::move(payload))](
+ const boost::system::error_code errorCode,
+ const std::vector<
+ std::pair<std::string, std::vector<std::string>>>&
+ objInfo) mutable {
+ if (errorCode)
+ {
+- BMCWEB_LOG_DEBUG << "error_code = " << errorCode;
++ BMCWEB_LOG_DEBUG
++ << "GetSoftwareObject path failed: error_code = "
++ << errorCode;
+ BMCWEB_LOG_DEBUG << "error msg = "
+ << errorCode.message();
+ if (asyncResp)
+@@ -118,7 +227,7 @@ static void
+ // is added
+ fwAvailableTimer = nullptr;
+
+- activateImage(objPath.str, objInfo[0].first);
++ activateImage(objPath.str, objInfo[0].first, imgTargets);
+ if (asyncResp)
+ {
+ std::shared_ptr<task::TaskData> task =
+@@ -249,8 +358,7 @@ static void
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
+- std::array<const char*, 1>{
+- "xyz.openbmc_project.Software.Activation"});
++ std::array<const char*, 1>{activationIntf});
+ }
+ }
+ }
+@@ -260,7 +368,7 @@ static void
+ static void monitorForSoftwareAvailable(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const crow::Request& req, const std::string& url,
+- int timeoutTimeSeconds = 10)
++ const std::vector<std::string>& imgUriTargets, int timeoutTimeSeconds = 10)
+ {
+ // Only allow one FW update at a time
+ if (fwUpdateInProgress)
+@@ -300,10 +408,10 @@ static void monitorForSoftwareAvailable(
+ }
+ });
+ task::Payload payload(req);
+- auto callback = [asyncResp,
++ auto callback = [asyncResp, imgTargets{imgUriTargets},
+ payload](sdbusplus::message::message& m) mutable {
+ BMCWEB_LOG_DEBUG << "Match fired";
+- softwareInterfaceAdded(asyncResp, m, std::move(payload));
++ softwareInterfaceAdded(asyncResp, imgTargets, m, std::move(payload));
+ };
+
+ fwUpdateInProgress = true;
+@@ -485,12 +593,15 @@ inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
+ std::string fwFile = imageURI.substr(separator + 1);
+ BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
+
++ // We will pass empty targets and its handled in activation.
++ std::vector<std::string> httpUriTargets;
++
+ // Setup callback for when new software detected
+ // Give TFTP 10 minutes to complete
+ monitorForSoftwareAvailable(
+ asyncResp, req,
+ "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
+- 600);
++ httpUriTargets, 600);
+
+ // TFTP can take up to 10 minutes depending on image size and
+ // connection speed. Return to caller as soon as the TFTP operation
+@@ -539,6 +650,9 @@ inline void requestRoutesUpdateService(App& app)
+ asyncResp->res.jsonValue["Name"] = "Update Service";
+ asyncResp->res.jsonValue["HttpPushUri"] =
+ "/redfish/v1/UpdateService";
++ asyncResp->res.jsonValue["HttpPushUriTargets"] = httpPushUriTargets;
++ asyncResp->res.jsonValue["HttpPushUriTargetsBusy"] =
++ httpPushUriTargetBusy;
+ // UpdateService cannot be disabled
+ asyncResp->res.jsonValue["ServiceEnabled"] = true;
+ asyncResp->res.jsonValue["FirmwareInventory"] = {
+@@ -590,6 +704,34 @@ inline void requestRoutesUpdateService(App& app)
+ "OnReset";
+ }
+ });
++
++ // Get the ApplyOptions value
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const std::variant<bool> applyOption) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const bool* b = std::get_if<bool>(&applyOption);
++
++ if (b)
++ {
++ asyncResp->res
++ .jsonValue["Oem"]["ApplyOptions"]["@odata.type"] =
++ "#OemUpdateService.ApplyOptions";
++ asyncResp->res
++ .jsonValue["Oem"]["ApplyOptions"]["ClearConfig"] =
++ *b;
++ }
++ },
++ "xyz.openbmc_project.Software.BMC.Updater",
++ "/xyz/openbmc_project/software",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Software.ApplyOptions", "ClearConfig");
+ });
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
+ .privileges(redfish::privileges::patchUpdateService)
+@@ -600,12 +742,60 @@ inline void requestRoutesUpdateService(App& app)
+ BMCWEB_LOG_DEBUG << "doPatch...";
+
+ std::optional<nlohmann::json> pushUriOptions;
+- if (!json_util::readJsonPatch(req, asyncResp->res,
+- "HttpPushUriOptions", pushUriOptions))
++ std::optional<std::vector<std::string>> imgTargets;
++ std::optional<bool> imgTargetBusy;
++ std::optional<nlohmann::json> oemProps;
++ if (!json_util::readJsonPatch(
++ req, asyncResp->res, "HttpPushUriOptions", pushUriOptions,
++ "HttpPushUriTargets", imgTargets, "HttpPushUriTargetsBusy",
++ imgTargetBusy, "Oem", oemProps))
+ {
++ BMCWEB_LOG_DEBUG
++ << "UpdateService doPatch: Invalid request body";
+ return;
+ }
+
++ if (oemProps)
++ {
++ std::optional<nlohmann::json> applyOptions;
++
++ if (!json_util::readJson(*oemProps, asyncResp->res,
++ "ApplyOptions", applyOptions))
++ {
++ return;
++ }
++
++ if (applyOptions)
++ {
++ std::optional<bool> clearConfig;
++ if (!json_util::readJson(*applyOptions, asyncResp->res,
++ "ClearConfig", clearConfig))
++ {
++ return;
++ }
++
++ if (clearConfig)
++ {
++ // Set the requested image apply time value
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "D-Bus responses error: " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ messages::success(asyncResp->res);
++ },
++ "xyz.openbmc_project.Software.BMC.Updater",
++ "/xyz/openbmc_project/software",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Software.ApplyOptions",
++ "ClearConfig", std::variant<bool>{*clearConfig});
++ }
++ }
++ }
+ if (pushUriOptions)
+ {
+ std::optional<nlohmann::json> pushUriApplyTime;
+@@ -668,6 +858,102 @@ inline void requestRoutesUpdateService(App& app)
+ }
+ }
+ }
++ if (imgTargetBusy)
++ {
++ if ((httpPushUriTargetBusy) && (*imgTargetBusy))
++ {
++ BMCWEB_LOG_DEBUG
++ << "Other client has reserved the HttpPushUriTargets "
++ "property for firmware updates.";
++ messages::resourceInUse(asyncResp->res);
++ return;
++ }
++
++ if (imgTargets)
++ {
++ if (!(*imgTargetBusy))
++ {
++ BMCWEB_LOG_DEBUG
++ << "UpdateService doPatch: httpPushUriTargetBusy "
++ "should be "
++ "true before setting httpPushUriTargets";
++ messages::invalidObject(asyncResp->res,
++ crow::utility::urlFromPieces(
++ "HttpPushUriTargetsBusy"));
++ return;
++ }
++ if ((*imgTargets).size() != 0)
++ {
++ // TODO: Now we support max one target becuase
++ // software-manager code support one activation per
++ // object. It will be enhanced to multiple targets for
++ // single image in future. For now, consider first
++ // target alone.
++ if ((*imgTargets).size() != 1)
++ {
++ messages::invalidObject(
++ asyncResp->res, crow::utility::urlFromPieces(
++ "HttpPushUriTargets"));
++ return;
++ }
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, uriTargets{*imgTargets},
++ targetBusy{*imgTargetBusy}](
++ const boost::system::error_code ec,
++ const std::vector<std::string> swInvPaths) {
++ if (ec)
++ {
++ return;
++ }
++
++ bool swInvObjFound = false;
++ for (const std::string& path : swInvPaths)
++ {
++ std::size_t idPos = path.rfind("/");
++ if ((idPos == std::string::npos) ||
++ ((idPos + 1) >= path.size()))
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_DEBUG
++ << "Can't parse firmware ID!!";
++ return;
++ }
++ std::string swId = path.substr(idPos + 1);
++
++ if (swId == uriTargets[0])
++ {
++ swInvObjFound = true;
++ break;
++ }
++ }
++ if (!swInvObjFound)
++ {
++ messages::invalidObject(
++ asyncResp->res,
++ crow::utility::urlFromPieces(
++ "HttpPushUriTargets"));
++ return;
++ }
++ httpPushUriTargetBusy = targetBusy;
++ httpPushUriTargets = uriTargets;
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper",
++ "GetSubTreePaths", "/", static_cast<int32_t>(0),
++ std::array<const char*, 1>{versionIntf});
++ }
++ else
++ {
++ httpPushUriTargetBusy = *imgTargetBusy;
++ httpPushUriTargets = *imgTargets;
++ }
++ }
++ else
++ {
++ httpPushUriTargetBusy = *imgTargetBusy;
++ }
++ }
+ });
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
+ .privileges(redfish::privileges::postUpdateService)
+@@ -678,7 +964,8 @@ inline void requestRoutesUpdateService(App& app)
+
+ // Setup callback for when new software detected
+ monitorForSoftwareAvailable(asyncResp, req,
+- "/redfish/v1/UpdateService");
++ "/redfish/v1/UpdateService",
++ httpPushUriTargets);
+
+ std::string filepath("/tmp/images/" +
+ boost::uuids::to_string(
+@@ -754,8 +1041,7 @@ inline void requestRoutesSoftwareInventoryCollection(App& app)
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+ "/xyz/openbmc_project/software", static_cast<int32_t>(0),
+- std::array<const char*, 1>{
+- "xyz.openbmc_project.Software.Version"});
++ std::array<const char*, 1>{versionIntf});
+ });
+ }
+ /* Fill related item links (i.e. bmc, bios) in for inventory */
+@@ -923,7 +1209,7 @@ inline void requestRoutesSoftwareInventory(App& app)
+ },
+ obj.second[0].first, obj.first,
+ "org.freedesktop.DBus.Properties", "GetAll",
+- "xyz.openbmc_project.Software.Version");
++ versionIntf);
+ }
+ if (!found)
+ {
+@@ -948,8 +1234,7 @@ inline void requestRoutesSoftwareInventory(App& app)
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
+ static_cast<int32_t>(0),
+- std::array<const char*, 1>{
+- "xyz.openbmc_project.Software.Version"});
++ std::array<const char*, 1>{versionIntf});
+ });
+ }
+
+diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml
+index 49835a7..1a6767b 100644
+--- a/static/redfish/v1/$metadata/index.xml
++++ b/static/redfish/v1/$metadata/index.xml
+@@ -2575,6 +2575,9 @@
+ <edmx:Reference Uri="/redfish/v1/schema/OemComputerSystem_v1.xml">
+ <edmx:Include Namespace="OemComputerSystem"/>
+ </edmx:Reference>
++ <edmx:Reference Uri="/redfish/v1/schema/OemUpdateService_v1.xml">
++ <edmx:Include Namespace="OemUpdateService"/>
++ </edmx:Reference>
+ <edmx:Reference Uri="/redfish/v1/schema/OemVirtualMedia_v1.xml">
+ <edmx:Include Namespace="OemVirtualMedia"/>
+ <edmx:Include Namespace="OemVirtualMedia.v1_0_0"/>
+diff --git a/static/redfish/v1/JsonSchemas/OemUpdateService/index.json b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+new file mode 100644
+index 0000000..74e39cd
+--- /dev/null
++++ b/static/redfish/v1/JsonSchemas/OemUpdateService/index.json
+@@ -0,0 +1,69 @@
++{
++ "$id": "http://redfish.dmtf.org/schemas/v1/OemUpdateService.json",
++ "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
++ "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright",
++ "definitions": {
++ "ApplyOptions": {
++ "additionalProperties": false,
++ "description": "An indication by boolean value whether to update firmware configuration along with firmware image update.",
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "ClearConfig": {
++ "description": "This indicates whether to update firmware configuration or not.",
++ "longDescription": "The value of this property is used to indicate the firmware configuration update.",
++ "readonly": false,
++ "type": [
++ "boolean",
++ "null"
++ ]
++ }
++ },
++ "type": "object"
++ },
++ "Oem": {
++ "additionalProperties": true,
++ "description": "OemUpdateService Oem properties.",
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "ApplyOptions": {
++ "anyOf": [
++ {
++ "$ref": "#/definitions/ApplyOptions"
++ },
++ {
++ "type": "null"
++ }
++ ]
++ }
++ },
++ "type": "object"
++ }
++ },
++ "title": "#OemUpdateService"
++}
+diff --git a/static/redfish/v1/schema/OemUpdateService_v1.xml b/static/redfish/v1/schema/OemUpdateService_v1.xml
+new file mode 100644
+index 0000000..cbb7aa4
+--- /dev/null
++++ b/static/redfish/v1/schema/OemUpdateService_v1.xml
+@@ -0,0 +1,40 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
++ <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData" />
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/RedfishExtensions_v1.xml">
++ <edmx:Include Namespace="Validation.v1_0_0" Alias="Validation"/>
++ <edmx:Include Namespace="RedfishExtensions.v1_0_0" Alias="Redfish"/>
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/UpdateService_v1.xml">
++ <edmx:Include Namespace="UpdateService"/>
++ <edmx:Include Namespace="UpdateService.v1_4_0"/>
++ </edmx:Reference>
++ <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml">
++ <edmx:Include Namespace="Resource"/>
++ <edmx:Include Namespace="Resource.v1_0_0"/>
++ </edmx:Reference>
++
++ <edmx:DataServices>
++ <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemUpdateService">
++ <ComplexType Name="Oem" BaseType="Resource.OemObject">
++ <Annotation Term="OData.AdditionalProperties" Bool="true" />
++ <Annotation Term="OData.Description" String="OemUpdateService Oem properties." />
++ <Annotation Term="OData.AutoExpand"/>
++ <Property Name="ApplyOptions" Type="OemUpdateService.ApplyOptions"/>
++ </ComplexType>
++
++ <ComplexType Name="ApplyOptions" BaseType="Resource.OemObject">
++ <Annotation Term="OData.AdditionalProperties" Bool="false" />
++ <Annotation Term="OData.Description" String="An indication by boolean value whether to update firmware configuration along with firmware image update." />
++ <Property Name="ClearConfig" Type="Edm.Boolean">
++ <Annotation Term="OData.Permissions" EnumMember="OData.Permission/ReadWrite"/>
++ <Annotation Term="OData.Description" String="This indicates whether to update firmware configuration or not."/>
++ <Annotation Term="OData.LongDescription" String="The value of this property is used to indicate the firmware configuration update."/>
++ </Property>
++ </ComplexType>
++
++ </Schema>
++ </edmx:DataServices>
++</edmx:Edmx>
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch
new file mode 100644
index 000000000..02f843bb8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Use-chip-id-based-UUID-for-Service-Root.patch
@@ -0,0 +1,74 @@
+From 034920eca21bc25899565484928ee72025e21ff8 Mon Sep 17 00:00:00 2001
+From: Wiktor Golgowski <wiktor.golgowski@linux.intel.com>
+Date: Thu, 30 Apr 2020 11:09:35 +0200
+Subject: [PATCH] Use chip id-based UUID for Service Root.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the sysfs-provided chip id is available, it will be used as
+payload to generate Service Root UUID from hardcoded namespace.
+
+Tested:
+Generated UUID is consistent between BMC image reflashes.
+If the sysfs node is not available, code falls back to randomly
+generated UUID.
+
+Signed-off-by: Wiktor Gołgowski <wiktor.golgowski@linux.intel.com>
+---
+ include/persistent_data.hpp | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
+index 24f7afd..8826b06 100644
+--- a/include/persistent_data.hpp
++++ b/include/persistent_data.hpp
+@@ -25,6 +25,10 @@ class ConfigFile
+ public:
+ // todo(ed) should read this from a fixed location somewhere, not CWD
+ static constexpr const char* filename = "bmcweb_persistent_data.json";
++ static constexpr const char* chipIdSysfsNode = "/sys/devices/platform"
++ "/ahb/ahb:apb/1e6e2000.syscon/1e6e2000.syscon:misc_control/chip_id";
++ static constexpr const char* UuidNs = "{b7b0553a-54cc-4162-982d-"
++ "944847ed76f5}";
+
+ ConfigFile()
+ {
+@@ -144,9 +148,31 @@ class ConfigFile
+
+ if (systemUuid.empty())
+ {
+- systemUuid =
+- boost::uuids::to_string(boost::uuids::random_generator()());
+- needWrite = true;
++ // Try to retrieve chip id-based uuid.
++ std::ifstream chipIdFile(chipIdSysfsNode);
++ if (chipIdFile.is_open())
++ {
++ std::string chipId;
++ std::getline(chipIdFile, chipId);
++ if (!chipId.empty())
++ {
++ boost::uuids::name_generator_sha1 gen(
++ boost::uuids::string_generator()(UuidNs));
++ systemUuid = boost::uuids::to_string(gen(chipId.c_str()));
++ needWrite = true;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Cannot get chip id-based System UUID.";
++ }
++ }
++ // If the above fails, generate random uuid.
++ if (systemUuid.empty())
++ {
++ systemUuid =
++ boost::uuids::to_string(boost::uuids::random_generator()());
++ needWrite = true;
++ }
+ }
+ if (fileRevision < jsonRevision)
+ {
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch
new file mode 100644
index 000000000..e54e495bb
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0010-managers-add-attributes-for-Manager.CommandShell.patch
@@ -0,0 +1,60 @@
+From 971aa5058ac4bb626eeadf8b00738737748ed549 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Tue, 29 Jun 2021 15:25:38 +0000
+Subject: [PATCH] managers: add attributes for Manager.CommandShell
+
+Issue: ConnectTypesSupported, ServiceEnabled and
+ MaxConcurrentSessions Attributes are missing for
+ Manager.CommandShell, though Requirement mandates it.
+
+Fix: Added missing attributes to Manager.CommandShell
+
+Tested:
+1. Verified redfish validator passed
+2. Get bmc details from Redfish
+Redfish URI: https://<BMC IP>/redfish/v1/Managers/bmc
+Response:
+{
+ "@odata.id": "/redfish/v1/Managers/bmc",
+ "@odata.type": "#Manager.v1_9_0.Manager",
+....
+....
+ "CommandShell": {
+ "ConnectTypesSupported": [
+ "SSH",
+ "IPMI"
+ ],
+ "MaxConcurrentSessions": 4,
+ "ServiceEnabled": true
+ },
+....
+....
+
+Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+Change-Id: I2a56db912fc81064098f7aa9f4d110ac3baf361d
+---
+ redfish-core/lib/managers.hpp | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
+index b286f19..186003b 100644
+--- a/redfish-core/lib/managers.hpp
++++ b/redfish-core/lib/managers.hpp
+@@ -1998,6 +1998,14 @@ inline void requestRoutesManager(App& app)
+ 15;
+ asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] =
+ {"IPMI", "SSH"};
++
++ // Fill in CommandShell info
++ asyncResp->res.jsonValue["CommandShell"]["ServiceEnabled"] = true;
++ asyncResp->res.jsonValue["CommandShell"]["MaxConcurrentSessions"] =
++ 4;
++ asyncResp->res.jsonValue["CommandShell"]["ConnectTypesSupported"] =
++ {"SSH", "IPMI"};
++
+ #ifdef BMCWEB_ENABLE_KVM
+ // Fill in GraphicalConsole info
+ asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] =
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch
new file mode 100644
index 000000000..f41e6f994
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch
@@ -0,0 +1,158 @@
+From b9747ecfce682f15dce0bb6e41e0c894f29419f3 Mon Sep 17 00:00:00 2001
+From: Snehalatha Venkatesh <snehalathax.v@intel.com>
+Date: Thu, 8 Apr 2021 14:42:07 +0000
+Subject: [PATCH] bmcweb: Add PhysicalContext to Thermal resources
+
+Adding PhysicalContext to make redfish data compliance with OCP
+Server Mgmt Interface v0.2.1.pdf and specific to Thermal resources.
+https://github.com/opencomputeproject/OCP-Profiles/blob/master/
+OCPServerHardwareManagement.v0_2_4.json
+
+Tested:
+1. Redfish validator - passed for this new change
+2. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Thermal
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/0",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 127.0,
+ "MemberId": "BMC_Temp",
+ "MinReadingRangeTemp": -128.0,
+ "Name": "BMC Temp",
+ "PhysicalContext": "SystemBoard",
+ "ReadingCelsius": 25.75,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+},
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/1",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 255.0,
+ "MemberId": "CPU1_P12V_PVCCIN_VR_Temp",
+ "MinReadingRangeTemp": 0.0,
+ "Name": "CPU1 P12V PVCCIN VR Temp",
+ "PhysicalContext": "CPU",
+ "ReadingCelsius": 41.0,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+},
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Thermal#/Temperatures/28",
+ "@odata.type": "#Thermal.v1_3_0.Temperature",
+ "LowerThresholdCritical": 0.0,
+ "LowerThresholdNonCritical": 5.0,
+ "MaxReadingRangeTemp": 127.0,
+ "MemberId": "Inlet_BRD_Temp",
+ "MinReadingRangeTemp": -128.0,
+ "Name": "Inlet BRD Temp",
+ "PhysicalContext": "Intake",
+ "ReadingCelsius": 23.187,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 115.0,
+ "UpperThresholdNonCritical": 110.0
+},
+{
+ @odata.id": "/redfish/v1/Chassis/F2U8X25_HSBP_2/Thermal#/Temperatures/0",
+ @odata.type": "#Thermal.v1_3_0.Temperature",
+ LowerThresholdCritical": 7.0,
+ LowerThresholdNonCritical": 12.0,
+ MaxReadingRangeTemp": 127.0,
+ MemberId": "HSBP2_Temp",
+ MinReadingRangeTemp": -128.0,
+ Name": "HSBP2 Temp",
+ PhysicalContext": "Backplane",
+ ReadingCelsius": 21.437,
+ Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ UpperThresholdCritical": 57.0,
+ UpperThresholdNonCritical": 52.0
+}
+3. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Power
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Power#/Voltages/3",
+ "@odata.type": "#Power.v1_0_0.Voltage",
+ "LowerThresholdCritical": 1.648,
+ "LowerThresholdNonCritical": 1.699,
+ "MaxReadingRange": 2.3984009912875566,
+ "MemberId": "P1V8_PCH",
+ "MinReadingRange": 0.0,
+ "Name": "P1V8 PCH",
+ "ReadingVolts": 1.8055,
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ },
+ "UpperThresholdCritical": 1.961,
+ "UpperThresholdNonCritical": 1.904
+}
+4. GET - https://<bmc.ip>/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current
+Response:
+{
+ "@odata.id": "/redfish/v1/Chassis/<Board>/Sensors/PSU1_Input_Current",
+ "@odata.type": "#Sensor.v1_0_0.Sensor",
+ "Id": "PSU1_Input_Current",
+ "Name": "PSU1 Input Current",
+ "Reading": 0.947,
+ "ReadingRangeMax": 12.0,
+ "ReadingRangeMin": 0.0,
+ "ReadingType": "Current",
+ "ReadingUnits": "A",
+ "Status": {
+ "Health": "OK",
+ "State": "Enabled"
+ }
+}
+Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com>
+Signed-off-by: sunitakx <sunitax.kumari@linux.intel.com>
+---
+ redfish-core/lib/sensors.hpp | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
+index 5d27577..d51d09f 100644
+--- a/redfish-core/lib/sensors.hpp
++++ b/redfish-core/lib/sensors.hpp
+@@ -973,6 +973,22 @@ inline void objectInterfacesToJson(
+ {
+ unit = "/ReadingCelsius"_json_pointer;
+ sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature";
++ if (sensorName.find("CPU") != std::string::npos)
++ {
++ sensorJson["PhysicalContext"] = "CPU";
++ }
++ else if (sensorName.find("Inlet") != std::string::npos)
++ {
++ sensorJson["PhysicalContext"] = "Intake";
++ }
++ else if (sensorName.find("HSBP") != std::string::npos)
++ {
++ sensorJson["PhysicalContext"] = "Backplane";
++ }
++ else
++ {
++ sensorJson["PhysicalContext"] = "SystemBoard";
++ }
+ // TODO(ed) Documentation says that path should be type fan_tach,
+ // implementation seems to implement fan
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch
new file mode 100644
index 000000000..3ef4ee2de
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0012-Log-RedFish-event-for-Invalid-login-attempt.patch
@@ -0,0 +1,67 @@
+From 1f572a1991fc8d9b08689aa6e3470080467977a7 Mon Sep 17 00:00:00 2001
+From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+Date: Thu, 15 Apr 2021 10:59:42 +0000
+Subject: [PATCH] Log RedFish event for Invalid login attempt
+
+This commit adds support for logging RedFish event log while user tries
+to attempt login with invalid credentials.
+When user trying to login with invalid credentials on HTTPS interface
+like WebUI and RedFish, event should be logged in RedFish event log.
+This event log is useful for further analysis to debug the root-cause
+for failure.
+
+Tested:
+1. Verified RedFish validator passed
+2. Login with wrong credentials on HTTPS interface.
+3. Verified for RedFish/WebUI events. RedFish event logged successfully.
+GET: https://BMC-IP/redfish/v1/Systems/system/LogServices/
+ EventLog/Entries
+Response:
+"Members": [
+{
+ "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/
+ Entries/1618466128",
+ "@odata.type": "#LogEntry.v1_4_0.LogEntry",
+ "Created": "2021-04-15T05:55:28+00:00",
+ "EntryType": "Event",
+ "Id": "1618466128",
+ "Message": "Invalid username or password attempted on HTTPS.",
+ "MessageArgs": [
+ "HTTPS"
+ ],
+ "MessageId": "OpenBMC.0.1.InvalidLoginAttempted",
+ "Name": "System Event Log Entry",
+ "Severity": "Warning"
+}
+
+Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+---
+ include/pam_authenticate.hpp | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp
+index 12f19c0..01bf301 100644
+--- a/include/pam_authenticate.hpp
++++ b/include/pam_authenticate.hpp
+@@ -1,6 +1,7 @@
+ #pragma once
+
+ #include <security/pam_appl.h>
++#include <systemd/sd-journal.h>
+
+ #include <boost/utility/string_view.hpp>
+
+@@ -75,6 +76,10 @@ inline int pamAuthenticateUser(const std::string_view username,
+ PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
+ if (retval != PAM_SUCCESS)
+ {
++ sd_journal_send("MESSAGE= %s", "Invalid login attempted on HTTPS",
++ "PRIORITY=%i", LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.InvalidLoginAttempted",
++ "REDFISH_MESSAGE_ARGS=%s", "HTTPS", NULL);
+ pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
+ return retval;
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch
new file mode 100644
index 000000000..41acb6057
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0013-Add-UART-routing-logic-into-host-console-connection-.patch
@@ -0,0 +1,59 @@
+From 6c10adb53d3247f65e5d9399290e6b8e7962cdef Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+Date: Wed, 28 Apr 2021 17:19:50 -0700
+Subject: [PATCH] Add UART routing logic into host console connection flow
+
+Switching UART routing when starting obmc-service introduces garbled
+character printing out on physical host serial output and it's
+inevitable so this commit moves the routing logic into host console
+connection flow in bmcweb to avoid the issue until SOL is actually
+activated.
+
+Tested: The garbled character printing out was not observed during
+BMC booting. SOL worked well.
+
+Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
+---
+ include/obmc_console.hpp | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp
+index cdb19901e82d..9c4ae8821074 100644
+--- a/include/obmc_console.hpp
++++ b/include/obmc_console.hpp
+@@ -22,6 +22,9 @@ static boost::container::flat_set<crow::websocket::Connection*> sessions;
+
+ static bool doingWrite = false;
+
++constexpr char const* uartMuxCtrlPath = "/sys/bus/platform/drivers/aspeed-uart-routing/1e789098.uart-routing/hicra";
++constexpr char const* uartMuxCtrlVal = "0x03450003";
++
+ inline void doWrite()
+ {
+ if (doingWrite)
+@@ -110,6 +113,22 @@ inline void connectHandler(const boost::system::error_code& ec)
+ return;
+ }
+
++ FILE* file = fopen(uartMuxCtrlPath, "w");
++ if (file != nullptr)
++ {
++ int rc = fputs(uartMuxCtrlVal, file);
++ fclose(file);
++ if (rc < 0)
++ {
++ BMCWEB_LOG_ERROR << "Couldn't change UART routing: " << rc;
++ for (crow::websocket::Connection* session : sessions)
++ {
++ session->close("Error in connecting to host port");
++ }
++ return;
++ }
++ }
++
+ doWrite();
+ doRead();
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch
new file mode 100644
index 000000000..5ffc259c0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0014-recommended-fixes-by-crypto-review-team.patch
@@ -0,0 +1,75 @@
+From aaaa117817687a05284f8bfff07e2404e0d616b7 Mon Sep 17 00:00:00 2001
+From: Radivoje Jovanovic <radivoje.jovanovic@intel.com>
+Date: Thu, 10 Dec 2020 13:42:20 -0800
+Subject: [PATCH] recommended fixes by crypto review team
+
+some curves/cyphers are forbiden to be used by
+Intel crypto team.
+Only enable approved ones.
+the patch was created by aleksandr.v.tereschenko@intel.com
+
+Signed-off-by: Radivoje Jovanovic <radivoje.jovanovic@intel.com>
+---
+ include/ssl_key_handler.hpp | 39 ++++++++++++++++++++-----------------
+ 1 file changed, 21 insertions(+), 18 deletions(-)
+
+diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
+index 39e83d7..8de7349 100644
+--- a/include/ssl_key_handler.hpp
++++ b/include/ssl_key_handler.hpp
+@@ -381,31 +381,34 @@ inline std::shared_ptr<boost::asio::ssl::context>
+ mSslContext->use_private_key_file(sslPemFile,
+ boost::asio::ssl::context::pem);
+
+- // Set up EC curves to auto (boost asio doesn't have a method for this)
+- // There is a pull request to add this. Once this is included in an asio
+- // drop, use the right way
+- // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
+- if (SSL_CTX_set_ecdh_auto(mSslContext->native_handle(), 1) != 1)
++ std::string handshakeCurves = "P-384:P-521:X448";
++ if (SSL_CTX_set1_groups_list(mSslContext->native_handle(), handshakeCurves.c_str()) != 1)
+ {
+- BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n";
++ BMCWEB_LOG_ERROR << "Error setting ECDHE group list\n";
+ }
+
+- std::string mozillaModern = "ECDHE-ECDSA-AES256-GCM-SHA384:"
+- "ECDHE-RSA-AES256-GCM-SHA384:"
+- "ECDHE-ECDSA-CHACHA20-POLY1305:"
+- "ECDHE-RSA-CHACHA20-POLY1305:"
+- "ECDHE-ECDSA-AES128-GCM-SHA256:"
+- "ECDHE-RSA-AES128-GCM-SHA256:"
+- "ECDHE-ECDSA-AES256-SHA384:"
+- "ECDHE-RSA-AES256-SHA384:"
+- "ECDHE-ECDSA-AES128-SHA256:"
+- "ECDHE-RSA-AES128-SHA256";
++ std::string tls12Ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:"
++ "ECDHE-RSA-AES256-GCM-SHA384";
++ std::string tls13Ciphers = "TLS_AES_256_GCM_SHA384";
+
+ if (SSL_CTX_set_cipher_list(mSslContext->native_handle(),
+- mozillaModern.c_str()) != 1)
++ tls12Ciphers.c_str()) != 1)
+ {
+- BMCWEB_LOG_ERROR << "Error setting cipher list\n";
++ BMCWEB_LOG_ERROR << "Error setting TLS 1.2 cipher list\n";
+ }
++
++ if (SSL_CTX_set_ciphersuites(mSslContext->native_handle(),
++ tls13Ciphers.c_str()) != 1)
++ {
++ BMCWEB_LOG_ERROR << "Error setting TLS 1.3 cipher list\n";
++ }
++
++ if ((SSL_CTX_set_options(mSslContext->native_handle(),
++ SSL_OP_CIPHER_SERVER_PREFERENCE) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0)
++ {
++ BMCWEB_LOG_ERROR << "Error setting TLS server preference option\n";
++ }
++
+ return mSslContext;
+ }
+ } // namespace ensuressl
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch
new file mode 100644
index 000000000..2b8a6f294
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0015-Add-state-sensor-messages-to-the-registry.patch
@@ -0,0 +1,95 @@
+From b76ee9de6bc994e4ea1d65b797785a7b9e2f994c Mon Sep 17 00:00:00 2001
+From: "Arun P. Mohanan" <arun.p.m@linux.intel.com>
+Date: Wed, 27 Jan 2021 18:22:58 +0530
+Subject: [PATCH] Add state sensor messages to the registry
+
+Add messages to registry to indicate state sensor state change.
+
+Tested:
+Build and redfish validator passes.
+Logged these events and confirmed that they appear as expected on
+Redfish.
+GET: https://<BMC IP>/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180
+{
+ "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180",
+ "@odata.type": "#LogEntry.v1_4_0.LogEntry",
+ "Created": "2021-02-05T12:29:40+00:00",
+ "EntryType": "Event",
+ "Id": "1612528180",
+ "Message": "Operational Fault Status of Card_health_1 state sensor changed from Error to Normal.",
+ "MessageArgs": [
+ "Operational Fault Status",
+ "Card_health_1",
+ "Error",
+ "Normal"
+ ],
+ "MessageId": "OpenBMC.0.1.StateSensorNormal",
+ "Name": "System Event Log Entry",
+ "Severity": "OK"
+}
+
+Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com>
+---
+ .../registries/openbmc_message_registry.hpp | 33 +++++++++++++++++--
+ 1 file changed, 31 insertions(+), 2 deletions(-)
+
+diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
+index 9a8c0f6..8f66d7d 100644
+--- a/redfish-core/include/registries/openbmc_message_registry.hpp
++++ b/redfish-core/include/registries/openbmc_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ "0.3.1",
+ "OpenBMC",
+ };
+-constexpr std::array<MessageEntry, 189> registry = {
++constexpr std::array<MessageEntry, 192> registry = {
+ MessageEntry{
+ "ADDDCCorrectable",
+ {
+@@ -2155,6 +2155,36 @@ constexpr std::array<MessageEntry, 189> registry = {
+ {},
+ "None.",
+ }},
++ MessageEntry{
++ "StateSensorNormal",
++ {
++ "Indicates that a state sensor has changed state to normal.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "OK",
++ 4,
++ {"string", "string", "string", "string"},
++ "None.",
++ }},
++ MessageEntry{
++ "StateSensorWarning",
++ {
++ "Indicates that a state sensor has changed state to warning.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Warning",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
++ MessageEntry{
++ "StateSensorCritical",
++ {
++ "Indicates that a state sensor has changed state to critical.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Critical",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
+ MessageEntry{"SystemInterfaceDisabledProvisioned",
+ {
+ "Indicates that the system interface is in the disabled "
+@@ -2239,6 +2269,5 @@ constexpr std::array<MessageEntry, 189> registry = {
+ {"string"},
+ "None.",
+ }},
+-
+ };
+ } // namespace redfish::registries::openbmc
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch
new file mode 100644
index 000000000..c34ff741f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch
@@ -0,0 +1,44 @@
+From ae7b8d17579a0d5aae28e568d179bda779137fed Mon Sep 17 00:00:00 2001
+From: Nidhin MS <nidhin.ms@intel.com>
+Date: Wed, 14 Apr 2021 11:28:44 +0530
+Subject: [PATCH] Fix: bmcweb crashes if socket directory not present
+
+When trying to mount virtual media image bmcweb tries to create unix
+socket and if the parent directory does not exist
+stream_protocol::acceptor throws error and bmcweb crashes. Fix the same
+
+Tested:
+Removed directory and mounted the vm image. bmcweb crash was not
+observed
+
+Change-Id: I3aea1d8e197c06238f425a97435c01d3c80552a9
+Signed-off-by: Nidhin MS <nidhin.ms@intel.com>
+---
+ include/nbd_proxy.hpp | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp
+index afb6493..b451f3a 100644
+--- a/include/nbd_proxy.hpp
++++ b/include/nbd_proxy.hpp
+@@ -385,6 +385,17 @@ inline void requestRoutes(App& app)
+ // If the socket file exists (i.e. after bmcweb crash),
+ // we cannot reuse it.
+ std::remove((*socketValue).c_str());
++ std::filesystem::path socketPath(*socketValue);
++ std::error_code fsErr;
++ if (!std::filesystem::exists(socketPath.parent_path(),
++ fsErr))
++ {
++ BMCWEB_LOG_ERROR
++ << "VirtualMedia socket directory not present. "
++ << socketPath.parent_path();
++ conn.close("Unable to create unix socket");
++ return;
++ }
+
+ sessions[&conn] = std::make_shared<NbdProxyServer>(
+ conn, *socketValue, *endpointValue,
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch
new file mode 100644
index 000000000..54c892ffd
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0017-Add-msg-registry-for-subscription-related-actions.patch
@@ -0,0 +1,78 @@
+From a7769b0d85bb6c88f5eeaf70d56939d173a1cd80 Mon Sep 17 00:00:00 2001
+From: Ayushi Smriti <smriti.ayushi@intel.com>
+Date: Mon, 10 May 2021 12:32:30 +0530
+Subject: [PATCH] Add msg registry for subscription related actions
+
+For subscription event message log purpose, added message registry
+entry for event service subscription related actions- add, update
+and delete.
+
+Tested:
+ - Message registry entry appears in the log for the corresponding
+ subscription action.
+
+Signed-off-by: AppaRao Puli <apparao.puli@intel.com>
+Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com>
+---
+ .../registries/openbmc_message_registry.hpp | 38 ++++++++++++++++++-
+ 1 file changed, 37 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
+index 8f66d7d..bedaf6a 100644
+--- a/redfish-core/include/registries/openbmc_message_registry.hpp
++++ b/redfish-core/include/registries/openbmc_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ "0.3.1",
+ "OpenBMC",
+ };
+-constexpr std::array<MessageEntry, 192> registry = {
++constexpr std::array<MessageEntry, 195> registry = {
+ MessageEntry{
+ "ADDDCCorrectable",
+ {
+@@ -392,6 +392,42 @@ constexpr std::array<MessageEntry, 192> registry = {
+ {},
+ "None.",
+ }},
++ MessageEntry{
++ "EventSubscriptionAdded",
++ {
++ "Indicates that an Event subscription with specific id was added.",
++ "Event subscription with id %1 was added.",
++ "OK",
++ 1,
++ {
++ "string",
++ },
++ "None.",
++ }},
++ MessageEntry{
++ "EventSubscriptionRemoved",
++ {
++ "Indicates that an Event subscription with specific id was removed.",
++ "Event subscription with id %1 was removed.",
++ "OK",
++ 1,
++ {
++ "string",
++ },
++ "None.",
++ }},
++ MessageEntry{
++ "EventSubscriptionUpdated",
++ {
++ "Indicates that an Event subscription with specific id was updated.",
++ "Event subscription with id %1 was updated.",
++ "OK",
++ 1,
++ {
++ "string",
++ },
++ "None.",
++ }},
+ MessageEntry{"FanInserted",
+ {
+ "Indicates that a system fan has been inserted.",
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch
new file mode 100644
index 000000000..ab4f65b8b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch
@@ -0,0 +1,74 @@
+From d36121d09db4e044b0d7599eae84ad7dc3240fca Mon Sep 17 00:00:00 2001
+From: mansijos <mansi.joshi@intel.com>
+Date: Wed, 26 May 2021 17:40:04 +0530
+Subject: [PATCH] Add BMC Time update log to the registry
+
+Add message in registry to log an event that indicates BMC time
+is set via NTP, Host or Manually.
+During early stage of system boot if any critical events occur,
+they are getting logged with 1970 timestamp till the time BMC
+time update happens. This is expected behavior, but to call it out
+explicitly it is good to log when BMC time is updated.
+
+Tested:
+Built and validator passes.
+Confirmed that the event is getting logged correctly in Redfish.
+
+Signed-off-by: mansijos <mansi.joshi@intel.com>
+---
+ .../registries/openbmc_message_registry.hpp | 32 ++++++++++++++++++-
+ 1 file changed, 31 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
+index bedaf6a..4837d2b 100644
+--- a/redfish-core/include/registries/openbmc_message_registry.hpp
++++ b/redfish-core/include/registries/openbmc_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ "0.3.1",
+ "OpenBMC",
+ };
+-constexpr std::array<MessageEntry, 195> registry = {
++constexpr std::array<MessageEntry, 198> registry = {
+ MessageEntry{
+ "ADDDCCorrectable",
+ {
+@@ -263,6 +263,36 @@ constexpr std::array<MessageEntry, 195> registry = {
+ {},
+ "None.",
+ }},
++ MessageEntry{
++ "BMCTimeUpdatedViaHost",
++ {
++ "Indicates that BMC time has been set via Host.",
++ "BMC time has been set via Host. Date Time is set to %1 from %2.",
++ "OK",
++ 2,
++ {"string", "string"},
++ "None.",
++ }},
++ MessageEntry{
++ "BMCTimeUpdatedManually",
++ {
++ "Indicates that BMC time has been set Manually.",
++ "BMC time has been set Manually. Date Time is set to %1 from %2.",
++ "OK",
++ 2,
++ {"string", "string"},
++ "None.",
++ }},
++ MessageEntry{
++ "BMCTimeUpdatedViaNTP",
++ {
++ "Indicates that BMC time has been set via NTP.",
++ "BMC time has been set via NTP. Date Time is set to %1 from %2.",
++ "OK",
++ 2,
++ {"string", "string"},
++ "None.",
++ }},
+ MessageEntry{"ChassisIntrusionDetected",
+ {
+ "Indicates that a physical security event "
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch
new file mode 100644
index 000000000..c2be99ce0
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0019-Add-generic-message-PropertySizeExceeded.patch
@@ -0,0 +1,120 @@
+From 32db2ac5cdd9f32a8bbd4c6e045dad9747634d5b Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Wed, 6 Oct 2021 21:51:16 +0000
+Subject: [PATCH] Add generic message - PropertySizeExceeded
+
+Adding a generic error message "PropertySizeExceeded"
+to address properties which exceed there defined size limit.
+
+Tested:
+No functional change. Build passed.
+Verified by explicitly sending this message as a response.
+
+Change-Id: I0e9f85f82a69c598e169fc8e9a68c3f66c0084d8
+Signed-off-by: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com>
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+
+%% original patch: 0019-Add-generic-message-PropertySizeExceeded.patch
+---
+ redfish-core/include/error_messages.hpp | 12 +++++++++++
+ .../registries/base_message_registry.hpp | 16 +++++++++++++--
+ redfish-core/src/error_messages.cpp | 20 +++++++++++++++++++
+ 3 files changed, 46 insertions(+), 2 deletions(-)
+
+diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp
+index 6993a9d..2375bc3 100644
+--- a/redfish-core/include/error_messages.hpp
++++ b/redfish-core/include/error_messages.hpp
+@@ -242,6 +242,18 @@ nlohmann::json propertyValueNotInList(std::string_view arg1,
+ void propertyValueNotInList(crow::Response& res, std::string_view arg1,
+ std::string_view arg2);
+
++/**
++ * @brief Formats PropertySizeExceeded message into JSON
++ * Message body: "The property <arg1> is too long. The value exceeds its size
++ * limit."
++ *
++ * @param[in] arg1 Parameter of message that will replace %1 in its body.
++ *
++ * @returns Message PropertySizeExceeded formatted to JSON */
++nlohmann::json propertySizeExceeded(std::string_view arg1);
++
++void propertySizeExceeded(crow::Response& res, std::string_view arg1);
++
+ /**
+ * @brief Formats ResourceAtUriInUnknownFormat message into JSON
+ * Message body: "The resource at <arg1> is in a format not recognized by the
+diff --git a/redfish-core/include/registries/base_message_registry.hpp b/redfish-core/include/registries/base_message_registry.hpp
+index 48875ec..279c0a0 100644
+--- a/redfish-core/include/registries/base_message_registry.hpp
++++ b/redfish-core/include/registries/base_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ constexpr const char* url =
+ "https://redfish.dmtf.org/registries/Base.1.11.0.json";
+
+-constexpr std::array<MessageEntry, 93> registry =
++constexpr std::array<MessageEntry, 94> registry =
+ {
+ MessageEntry{
+ "AccessDenied",
+@@ -1108,7 +1108,18 @@ constexpr std::array<MessageEntry, 93> registry =
+ {},
+ "Correct the request body and resubmit the request if it failed.",
+ }},
+-
++ MessageEntry{
++ "PropertySizeExceeded",
++ {
++ "Indicates that a given property exceeds the size limit imposed.",
++ "The property %1 is too long. The value exceeds its size limit.",
++ "Warning",
++ 1,
++ {
++ "string",
++ },
++ "Correct the value for the property in the request body and resubmit the request if the operation failed.",
++ }}
+ };
+
+ enum class Index
+@@ -1206,5 +1217,6 @@ enum class Index
+ success = 90,
+ undeterminedFault = 91,
+ unrecognizedRequestBody = 92,
++ propertySizeExceeded = 93
+ };
+ } // namespace redfish::registries::base
+diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp
+index 7733db3..ed3256f 100644
+--- a/redfish-core/src/error_messages.cpp
++++ b/redfish-core/src/error_messages.cpp
+@@ -448,6 +448,26 @@ void propertyValueFormatError(crow::Response& res, std::string_view arg1,
+ addMessageToJson(res.jsonValue, propertyValueFormatError(arg1, arg2), arg2);
+ }
+
++/**
++ * @internal
++ * @brief Formats PropertySizeExceeded message into JSON for the specified
++ * property
++ *
++ * See header file for more information
++ * @endinternal
++ */
++nlohmann::json propertySizeExceeded(std::string_view arg1)
++{
++ return getLog(redfish::registries::base::Index::propertySizeExceeded,
++ std::to_array({arg1}));
++}
++
++void propertySizeExceeded(crow::Response& res, std::string_view arg1)
++{
++ res.result(boost::beast::http::status::bad_request);
++ addMessageToJson(res.jsonValue, propertySizeExceeded(arg1), arg1);
++}
++
+ /**
+ * @internal
+ * @brief Formats PropertyValueNotInList message into JSON for the specified
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch
new file mode 100644
index 000000000..cc9da3b8b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch
@@ -0,0 +1,85 @@
+From f75efac9eebea8bf8f548d10a8cbafa28f556a8f Mon Sep 17 00:00:00 2001
+From: Meera-Katta <meerax.katta@linux.intel.com>
+Date: Wed, 7 Jul 2021 13:19:09 +0000
+Subject: [PATCH] Redfish: Deny set AccountLockDuration to zero
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Issue: Redfish schema says, no lockout shall occur in case of Account
+LockoutDuration value is zero. But Linux PAM module documentation says, if
+account lockout value is zero, account will be locked out indefinitely
+after the number of failed login attempts. As per the current
+implementation user can write any value into the PAM module. If user tried
+to set unlock timeout value to zero, account will be locked out
+indefinitely until administrator explicitly reenables it.
+
+Workaround: Denying user to set AccountLockDuration to zero from Redfish.
+Setting ‘AccountLockDuration’ to 0 will be permitted only after
+‘AccountLockoutCounterResetEnabled’ support is added.
+Otherwise,account will be locked permanently after the AccountLockoutDuration
+if ‘AccountLockDuration’ is set to zero, while
+AccountLockoutThreshold is non zero. If someone wants no account lockout
+irrespective of number of failed login attempts, it can be still achieved by
+setting ‘AccountLockoutThreshold’ to zero
+(instead of trying to set ‘AccountLockDuration’ to zero.)
+
+Tested:
+1) Redfish Service Validator passed for this change.
+2) Verified from Redfish
+PATCH : https://<BMC IP>/redfish/v1/AccountService
+Body:
+{"AccountLockoutDuration":0}
+
+Response:
+{
+ "AccountLockoutDuration@Message.ExtendedInfo": [
+ {
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "The value unlockTimeout for the property
+ AccountLockoutDuration is not in the list of acceptable values.",
+ "MessageArgs": [
+ "unlockTimeout",
+ "AccountLockoutDuration"
+ ],
+ "MessageId": "Base.1.8.1.PropertyValueNotInList",
+ "MessageSeverity": "Warning",
+ "Resolution": "Choose a value from the enumeration list that the
+ implementation can support and resubmit the request if the
+ operation failed."
+ }
+ ]
+}
+
+Signed-off-by: Meera-Katta <meerax.katta@linux.intel.com>
+---
+ redfish-core/lib/account_service.hpp | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
+index e6fe205..42085fa 100644
+--- a/redfish-core/lib/account_service.hpp
++++ b/redfish-core/lib/account_service.hpp
+@@ -1448,6 +1448,19 @@ inline void requestAccountServiceRoutes(App& app)
+
+ if (unlockTimeout)
+ {
++ // Account will be locked permanently after the N number
++ // of failed login attempts if we set unlockTimeout value
++ // to be 0.
++ if (unlockTimeout.value() == 0)
++ {
++ BMCWEB_LOG_INFO
++ << "Unlock timeout value must be greater"
++ "than zero";
++ messages::propertyValueNotInList(asyncResp->res,
++ "unlockTimeout",
++ "AccountLockoutDuration");
++ return;
++ }
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch
new file mode 100644
index 000000000..b3feee39a
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0023-Add-get-IPMI-session-id-s-to-Redfish.patch
@@ -0,0 +1,390 @@
+From 5c79e34be9357c2a2cd9bac61cd0162dbd342a2d Mon Sep 17 00:00:00 2001
+From: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+Date: Fri, 30 Jul 2021 17:33:16 +0000
+Subject: [PATCH] Add/get IPMI session id's to Redfish
+
+As per existing implementation, Redfish supports to get only sessions
+created on Redfish & EWS. But as per Redfish schema Redfish should
+include to get IPMI sessions as well.
+So add support to display IPMI session Id's as well on Redfish.
+This commit will not impact any functionality/behavior of existing code.
+Below functionalities implemented in this commit.
+1. Get IPMI session collection
+2. Get individual IPMI session information
+3. Delete IPMI sessions - Respond with not supported as we can't delete
+ IPMI sessions from Redfish interface
+
+Tested:
+1. Verified redfish validator passed with active IPMI session.
+2. Get session details from Redfish
+GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions
+Response:
+{
+ "@odata.id": "/redfish/v1/SessionService/Sessions/",
+ "@odata.type": "#SessionCollection.SessionCollection",
+ "Description": "Session Collection",
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/SessionService/Sessions/TlFPbR9ZIn"
+ },
+ {
+ "@odata.id": "/redfish/v1/SessionService/Sessions/184U3014ub"
+ },
+ {
+ "@odata.id": "/redfish/v1/SessionService/Sessions/cV0xi5QoPy"
+ },
+ {
+ "@odata.id": "/redfish/v1/SessionService/Sessions/8f6234d7_81"
+ }
+ ],
+ "Members@odata.count": 4,
+ "Name": "Session Collection"
+}
+
+3. Get session details from RedFish
+Case 1: RedFish session
+GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions/TlFPbR9ZIn
+Response:
+{
+ "@odata.id": "/redfish/v1/SessionService/Sessions/TlFPbR9ZIn",
+ "@odata.type": "#Session.v1_3_0.Session",
+ "ClientOriginIPAddress": "::ffff:10.213.91.40",
+ "Description": "Manager User Session",
+ "Id": "TlFPbR9ZIn",
+ "Name": "User Session",
+ "UserName": "root"
+}
+Case 2: IPMI session
+Verified and displayed IPMI session details on RedFish.
+GET: https://<BMC-IP>/redfish/v1/SessionService/Sessions/8f6234d7_81
+Response:
+{
+ "@odata.id": "/redfish/v1/SessionService/Sessions/8f6234d7_81",
+ "@odata.type": "#Session.v1_3_0.Session",
+ "ClientOriginIPAddress": "xx.xx.xx.xx",
+ "Description": "Manager User Session",
+ "Id": "8f6234d7_81",
+ "Name": "User Session",
+ "UserName": "root"
+}
+4. Delete IPMI session:
+Verified IPMI session is not allowed to delete from Redfish
+DELETE: https://<BMC-IP>/redfish/v1/SessionService/Sessions/8f6234d7_81
+Response:
+{
+ "error": {
+ "@Message.ExtendedInfo": [
+ {
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "The action deleting IPMI session from
+ Redfish is not supported by the resource.",
+ "MessageArgs": [
+ "deleting IPMI session from Redfish"
+ ],
+ "MessageId": "Base.1.8.1.ActionNotSupported",
+ "MessageSeverity": "Critical",
+ "Resolution": "The action supplied cannot be resubmitted
+ to the implementation. Perhaps the action was invalid,
+ the wrong resource was the target or the implementation
+ documentation may be of assistance."
+ }
+ ],
+ "code": "Base.1.8.1.ActionNotSupported",
+ "message": "The action deleting IPMI session from Redfish is not
+ supported by the resource."
+ }
+}
+5. Delete RedFish session
+Result: successfully deleted valid RedFish session.
+
+Signed-off-by: Jayaprakash Mutyala <mutyalax.jayaprakash@intel.com>
+---
+ redfish-core/lib/redfish_sessions.hpp | 244 +++++++++++++++++++++++---
+ 1 file changed, 222 insertions(+), 22 deletions(-)
+
+diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
+index 929e0c8..3c7a968 100644
+--- a/redfish-core/lib/redfish_sessions.hpp
++++ b/redfish-core/lib/redfish_sessions.hpp
+@@ -56,14 +56,127 @@ inline void requestRoutesSession(App& app)
+ auto session = persistent_data::SessionStore::getInstance()
+ .getSessionByUid(sessionId);
+
+- if (session == nullptr)
++ if (session)
+ {
+- messages::resourceNotFound(asyncResp->res, "Session",
+- sessionId);
++ fillSessionObject(asyncResp->res, *session);
+ return;
+ }
+
+- fillSessionObject(asyncResp->res, *session);
++ std::array<std::string, 1> interfaces = {
++ "xyz.openbmc_project.Ipmi.SessionInfo"};
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, sessionId](const boost::system::error_code ec,
++ const GetSubTreeType& subtree) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Error in querying GetSubTree with "
++ "Object Mapper. "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ if (subtree.size() == 0)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Can't find Session Info Attributes!";
++ messages::resourceNotFound(asyncResp->res,
++ "Session", sessionId);
++ return;
++ }
++ bool ipmiSessionFound = false;
++ std::string ipmiSessionService;
++ std::string ipmiSessionInfPath;
++ for (const auto& [ipmiSessionPath, object] : subtree)
++ {
++ if (ipmiSessionPath.empty() || object.empty())
++ {
++ BMCWEB_LOG_DEBUG
++ << "Session Info Attributes mapper error!";
++ continue;
++ }
++ if (!boost::ends_with(ipmiSessionPath, sessionId))
++ {
++ continue;
++ }
++ ipmiSessionFound = true;
++ ipmiSessionService = object[0].first;
++ ipmiSessionInfPath = ipmiSessionPath;
++ break;
++ }
++ if (!ipmiSessionFound)
++ {
++ messages::resourceNotFound(asyncResp->res,
++ "Session", sessionId);
++ return;
++ }
++ if (ipmiSessionService.empty())
++ {
++ BMCWEB_LOG_DEBUG
++ << "Session Info Attributes mapper "
++ "error!";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, sessionId](
++ const boost::system::error_code ec,
++ const std::vector<std::pair<
++ std::string,
++ std::variant<std::monostate, std::string,
++ uint32_t>>>& properties) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Error in querying Session "
++ "Info State property "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ std::string userName = "";
++ uint32_t remoteIpAddr;
++ try
++ {
++ sdbusplus::unpackProperties(
++ properties, "Username", userName,
++ "RemoteIPAddr", remoteIpAddr);
++ asyncResp->res.jsonValue["Id"] = sessionId;
++ asyncResp->res.jsonValue["UserName"] =
++ userName;
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/SessionService/"
++ "Sessions/" +
++ sessionId;
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#Session.v1_3_0.Session";
++ asyncResp->res.jsonValue["Name"] =
++ "User Session";
++ asyncResp->res.jsonValue["Description"] =
++ "Manager User Session";
++ struct in_addr ipAddr;
++ ipAddr.s_addr = remoteIpAddr;
++ asyncResp->res
++ .jsonValue["ClientOriginIPAddress"] =
++ inet_ntoa(ipAddr);
++ }
++ catch (const sdbusplus::exception::
++ UnpackPropertyError& error)
++ {
++ BMCWEB_LOG_ERROR << error.what();
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ return;
++ },
++ ipmiSessionService, ipmiSessionInfPath,
++ "org.freedesktop.DBus.Properties", "GetAll",
++ "xyz.openbmc_project.Ipmi.SessionInfo");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
++ interfaces);
+ });
+
+ BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
+@@ -75,34 +188,79 @@ inline void requestRoutesSession(App& app)
+ auto session = persistent_data::SessionStore::getInstance()
+ .getSessionByUid(sessionId);
+
+- if (session == nullptr)
+- {
+- messages::resourceNotFound(asyncResp->res, "Session",
+- sessionId);
+- return;
+- }
+-
+ // Perform a proper ConfigureSelf authority check. If a
+ // session is being used to DELETE some other user's session,
+ // then the ConfigureSelf privilege does not apply. In that
+ // case, perform the authority check again without the user's
+ // ConfigureSelf privilege.
+- if (session->username != req.session->username)
++ if (session)
+ {
+- Privileges effectiveUserPrivileges =
+- redfish::getUserPrivileges(req.userRole);
+-
+- if (!effectiveUserPrivileges.isSupersetOf(
+- {"ConfigureUsers"}))
++ if (session->username != req.session->username)
+ {
+- messages::insufficientPrivilege(asyncResp->res);
+- return;
++ Privileges effectiveUserPrivileges =
++ redfish::getUserPrivileges(req.userRole);
++
++ if (!effectiveUserPrivileges.isSupersetOf(
++ {"ConfigureUsers"}))
++ {
++ messages::insufficientPrivilege(asyncResp->res);
++ return;
++ }
+ }
++ persistent_data::SessionStore::getInstance().removeSession(
++ session);
++ messages::success(asyncResp->res);
++ return;
+ }
+
+- persistent_data::SessionStore::getInstance().removeSession(
+- session);
+- messages::success(asyncResp->res);
++ std::array<std::string, 1> interfaces = {
++ "xyz.openbmc_project.Ipmi.SessionInfo"};
++ crow::connections::systemBus->async_method_call(
++ [asyncResp,
++ sessionId](const boost::system::error_code ec,
++ const std::vector<std::string>& ifaceList) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Error in querying GetSubTreePaths "
++ "with Object Mapper. "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ if (ifaceList.size() == 0)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Can't find Session Info Attributes!";
++ return;
++ }
++ bool ipmiSessionFound = false;
++ for (const std::string& ipmiSessionPath : ifaceList)
++ {
++ if (!boost::ends_with(ipmiSessionPath, sessionId))
++ {
++ continue;
++ }
++ ipmiSessionFound = true;
++ break;
++ }
++ if (ipmiSessionFound)
++ {
++ BMCWEB_LOG_DEBUG << "Deleting IPMI session from "
++ "Redfish is not allowed.";
++ messages::actionNotSupported(
++ asyncResp->res,
++ "deleting IPMI session from Redfish");
++ return;
++ }
++ messages::resourceNotFound(asyncResp->res, "Session",
++ sessionId);
++ return;
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
++ 0, interfaces);
+ });
+
+ BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
+@@ -131,6 +289,48 @@ inline void requestRoutesSession(App& app)
+ "/redfish/v1/SessionService/Sessions/";
+ asyncResp->res.jsonValue["Name"] = "Session Collection";
+ asyncResp->res.jsonValue["Description"] = "Session Collection";
++
++ std::array<std::string, 1> interfaces = {
++ "xyz.openbmc_project.Ipmi.SessionInfo"};
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const std::vector<std::string>& ifaceList) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Error in querying GetSubTreePaths "
++ "with Object Mapper. "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ if (ifaceList.size() == 0)
++ {
++ BMCWEB_LOG_DEBUG
++ << "Can't find Session Info Attributes!";
++ return;
++ }
++ for (const std::string& ipmiSessionPath : ifaceList)
++ {
++ std::filesystem::path filePath(ipmiSessionPath);
++ std::string ipmiSessionID =
++ filePath.has_filename() ? filePath.filename()
++ : "";
++ if (!ipmiSessionID.empty() && ipmiSessionID != "0")
++ {
++ asyncResp->res.jsonValue["Members"].push_back(
++ {{"@odata.id",
++ "/redfish/v1/SessionService/Sessions/" +
++ ipmiSessionID}});
++ }
++ }
++ asyncResp->res.jsonValue["Members@odata.count"] =
++ asyncResp->res.jsonValue["Members"].size();
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
++ 0, interfaces);
+ });
+
+ BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch
new file mode 100644
index 000000000..22ae05fa3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0024-Add-count-sensor-type.patch
@@ -0,0 +1,29 @@
+From 94a0ae774933b7801d0c8d843b3ac3a39a5e5646 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Adrian=20Ambro=C5=BCewicz?= <adrian.ambrozewicz@intel.com>
+Date: Fri, 30 Jul 2021 15:25:29 +0200
+Subject: [PATCH] Add 'count' sensor type
+
+PMT exposes data mainly in raw counter formats. This change makes
+bmcweb aware of new sensor type.
+
+Testing:
+- values of type 'count' from PMT exposed successfully on Redfish
+---
+ redfish-core/lib/sensors.hpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
+index 45a1eb6..7405e5a 100644
+--- a/redfish-core/lib/sensors.hpp
++++ b/redfish-core/lib/sensors.hpp
+@@ -63,6 +63,7 @@ static const boost::container::flat_map<std::string_view,
+ {node::sensors,
+ {"/xyz/openbmc_project/sensors/power",
+ "/xyz/openbmc_project/sensors/current",
++ "/xyz/openbmc_project/sensors/count",
+ "/xyz/openbmc_project/sensors/airflow",
+ #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM
+ "/xyz/openbmc_project/sensors/voltage",
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-to-ProcessorSummary.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-to-ProcessorSummary.patch
new file mode 100644
index 000000000..8bb9d7d34
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0025-Add-Model-to-ProcessorSummary.patch
@@ -0,0 +1,108 @@
+From bd71b3bd6b58b6c94bbeaeffb31631d4f5393578 Mon Sep 17 00:00:00 2001
+From: Ali Ahmed <ama213000@gmail.com>
+Date: Fri, 3 Sep 2021 02:33:43 -0500
+Subject: [PATCH] Add Model to ProcessorSummary
+
+In Redfish ComputerSystem schema, the ProcessorSummary parameter
+lists summary information of the Processors on the system. This commit
+adds the 'Model' property to ProcessorSummary.
+
+If the CPU Models are different, then the 'Model' field takes the first
+entry in alphabetical order.
+
+Testing:
+1. Redfish Validator Testing successfully passed.
+2. Curl testing:
+
+curl -k -H "X-Auth-Token: $tok" https://$bmc/redfish/v1/Systems/system
+
+...
+ "ProcessorSummary": {
+ "CoreCount": 24,
+ "Count": 2,
+ "Model": "test_name",
+ "Status": {
+ "Health": "OK",
+ "HealthRollup": "OK",
+ "State": "Disabled"
+ }
+ },
+...
+
+Change-Id: I39cbf6ed35c35ce3a3551c9689237d5023775326
+Signed-off-by: Ali Ahmed <ama213000@gmail.com>
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ redfish-core/lib/systems.hpp | 47 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 41 insertions(+), 6 deletions(-)
+
+diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
+index 62bd11b0..1b343693 100644
+--- a/redfish-core/lib/systems.hpp
++++ b/redfish-core/lib/systems.hpp
+@@ -165,21 +165,56 @@ inline void getProcessorProperties(
+
+ for (const auto& property : properties)
+ {
++ if (property.first == "Family")
++ {
++ // Get the CPU Model
++ const std::string* modelStr =
++ std::get_if<std::string>(&property.second);
++ if (!modelStr)
++ {
++ BMCWEB_LOG_DEBUG << "Failed to get CPU Family";
++ // Skip it and continue with other properties
++ continue;
++ }
++ if ((*modelStr).size() < 1)
++ {
++ BMCWEB_LOG_DEBUG << "Empty CPU Family info, skipping...";
++ continue;
++ }
++ nlohmann::json& prevModel =
++ aResp->res.jsonValue["ProcessorSummary"]["Model"];
++ std::string* prevModelPtr = prevModel.get_ptr<std::string*>();
+
+- // TODO: Get Model
++ // If CPU Models are different, use the first entry in
++ // alphabetical order
+
+- // Get CoreCount
+- if (property.first == "CoreCount")
++ // If Model has never been set
++ // before, set it to *modelStr
++ if (prevModelPtr == nullptr)
++ {
++ prevModel = *modelStr;
++ }
++ // If Model has been set before, only change if new Model is
++ // higher in alphabetical order
++ else
++ {
++ if (*modelStr < *prevModelPtr)
++ {
++ prevModel = *modelStr;
++ }
++ }
++ }
++ else if (property.first == "CoreCount")
+ {
+-
+ // Get CPU CoreCount and add it to the total
+ const uint16_t* coreCountVal =
+ std::get_if<uint16_t>(&property.second);
+
+ if (coreCountVal == nullptr)
+ {
+- messages::internalError(aResp->res);
+- return;
++ BMCWEB_LOG_DEBUG << "Failed to get CPU Core count";
++ // Skip it and continue with other properties
++ continue;
+ }
+
+ nlohmann::json& coreCount =
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0026-Revert-Delete-the-copy-constructor-on-the-Request.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0026-Revert-Delete-the-copy-constructor-on-the-Request.patch
new file mode 100644
index 000000000..4e525c9a3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0026-Revert-Delete-the-copy-constructor-on-the-Request.patch
@@ -0,0 +1,33 @@
+From 1503fcc9e5fb8d85cfceb03b38a52a519fb63210 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Tue, 4 Jan 2022 06:49:58 +0530
+Subject: [PATCH] Revert "Delete the copy constructor on the Request object"
+
+This commit is reverted to resolve build issues due arising due
+to removal of the copy constructor.
+
+This reverts commit 597d2b142362bafa90f24fc8c30750afab91f78f.
+---
+ http/http_request.hpp | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/http/http_request.hpp b/http/http_request.hpp
+index be1c2a2..be84f18 100644
+--- a/http/http_request.hpp
++++ b/http/http_request.hpp
+@@ -45,12 +45,6 @@ struct Request
+ }
+ }
+
+- Request(const Request&) = delete;
+- Request(const Request&&) = delete;
+- Request& operator=(const Request&) = delete;
+- Request& operator=(const Request&&) = delete;
+- ~Request() = default;
+-
+ boost::beast::http::verb method() const
+ {
+ return req.method();
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0028-Add-Redfish-schema-files-for-OEM-PECI.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0028-Add-Redfish-schema-files-for-OEM-PECI.patch
new file mode 100644
index 000000000..80b5c9678
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0028-Add-Redfish-schema-files-for-OEM-PECI.patch
@@ -0,0 +1,216 @@
+From cf40628193cfbb77580b60d1c7da0c34c0bb3d8f Mon Sep 17 00:00:00 2001
+From: yes <smriti.ayushi@intel.com>
+Date: Mon, 7 Feb 2022 22:56:42 +0530
+Subject: [PATCH] Add Redfish schema files for OEM PECI
+
+OpenBMC supports 'SendRawPeci' command in Redfish.
+This change is to add Json and Xml OEM schema files for the same
+in standard Redfish schema file format.
+
+Tested:
+-Redfish validator passed successfully.
+-Able to read the schema file from bmc ip. OemCrashdump namespace
+ gets created.
+
+Signed-off-by: smriti.ayushi@intel.com
+---
+ scripts/update_schemas.py | 5 ++
+ static/redfish/v1/$metadata/index.xml | 3 +
+ .../OemCrashdump/OemCrashdump.json | 86 +++++++++++++++++++
+ .../v1/JsonSchemas/OemCrashdump/index.json | 20 +++++
+ static/redfish/v1/schema/OemCrashdump_v1.xml | 33 +++++++
+ 5 files changed, 147 insertions(+)
+ create mode 100644 static/redfish/v1/JsonSchemas/OemCrashdump/OemCrashdump.json
+ create mode 100644 static/redfish/v1/JsonSchemas/OemCrashdump/index.json
+ create mode 100644 static/redfish/v1/schema/OemCrashdump_v1.xml
+
+diff --git a/scripts/update_schemas.py b/scripts/update_schemas.py
+index eb6318f..aa92744 100755
+--- a/scripts/update_schemas.py
++++ b/scripts/update_schemas.py
+@@ -226,6 +226,11 @@ with open(metadata_index_path, 'w') as metadata_index:
+ metadata_index.write(" <edmx:Include Namespace=\"OemManager\"/>\n")
+ metadata_index.write(" </edmx:Reference>\n")
+
++ metadata_index.write(
++ " <edmx:Reference Uri=\"/redfish/v1/schema/OemCrashdump_v1.xml\">\n")
++ metadata_index.write(" <edmx:Include Namespace=\"OemCrashdump\"/>\n")
++ metadata_index.write(" </edmx:Reference>\n")
++
+ metadata_index.write(
+ " <edmx:Reference Uri=\""
+ "/redfish/v1/schema/OemComputerSystem_v1.xml\">\n")
+diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml
+index c925581..b0fae5c 100644
+--- a/static/redfish/v1/$metadata/index.xml
++++ b/static/redfish/v1/$metadata/index.xml
+@@ -2502,6 +2502,9 @@
+ <edmx:Reference Uri="/redfish/v1/schema/OemComputerSystem_v1.xml">
+ <edmx:Include Namespace="OemComputerSystem"/>
+ </edmx:Reference>
++ <edmx:Reference Uri="/redfish/v1/schema/OemCrashdump_v1.xml">
++ <edmx:Include Namespace="OemCrashdump"/>
++ </edmx:Reference>
+ <edmx:Reference Uri="/redfish/v1/schema/OemUpdateService_v1.xml">
+ <edmx:Include Namespace="OemUpdateService"/>
+ </edmx:Reference>
+diff --git a/static/redfish/v1/JsonSchemas/OemCrashdump/OemCrashdump.json b/static/redfish/v1/JsonSchemas/OemCrashdump/OemCrashdump.json
+new file mode 100644
+index 0000000..7446e93
+--- /dev/null
++++ b/static/redfish/v1/JsonSchemas/OemCrashdump/OemCrashdump.json
+@@ -0,0 +1,86 @@
++{
++ "$id": "http://redfish.dmtf.org/schemas/v1/OemCrashdump.v1_0_0.json",
++ "$schema": "http://redfish.dmtf.org/schemas/v1/redfish-schema-v1.json",
++ "copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright",
++ "definitions": {
++ "Actions": {
++ "additionalProperties": false,
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "#Crashdump.SendRawPeci": {
++ "$ref": "#/definitions/SendRawPeci"
++ }
++ },
++ "type": "object"
++ },
++ "SendRawPeci": {
++ "additionalProperties": false,
++ "description": "This action is used to send a raw PECI command to the CPU.",
++ "longDescription": "This action is used to send a raw PECI command to the CPU.",
++ "parameters": {
++ "PECICommands": {
++ "description": "This parameter contains the raw PECI command data to be sent to the CPU.",
++ "items": {
++ "type": "integer"
++ },
++ "longDescription": "Single array contains arrays of bytes each represents a single full raw PECI command.",
++ "requiredParameter": true,
++ "type": "array"
++ },
++ "PECIDevice": {
++ "description": "PECI device.",
++ "longDescription": "This represents the PECI device to use for the transaction.",
++ "type": "string"
++ },
++ "RetryTimingUs": {
++ "description": "This parameter can be used to modify the retry timing in the BMC PECI driver.",
++ "items": {
++ "type": "integer"
++ },
++ "longDescription": "An array of three-time values in microseconds as: [retryIntervalMin,retryIntervalMax,retryTimeout].",
++ "type": "array"
++ }
++ },
++ "patternProperties": {
++ "^([a-zA-Z_][a-zA-Z0-9_]*)?@(odata|Redfish|Message)\\.[a-zA-Z_][a-zA-Z0-9_]*$": {
++ "description": "This property shall specify a valid odata or Redfish property.",
++ "type": [
++ "array",
++ "boolean",
++ "integer",
++ "number",
++ "null",
++ "object",
++ "string"
++ ]
++ }
++ },
++ "properties": {
++ "target": {
++ "description": "Link to invoke action",
++ "format": "uri-reference",
++ "type": "string"
++ },
++ "title": {
++ "description": "Friendly action name",
++ "type": "string"
++ }
++ },
++ "type": "object"
++ }
++ },
++ "title": "#OemCrashdump"
++}
+diff --git a/static/redfish/v1/JsonSchemas/OemCrashdump/index.json b/static/redfish/v1/JsonSchemas/OemCrashdump/index.json
+new file mode 100644
+index 0000000..7a19e8e
+--- /dev/null
++++ b/static/redfish/v1/JsonSchemas/OemCrashdump/index.json
+@@ -0,0 +1,20 @@
++{
++ "@odata.context": "/redfish/v1/$metadata#JsonSchemaFile.JsonSchemaFile",
++ "@odata.id": "/redfish/v1/JsonSchemas/OemCrashdump",
++ "@odata.type": "#JsonSchemaFile.v1_0_2.JsonSchemaFile",
++ "Name": "Oem Crashdump Schema File",
++ "Schema": "#OemCrashdump.OemCrashdump",
++ "Description": "Oem Crashdump Schema File Location",
++ "Id": "OemCrashdump",
++ "Languages": [
++ "en"
++ ],
++ "Languages@odata.count": 1,
++ "Location": [
++ {
++ "Language": "en",
++ "Uri": "/redfish/v1/JsonSchemas/OemCrashdump/OemCrashdump.json"
++ }
++ ],
++ "Location@odata.count": 1
++}
+diff --git a/static/redfish/v1/schema/OemCrashdump_v1.xml b/static/redfish/v1/schema/OemCrashdump_v1.xml
+new file mode 100644
+index 0000000..3570408
+--- /dev/null
++++ b/static/redfish/v1/schema/OemCrashdump_v1.xml
+@@ -0,0 +1,33 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
++ <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
++ <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData" />
++ </edmx:Reference>
++
++ <edmx:DataServices>
++ <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OemCrashdump">
++ <edmx:simpleType name="Command">
++ <edmx:restriction base="Collection(Edm.Int64)" />
++ </edmx:simpleType>
++
++ <Action Name="SendRawPeci" IsBound="true">
++ <Parameter Name="PECICommands" Type="Collection(OemCrashdump.Command)" Nullable="false">
++ <Annotation Term="OData.Description" String="This parameter contains the raw PECI command data to be sent to the CPU." />
++ <Annotation Term="OData.LongDescription" String="Single array contains arrays of bytes each represents a single full raw PECI command." />
++ <Annotation Term="Redfish.Required"/>
++ </Parameter>
++ <Parameter Name="PECIDevice" Type="Edm.String" Nullable="false">
++ <Annotation Term="OData.Description" String="PECI device." />
++ <Annotation Term="OData.LongDescription" String="This represents the PECI device to use for the transaction." />
++ </Parameter>
++ <Parameter Name="RetryTimingUs" Type="Edm.Int64" Nullable="false">
++ <Annotation Term="OData.Description" String="This parameter can be used to modify the retry timing in the BMC PECI driver." />
++ <Annotation Term="OData.LongDescription" String="An array of three-time values in microseconds as: [retryIntervalMin,retryIntervalMax,retryTimeout]." />
++ </Parameter>
++ <Annotation Term="OData.Description" String="This action is used to send a raw PECI command to the CPU." />
++ <Annotation Term="OData.LongDescription" String="This action is used to send a raw PECI command to the CPU." />
++ </Action>
++ </Schema>
++ </edmx:DataServices>
++</edmx:Edmx>
++
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch
new file mode 100644
index 000000000..409a2af3f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch
@@ -0,0 +1,874 @@
+From 8b21bce7fa0974c454e4bed539be0475738ee21e Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Fri, 4 Sep 2020 19:24:25 +0800
+Subject: [PATCH] Define Redfish interface "/Registries/Bios" and enable
+ Attributes property
+
+1. Define Redfish interface "/Registries/Bios" for BIOS Attribute Registry
+ RBC Daemon provide method to get BIOS attribute registry.
+2. Eanble Attributes property for BIOS resource
+3. Define Redfish interface "/Systems/system/Bios/Settings" for BIOS
+settings
+4. RBC daemon is at
+https://gerrit.openbmc-project.xyz/#/c/openbmc/bios-settings-mgr/+/35563/
+5. IPMI command implementation is at
+https://gerrit.openbmc-project.xyz/#/c/openbmc/intel-ipmi-oem/+/30827/
+6. Property design is at
+https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/BIOSConfig
+7. Design doc is at
+https://github.com/openbmc/docs/blob/master/designs/remote-bios-configuration.md
+8. There will be 95 test cases for this feature in the validation team.
+
+Tested:
+
+1. Use postman (Redfish tool) could get all the attributes in bios
+resouce, get bios settings, get bios attribute
+registry.
+https://IP_ADDR/redfish/v1/Systems/system/Bios
+{
+ "@Redfish.Settings": {
+ "@odata.type": "#Settings.v1_3_0.Settings",
+ "SettingsObject": {
+ "@odata.id": "/redfish/v1/Systems/system/Bios/Settings"
+ }
+ },
+ "@odata.id": "/redfish/v1/Systems/system/Bios",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "Actions": {
+ "#Bios.ChangePassword": {
+ "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword"
+ },
+ "#Bios.ResetBios": {
+ "target": "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"
+ }
+ },
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "attr0": "current value"
+ },
+ "Description": "BIOS Configuration Service",
+ "Id": "BIOS",
+ "Links": {
+ "ActiveSoftwareImage": {
+ "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ },
+ "SoftwareImages": [
+ {
+ "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ }
+ ],
+ "SoftwareImages@odata.count": 1
+ },
+ "Name": "BIOS Configuration"
+}
+
+Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry
+{
+ "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry",
+ "@odata.type": "#MessageRegistryFile.v1_1_0.MessageRegistryFile",
+ "Description": "BiosAttributeRegistry Message Registry File Location",
+ "Id": "BiosAttributeRegistry",
+ "Languages": [
+ "en"
+ ],
+ "Languages@odata.count": 1,
+ "Location": [
+ {
+ "Language": "en",
+ "Uri": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry"
+ }
+ ],
+ "Location@odata.count": 1,
+ "Name": "BiosAttributeRegistry Message Registry File",
+ "Registry": "BiosAttributeRegistry.1.0.0"
+}
+
+Redfish interface: https://BMCIP/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry
+{
+ "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry",
+ "@odata.type": "#AttributeRegistry.v1_3_2.AttributeRegistry",
+ "Id": "BiosAttributeRegistry",
+ "Language": "en",
+ "Name": "Bios Attribute Registry",
+ "OwningEntity": "OpenBMC",
+ "RegistryEntries": {
+ "Attributes": [
+ {
+ "AttributeName": "attr0",
+ "CurrentValue": "current value",
+ "DefaultValue": "default value",
+ "DisplayName": "display name for attr0",
+ "HelpText": "description for attr0",
+ "MenuPath": "./menu/path/for/attr0",
+ "ReadOnly": false,
+ "Type": "String",
+ "Value": []
+ }
+ ]
+ },
+ "RegistryVersion": "1.0.0"
+}
+
+https://BMC_IPADDR/redfish/v1/Systems/system/Bios/Settings
+{
+ "@odata.id": "/redfish/v1/Systems/system/Bios/Settings",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "QuietBoot": "0x0"
+ },
+ "Id": "BiosSettingsV1",
+ "Name": "Bios Settings Version 1"
+}
+
+2. Passed Validator check for bios resource and bios attribute registry
+*** /redfish/v1/Systems/system/Bios
+INFO - Type (#Bios.v1_1_0.Bios), GET SUCCESS (time: 1.57377)
+INFO - PASS
+*** /redfish/v1/Registries/BiosAttributeRegistry
+INFO - Type (#MessageRegistryFile.v1_1_0.MessageRegistryFile), GET SUCCESS (time: 0.075438)
+INFO - PASS
+INFO -
+*** /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry
+INFO - Type (#AttributeRegistry.v1_3_2.AttributeRegistry), GET SUCCESS (time: 0.075751)
+INFO - PASS
+
+@odata.id /redfish/v1/Systems/system/Bios odata Exists PASS
+@odata.type #Settings.v1_3_0.Settings odata Exists PASS
+Links [JSON Object] Bios.v1_1_0.Links Yes complex
+Links.ActiveSoftwareImage Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active link to: SoftwareInventory Yes PASS
+Links.SoftwareImages Array (size: 1) array of: SoftwareInventory Yes ...
+Links.SoftwareImages[0] Link: /redfish/v1/UpdateService/FirmwareInventory/bios_active SoftwareInventory Yes PASS
+Links.Oem - Resource.Oem No Optional
+SoftwareImages@odata.count 1 odata Exists PASS
+AttributeRegistry BiosAttributeRegistry string Yes PASS
+Actions [JSON Object] Bios.v1_0_0.Actions Yes complex
+Actions.#Bios.ResetBios Action - Yes PASS
+Actions.#Bios.ChangePassword Action - Yes PASS
+Attributes [JSON Object] Bios.v1_0_0.Attributes Yes complex
+Attributes.attr0 current value primitive Yes PASS
+Id BIOS string Yes PASS
+Description BIOS Configuration Service string Yes PASS
+Name BIOS Configuration string Yes PASS
+Oem - Resource.Oem No Optional
+@Redfish.Settings [JSON Object] Settings.Settings Yes complex
+@Redfish.Settings.MaintenanceWindowResource - link to: ItemOrCollection No Optional
+@Redfish.Settings.SupportedApplyTimes - string (enum) No Optional
+@Redfish.Settings.Time - date No Optional
+@Redfish.Settings.ETag - string No Optional
+@Redfish.Settings.SettingsObject Link: /redfish/v1/Systems/system/Bios/Settings link to: Item Yes PASS
+@Redfish.Settings.Messages - Message No Optional
+
+@odata.id /redfish/v1/Registries/BiosAttributeRegistry odata Exists PASS
+@odata.type #MessageRegistryFile.v1_1_0.MessageRegistryFile odata Exists PASS
+Languages@odata.count 1 odata Exists PASS
+Location@odata.count 1 odata Exists PASS
+Actions - MessageRegistryFile.v1_1_0.Actions No Optional
+Languages Array (size: 1) string Yes ...
+Languages[0] en string Yes PASS
+Registry BiosAttributeRegistry.1.0.0 string Yes PASS
+Location Array (size: 1) array of: Location Yes ...
+Location[0] [JSON Object] Location Yes complex
+Location[0].Language en string Yes PASS
+Location[0].Uri /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry string Yes PASS
+Location[0].ArchiveUri - string No Optional
+Location[0].PublicationUri - string No Optional
+Location[0].ArchiveFile - string No Optional
+Id BiosAttributeRegistry string Yes PASS
+Description BiosAttributeRegistry Message Registry File Location string Yes PASS
+Name BiosAttributeRegistry Message Registry File string Yes PASS
+Oem - Resource.Oem No Optional
+
+@odata.id /redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry odata Exists PASS
+@odata.type #AttributeRegistry.v1_3_2.AttributeRegistry odata Exists PASS
+Actions - AttributeRegistry.v1_1_0.Actions No Optional
+Language en string Yes PASS
+RegistryVersion 1.0.0 string Yes PASS
+OwningEntity OpenBMC string Yes PASS
+SupportedSystems - SupportedSystems No Optional
+RegistryEntries [JSON Object] AttributeRegistry.v1_0_0.RegistryEntries Yes complex
+RegistryEntries.Attributes Array (size: 1) array of: Attributes Yes ...
+RegistryEntries.Attributes[0] [JSON Object] Attributes Yes complex
+RegistryEntries.Attributes[0].Oem - Resource.Oem No Optional
+RegistryEntries.Attributes[0].ResetRequired - boolean No Optional
+RegistryEntries.Attributes[0].UefiDevicePath - string No Optional
+RegistryEntries.Attributes[0].UefiKeywordName - string No Optional
+RegistryEntries.Attributes[0].UefiNamespaceId - string No Optional
+RegistryEntries.Attributes[0].AttributeName attr0 string Yes PASS
+RegistryEntries.Attributes[0].Type String string (enum) Yes PASS
+RegistryEntries.Attributes[0].Value Array (size: 0) array of: AttributeValue Yes ...
+RegistryEntries.Attributes[0].DisplayName display name for attr0 string Yes PASS
+RegistryEntries.Attributes[0].HelpText description for attr0 string Yes PASS
+RegistryEntries.Attributes[0].WarningText - string No Optional
+RegistryEntries.Attributes[0].CurrentValue current value primitive Yes PASS
+RegistryEntries.Attributes[0].DefaultValue default value primitive Yes PASS
+RegistryEntries.Attributes[0].DisplayOrder - number No Optional
+RegistryEntries.Attributes[0].MenuPath ./menu/path/for/attr0 string Yes PASS
+RegistryEntries.Attributes[0].ReadOnly False boolean Yes PASS
+RegistryEntries.Attributes[0].WriteOnly - boolean No Optional
+RegistryEntries.Attributes[0].GrayOut - boolean No Optional
+RegistryEntries.Attributes[0].Hidden - boolean No Optional
+RegistryEntries.Attributes[0].Immutable - boolean No Optional
+RegistryEntries.Attributes[0].IsSystemUniqueProperty - boolean No Optional
+RegistryEntries.Attributes[0].MaxLength - number No Optional
+RegistryEntries.Attributes[0].MinLength - number No Optional
+RegistryEntries.Attributes[0].ScalarIncrement - number No Optional
+RegistryEntries.Attributes[0].UpperBound - number No Optional
+RegistryEntries.Attributes[0].LowerBound - number No Optional
+RegistryEntries.Attributes[0].ValueExpression - string No Optional
+RegistryEntries.Menus - Menus No Optional
+RegistryEntries.Dependencies - Dependencies No Optional
+Id BiosAttributeRegistry string Yes PASS
+Description - string No Optional
+Name Bios Attribute Registry string Yes PASS
+Oem - Resource.Oem No Optional
+
+Change-Id: Iecc61018c350f0b8c89df59b2864b941508b1916
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com>
+Signed-off-by: Smriti Ayushi <smriti.ayushi@intel.com>
+---
+ redfish-core/include/redfish.hpp | 3 +
+ .../include/registries/bios_registry.hpp | 40 ++
+ redfish-core/lib/bios.hpp | 511 ++++++++++++++++++
+ redfish-core/lib/message_registries.hpp | 10 +-
+ 4 files changed, 563 insertions(+), 1 deletion(-)
+ create mode 100644 redfish-core/include/registries/bios_registry.hpp
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index a9704d6..e4881ff 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -152,7 +152,10 @@ class RedfishService
+ requestRoutesSystemActionsReset(app);
+ requestRoutesSystemResetActionInfo(app);
+ requestRoutesBiosService(app);
++ requestRoutesBiosSettings(app);
++ requestRoutesBiosAttributeRegistry(app);
+ requestRoutesBiosReset(app);
++ requestRoutesBiosChangePassword(app);
+
+ #ifdef BMCWEB_ENABLE_VM_NBDPROXY
+ requestNBDVirtualMediaRoutes(app);
+diff --git a/redfish-core/include/registries/bios_registry.hpp b/redfish-core/include/registries/bios_registry.hpp
+new file mode 100644
+index 0000000..bcf6b73
+--- /dev/null
++++ b/redfish-core/include/registries/bios_registry.hpp
+@@ -0,0 +1,40 @@
++/*
++// Copyright (c) 2020 Intel Corporation
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++//
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++// Unless required by applicable law or agreed to in writing, software
++// distributed under the License is distributed on an "AS IS" BASIS,
++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++// See the License for the specific language governing permissions and
++// limitations under the License.
++*/
++#pragma once
++#include <registries.hpp>
++
++namespace redfish::registries::bios
++{
++const Header header = {
++ "Copyright 2020 OpenBMC. All rights reserved.",
++ "#MessageRegistry.v1_4_0.MessageRegistry",
++ "BiosAttributeRegistry.1.0.0",
++ "Bios Attribute Registry",
++ "en",
++ "This registry defines the messages for bios attribute registry.",
++ "BiosAttributeRegistry",
++ "1.0.0",
++ "OpenBMC",
++};
++// BiosAttributeRegistry registry is not defined in DMTF, We should use
++// OEM defined registries for this purpose.
++// Below link is wrong - We need to define OEM registries and use
++// appropriate data here.
++constexpr const char* url =
++ "https://redfish.dmtf.org/registries/BiosAttributeRegistry.1.0.0.json";
++
++constexpr std::array<MessageEntry, 0> registry = {};
++} // namespace redfish::registries::bios
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index d8475ae..e92e842 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -3,8 +3,140 @@
+ #include <app.hpp>
+ #include <registries/privilege_registry.hpp>
+ #include <utils/fw_utils.hpp>
++
+ namespace redfish
+ {
++
++/*baseBIOSTable
++map{attributeName,struct{attributeType,readonlyStatus,displayname,
++ description,menuPath,current,default,
++ array{struct{optionstring,optionvalue}}}}
++*/
++using BiosBaseTableType = std::vector<std::pair<
++ std::string,
++ std::tuple<
++ std::string, bool, std::string, std::string, std::string,
++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
++ std::vector<
++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>>;
++using BiosBaseTableItemType = std::pair<
++ std::string,
++ std::tuple<
++ std::string, bool, std::string, std::string, std::string,
++ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
++ std::vector<
++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>;
++using OptionsItemType =
++ std::tuple<std::string, std::variant<int64_t, std::string>>;
++
++enum BiosBaseTableIndex
++{
++ biosBaseAttrType = 0,
++ biosBaseReadonlyStatus,
++ biosBaseDisplayName,
++ biosBaseDescription,
++ biosBaseMenuPath,
++ biosBaseCurrValue,
++ biosBaseDefaultValue,
++ biosBaseOptions
++};
++enum OptionsItemIndex
++{
++ optItemType = 0,
++ optItemValue
++};
++/*
++ The Pending attribute name and new value.
++ ex- { {"QuietBoot",Type.Integer, 0x1},
++ { "DdrFreqLimit",Type.String,"2933"}
++ }
++*/
++using PendingAttributesType = std::vector<std::pair<
++ std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>>;
++using PendingAttributesItemType =
++ std::pair<std::string,
++ std::tuple<std::string, std::variant<int64_t, std::string>>>;
++enum PendingAttributesIndex
++{
++ pendingAttrType = 0,
++ pendingAttrValue
++};
++static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
++{
++ std::string ret;
++ if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager."
++ "AttributeType.Enumeration")
++ {
++ ret = "Enumeration";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.String")
++ {
++ ret = "String";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Password")
++ {
++ ret = "Password";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Integer")
++ {
++ ret = "Integer";
++ }
++ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.Boolean")
++ {
++ ret = "Boolean";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
++static std::string mapBoundTypeToRedfish(const std::string_view typeDbus)
++{
++ std::string ret;
++ if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.ScalarIncrement")
++ {
++ ret = "ScalarIncrement";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.LowerBound")
++ {
++ ret = "LowerBound";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.UpperBound")
++ {
++ ret = "UpperBound";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MinStringLength")
++ {
++ ret = "MinStringLength";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.MaxStringLength")
++ {
++ ret = "MaxStringLength";
++ }
++ else if (typeDbus ==
++ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf")
++ {
++ ret = "OneOf";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
++
+ /**
+ * BiosService class supports handle get method for bios.
+ */
+@@ -23,6 +155,85 @@ inline void
+ // Get the ActiveSoftwareImage and SoftwareImages
+ fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose, "",
+ true);
++
++ asyncResp->res.jsonValue["@Redfish.Settings"] = {
++ {"@odata.type", "#Settings.v1_3_0.Settings"},
++ {"SettingsObject",
++ {{"@odata.id", "/redfish/v1/Systems/system/Bios/Settings"}}}};
++ asyncResp->res.jsonValue["AttributeRegistry"] = "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["Attributes"] = nlohmann::json::object();
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ const std::string& service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const std::variant<BiosBaseTableType>& retBiosTable) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "getBiosAttributes DBUS error: "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ const BiosBaseTableType* baseBiosTable =
++ std::get_if<BiosBaseTableType>(&retBiosTable);
++ nlohmann::json& attributesJson =
++ asyncResp->res.jsonValue["Attributes"];
++ if (baseBiosTable == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const BiosBaseTableItemType& item : *baseBiosTable)
++ {
++ const std::string& key = item.first;
++ const std::string& itemType =
++ std::get<biosBaseAttrType>(item.second);
++ std::string attrType = mapAttrTypeToRedfish(itemType);
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseCurrValue>(item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr ? *currValue : "");
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue = std::get_if<int64_t>(
++ &std::get<biosBaseCurrValue>(item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr ? *currValue : 0);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ }
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager", "BaseBIOSTable");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
+ }
+ inline void requestRoutesBiosService(App& app)
+ {
+@@ -31,6 +242,306 @@ inline void requestRoutesBiosService(App& app)
+ .methods(boost::beast::http::verb::get)(handleBiosServiceGet);
+ }
+
++/**
++ * BiosSettings class supports handle GET/PATCH method for
++ * BIOS configuration pending settings.
++ */
++inline void requestRoutesBiosSettings(App& app)
++{
++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings")
++ .privileges(redfish::privileges::getBios)
++ .methods(boost::beast::http::verb::get)(
++ [](const crow::Request&,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ asyncResp->res.jsonValue["@odata.id"] =
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/Systems/system/Bios/Settings";
++ asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios";
++ asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1";
++ asyncResp->res.jsonValue["Id"] = "BiosSettingsV1";
++ asyncResp->res.jsonValue["AttributeRegistry"] =
++ "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["Attributes"] = {};
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "ObjectMapper::GetObject call failed: "
++ << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ std::string service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const std::variant<PendingAttributesType>&
++ retPendingAttributes) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "getBiosSettings DBUS error: " << ec;
++ messages::resourceNotFound(
++ asyncResp->res, "Systems/system/Bios",
++ "Settings");
++ return;
++ }
++ const PendingAttributesType* pendingAttributes =
++ std::get_if<PendingAttributesType>(
++ &retPendingAttributes);
++ nlohmann::json& attributesJson =
++ asyncResp->res.jsonValue["Attributes"];
++ if (pendingAttributes == nullptr)
++ {
++ BMCWEB_LOG_ERROR
++ << "pendingAttributes == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const PendingAttributesItemType& item :
++ *pendingAttributes)
++ {
++ const std::string& key = item.first;
++ const std::string& itemType =
++ std::get<pendingAttrType>(item.second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<pendingAttrValue>(
++ item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr
++ ? *currValue
++ : "");
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue =
++ std::get_if<int64_t>(
++ &std::get<pendingAttrValue>(
++ item.second));
++ attributesJson.emplace(
++ key, currValue != nullptr
++ ? *currValue
++ : 0);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ }
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "PendingAttributes");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ });
++}
++/**
++ * BiosAttributeRegistry class supports handle get method for BIOS attribute
++ * registry.
++ */
++inline void requestRoutesBiosAttributeRegistry(App& app)
++{
++ BMCWEB_ROUTE(
++ app,
++ "/redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry/")
++ .privileges(redfish::privileges::getBios)
++ .methods(
++ boost::beast::http::verb::
++ get)([](const crow::Request&,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/Registries/BiosAttributeRegistry/"
++ "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#AttributeRegistry.v1_3_2.AttributeRegistry";
++ asyncResp->res.jsonValue["Name"] = "Bios Attribute Registry";
++ asyncResp->res.jsonValue["Id"] = "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["RegistryVersion"] = "1.0.0";
++ asyncResp->res.jsonValue["Language"] = "en";
++ asyncResp->res.jsonValue["OwningEntity"] = "OpenBMC";
++ asyncResp->res.jsonValue["RegistryEntries"]["Attributes"] =
++ nlohmann::json::array();
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "ObjectMapper::GetObject call failed: " << ec;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ std::string service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const std::variant<BiosBaseTableType>&
++ retBiosTable) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "getBiosAttributeRegistry DBUS error: "
++ << ec;
++ messages::resourceNotFound(
++ asyncResp->res, "Registries/Bios", "Bios");
++ return;
++ }
++ const BiosBaseTableType* baseBiosTable =
++ std::get_if<BiosBaseTableType>(&retBiosTable);
++ nlohmann::json& attributeArray =
++ asyncResp->res
++ .jsonValue["RegistryEntries"]["Attributes"];
++ nlohmann::json optionsArray =
++ nlohmann::json::array();
++ if (baseBiosTable == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ for (const BiosBaseTableItemType& item :
++ *baseBiosTable)
++ {
++ const std::string& itemType =
++ std::get<biosBaseAttrType>(item.second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++ if (attrType == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR << "attrType == UNKNOWN";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ nlohmann::json attributeItem;
++ attributeItem["AttributeName"] = item.first;
++ attributeItem["Type"] = attrType;
++ attributeItem["ReadOnly"] =
++ std::get<biosBaseReadonlyStatus>(
++ item.second);
++ attributeItem["DisplayName"] =
++ std::get<biosBaseDisplayName>(item.second);
++ attributeItem["HelpText"] =
++ std::get<biosBaseDescription>(item.second);
++ attributeItem["MenuPath"] =
++ std::get<biosBaseMenuPath>(item.second);
++
++ if (attrType == "String")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseCurrValue>(
++ item.second));
++ const std::string* defValue =
++ std::get_if<std::string>(
++ &std::get<biosBaseDefaultValue>(
++ item.second));
++ attributeItem["CurrentValue"] =
++ currValue != nullptr ? *currValue : "";
++ attributeItem["DefaultValue"] =
++ defValue != nullptr ? *defValue : "";
++ }
++ else if (attrType == "Integer")
++ {
++ const int64_t* currValue =
++ std::get_if<int64_t>(
++ &std::get<biosBaseCurrValue>(
++ item.second));
++ const int64_t* defValue =
++ std::get_if<int64_t>(
++ &std::get<biosBaseDefaultValue>(
++ item.second));
++ attributeItem["CurrentValue"] =
++ currValue != nullptr ? *currValue : 0;
++ attributeItem["DefaultValue"] =
++ defValue != nullptr ? *defValue : 0;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const std::vector<OptionsItemType>&
++ optionsVector =
++ std::get<biosBaseOptions>(item.second);
++ for (const OptionsItemType& optItem :
++ optionsVector)
++ {
++ nlohmann::json optItemJson;
++ const std::string& strOptItemType =
++ std::get<optItemType>(optItem);
++ std::string optItemTypeRedfish =
++ mapBoundTypeToRedfish(strOptItemType);
++ if (optItemTypeRedfish == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR
++ << "optItemTypeRedfish == UNKNOWN";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ if (optItemTypeRedfish == "OneOf")
++ {
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<optItemValue>(
++ optItem));
++ optItemJson[optItemTypeRedfish] =
++ currValue != nullptr ? *currValue
++ : "";
++ }
++ else
++ {
++ const int64_t* currValue =
++ std::get_if<int64_t>(
++ &std::get<optItemValue>(
++ optItem));
++ optItemJson[optItemTypeRedfish] =
++ currValue != nullptr ? *currValue
++ : 0;
++ }
++
++ optionsArray.push_back(optItemJson);
++ }
++
++ attributeItem["Value"] = optionsArray;
++ attributeArray.push_back(attributeItem);
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "BaseBIOSTable");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ });
++}
++
+ /**
+ * BiosReset class supports handle POST method for Reset bios.
+ * The class retrieves and sends data directly to D-Bus.
+diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp
+index c030e5a..d76f70b 100644
+--- a/redfish-core/lib/message_registries.hpp
++++ b/redfish-core/lib/message_registries.hpp
+@@ -17,6 +17,7 @@
+
+ #include "registries.hpp"
+ #include "registries/base_message_registry.hpp"
++#include "registries/bios_registry.hpp"
+ #include "registries/openbmc_message_registry.hpp"
+ #include "registries/resource_event_message_registry.hpp"
+ #include "registries/task_event_message_registry.hpp"
+@@ -128,7 +129,6 @@ inline void handleMessageRegistryGet(
+ const crow::Request& /*req*/,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& registry, const std::string& registryMatch)
+-
+ {
+ const registries::Header* header = nullptr;
+ std::vector<const registries::MessageEntry*> registryEntries;
+@@ -167,6 +167,14 @@ inline void handleMessageRegistryGet(
+ registryEntries.emplace_back(&entry);
+ }
+ }
++ else if (registry == "BiosAttributeRegistry")
++ {
++ header = &registries::bios::header;
++ for (const registries::MessageEntry& entry : registries::bios::registry)
++ {
++ registryEntries.emplace_back(&entry);
++ }
++ }
+ else
+ {
+ messages::resourceNotFound(
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch
new file mode 100644
index 000000000..a52a1614f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch
@@ -0,0 +1,148 @@
+From ffa924ef204930a5bb442bf654eac02543acfb8f Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Wed, 30 Jun 2021 15:18:46 +0000
+Subject: [PATCH 2/5] BaseBiosTable: Add support for PATCH operation
+
+This commit brings in support for PATCH operation of the
+bios variables that updates the BaseBiosTable.
+
+Tested-By:
+* Passed Redfish validator
+
+* Single Attribute:
+PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d
+'{"data":[{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>}]}'
+
+* Multiple Attributes:
+PATCH https://${bmc}/redfish/v1/Systems/system/Bios/Settings -d
+'{"data":[{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>},
+{"AttributeName": <attribute name>, "AttributeType":
+<attribute type>, "AttributeValue": <attribute value>}]}'
+
+This makes use of the "Set" of "PendingAttributes" in the
+backend and that updates the BaseBiosTable.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+Change-Id: I12e78e5ac623c264c7a3e1dd5198aca67172736d
+---
+ redfish-core/lib/bios.hpp | 95 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 95 insertions(+)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index c1a5c56..14d2171 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -96,6 +96,29 @@ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
+
+ return ret;
+ }
++static std::string mapRedfishToAttrType(const std::string_view type)
++{
++ std::string ret;
++ if (type == "string")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
++ }
++ else if (type == "int")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer";
++ }
++ else if (type == "enum")
++ {
++ ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
++ "Enumeration";
++ }
++ else
++ {
++ ret = "UNKNOWN";
++ }
++
++ return ret;
++}
+ static std::string mapBoundTypeToRedfish(const std::string_view typeDbus)
+ {
+ std::string ret;
+@@ -370,6 +393,78 @@ inline void requestRoutesBiosSettings(App& app)
+ "/xyz/openbmc_project/bios_config/manager",
+ std::array<const char*, 0>());
+ });
++
++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings")
++ .privileges({{"ConfigureComponents"}})
++ .methods(boost::beast::http::verb::patch)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ nlohmann::json inpJson;
++
++ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "data",
++ inpJson))
++ {
++ return;
++ }
++
++ for (auto& attrInfo : inpJson)
++ {
++ std::optional<std::string> attrName;
++ std::optional<std::string> attrType;
++ std::optional<std::string> attrValue;
++ if (!json_util::getValueFromJsonObject(
++ attrInfo, "AttributeName", attrName))
++ {
++ messages::propertyMissing(asyncResp->res,
++ "AttributeName");
++ return;
++ }
++ if (!json_util::getValueFromJsonObject(
++ attrInfo, "AttributeType", attrType))
++ {
++ messages::propertyMissing(asyncResp->res,
++ "AttributeType");
++ return;
++ }
++ if (!json_util::getValueFromJsonObject(
++ attrInfo, "AttributeValue", attrValue))
++ {
++ messages::propertyMissing(asyncResp->res,
++ "AttributeValue");
++ return;
++ }
++ std::string biosAttrType = mapRedfishToAttrType(*attrType);
++
++ if (biosAttrType == "UNKNOWN")
++ {
++ BMCWEB_LOG_ERROR << "Invalid attribute type";
++ messages::propertyValueNotInList(
++ asyncResp->res, "AttributeType", *attrType);
++ return;
++ }
++
++ PendingAttributesType pendingAttributes;
++ pendingAttributes.emplace_back(std::make_pair(
++ *attrName, std::make_tuple(biosAttrType, *attrValue)));
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "doPatch resp_handler got error " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ },
++ "xyz.openbmc_project.BIOSConfigManager",
++ "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "PendingAttributes",
++ std::variant<PendingAttributesType>(pendingAttributes));
++ }
++ });
+ }
+ /**
+ * BiosAttributeRegistry class supports handle get method for BIOS attribute
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch
new file mode 100644
index 000000000..5ed92cc3e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0003-Add-support-to-ResetBios-action.patch
@@ -0,0 +1,53 @@
+From b7adca60dd69ac9566dc8f417065e244198fc711 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Wed, 6 Oct 2021 22:27:20 +0000
+Subject: [PATCH] Add support to ResetBios action
+
+Tested:
+
+Bios reset flag can be modified throw redfish
+POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios
+
+Change-Id: Ic719c55705e5f634539b3dd858b60922e505a8d0
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+---
+ redfish-core/lib/bios.hpp | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index f5aa7b7..f613613 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -648,17 +648,23 @@ inline void
+ handleBiosResetPost(const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+ {
++ std::string resetFlag =
++ "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.FactoryDefaults";
++
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "Failed to reset bios: " << ec;
++ BMCWEB_LOG_ERROR << "doPost bios reset got error " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ },
+- "org.open_power.Software.Host.Updater", "/xyz/openbmc_project/software",
+- "xyz.openbmc_project.Common.FactoryReset", "Reset");
++ "xyz.openbmc_project.BIOSConfigManager",
++ "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.BIOSConfig.Manager", "ResetBIOSSettings",
++ std::variant<std::string>(resetFlag));
+ }
+
+ inline void requestRoutesBiosReset(App& app)
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch
new file mode 100644
index 000000000..de705b60b
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0004-Add-support-to-ChangePassword-action.patch
@@ -0,0 +1,117 @@
+From 22956921a228f6f1cbbbd3045a3cc3969732dca3 Mon Sep 17 00:00:00 2001
+From: Arun Lal K M <arun.lal@intel.com>
+Date: Fri, 8 Oct 2021 20:56:00 +0000
+Subject: [PATCH] Add support to ChangePassword action
+
+Tested:
+
+Passed Redfish validator.
+Bios change password:
+root@intel-obmc:~# cat /var/lib/bios-settings-manager/seedData
+{
+"UserPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
+"AdminPwdHash": "08D91157785366CDC3AA64D87E5E3C621EDAB13E26B6E484397EBA5E459E54C567BF5B1FFB36A43B6142B18F8D642E9D",
+"Seed": "123456",
+"HashAlgo": "SHA384"
+}
+POST https://IP_ADDR/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword
+{
+ "NewPassword": "12345678",
+ "OldPassword": "1234567890",
+ "PasswordName": "Administrator"
+}
+root@intel-obmc:~# cat /var/lib/bios-settings-manager/passwordData
+{
+ "CurrentPassword": "1234567890",
+ "IsAdminPwdChanged": 1,
+ "IsUserPwdChanged": 0,
+ "NewPassword": "2DD65D57EB60B1D92C5F3D2DC84724FCEE7BC02E57AA75E834712266ED94CAC704047B2FF7CEC1C36BED280B36BB5AC6",
+ "UserName": "Administrator"
+}
+
+Signed-off-by: Arun Lal K M <arun.lal@intel.com>
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ redfish-core/lib/bios.hpp | 59 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index f613613..b06a904 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -175,6 +175,10 @@ inline void
+ asyncResp->res.jsonValue["Actions"]["#Bios.ResetBios"] = {
+ {"target", "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"}};
+
++ asyncResp->res.jsonValue["Actions"]["#Bios.ChangePassword"] = {
++ {"target", "/redfish/v1/Systems/system/Bios/Actions/"
++ "Bios.ChangePassword"}};
++
+ // Get the ActiveSoftwareImage and SoftwareImages
+ fw_util::populateFirmwareInformation(asyncResp, fw_util::biosPurpose, "",
+ true);
+@@ -265,6 +269,61 @@ inline void requestRoutesBiosService(App& app)
+ .methods(boost::beast::http::verb::get)(handleBiosServiceGet);
+ }
+
++/**
++ * BiosChangePassword class supports handle POST method for change bios
++ * password. The class retrieves and sends data directly to D-Bus.
++ */
++inline void requestRoutesBiosChangePassword(App& app)
++{
++ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/")
++ .privileges({{"ConfigureComponents"}})
++ .methods(boost::beast::http::verb::post)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ std::string currentPassword, newPassword, userName;
++ if (!json_util::readJsonPatch(req, asyncResp->res, "NewPassword",
++ newPassword, "OldPassword",
++ currentPassword, "PasswordName",
++ userName))
++ {
++ return;
++ }
++ if (currentPassword.empty())
++ {
++ messages::actionParameterUnknown(
++ asyncResp->res, "ChangePassword", "OldPassword");
++ return;
++ }
++ if (newPassword.empty())
++ {
++ messages::actionParameterUnknown(
++ asyncResp->res, "ChangePassword", "NewPassword");
++ return;
++ }
++ if (userName.empty())
++ {
++ messages::actionParameterUnknown(
++ asyncResp->res, "ChangePassword", "PasswordName");
++ return;
++ }
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_CRITICAL
++ << "Failed in doPost(BiosChangePassword) "
++ << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ },
++ "xyz.openbmc_project.BIOSConfigPassword",
++ "/xyz/openbmc_project/bios_config/password",
++ "xyz.openbmc_project.BIOSConfig.Password", "ChangePassword",
++ userName, currentPassword, newPassword);
++ });
++}
++
+ /**
+ * BiosSettings class supports handle GET/PATCH method for
+ * BIOS configuration pending settings.
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch
new file mode 100644
index 000000000..26393bfee
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch
@@ -0,0 +1,46 @@
+From edc6925e8c0d9f60da1f70c524261efaf05b2710 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Wed, 30 Jun 2021 15:42:06 +0000
+Subject: [PATCH 5/5] Fix:remove bios user pwd change option via Redfish
+
+BMC should not provide user bios setup password change option via
+Redfish as per bios security requirements. Only Admin BIOS setup
+password is supported.
+
+Added check for the password name action parameter and
+do not allow if it has User Password value from redfish side.
+
+Tested: sent POST query in redfish on URI:
+https://<ip>/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword
+error occurs for UserPassword parameter and allows for AdminPassword.
+
+Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com>
+Change-Id: I169cc6a4f786625d9e8b8dfe56816d52b1740f4c
+---
+ redfish-core/lib/bios.hpp | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index 0250c59..360a749 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -323,6 +323,16 @@ inline void requestRoutesBiosChangePassword(App& app)
+ asyncResp->res, "ChangePassword", "PasswordName");
+ return;
+ }
++
++ // In Intel BIOS, we are not supporting user password in BIOS
++ // setup
++ if (userName == "UserPassword")
++ {
++ messages::actionParameterUnknown(
++ asyncResp->res, "ChangePassword", "PasswordName");
++ return;
++ }
++
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch
new file mode 100644
index 000000000..05257bbf7
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch
@@ -0,0 +1,963 @@
+From bedec07809e2d2ecfed9db0d9509d2380ea094b7 Mon Sep 17 00:00:00 2001
+From: Arun Lal K M <arun.lal@intel.com>
+Date: Wed, 19 Jan 2022 14:44:07 +0000
+Subject: [PATCH] Add fix for broken feature 'Pending Attributes'.
+
+Fix is added for the following:
+1) GET to 'redfish/v1/Systems/system/Bios'.
+2) PATCH to 'redfish/v1/Systems/system/Bios/Settings'.
+3) GET to 'redfish/v1/Systems/system/Bios/Settings'.
+4) Fix for incremental duplicate values in BiosAttributeRegistry.
+5) POST to '/redfish/v1/Systems/system/Bios/Actions
+ /Bios.ChangePassword/'.
+6) Add support for Enumeration.
+7) Support DMTF standard of displaying "Value" in BIOS attribute registries.
+
+Tested:
+By giving PATCH to 'redfish/v1/Systems/system/Bios/Settings'
+PATCH command raw data:
+{
+ "data":{
+ "AmpPrefetchEnable": "0x1",
+ "Ce2LmLoggingEn": "0x1",
+ "DfxEadrDebugLogs": "0x2",
+ "PsfUrEnable": "0x1",
+ "ATS": "0x0"
+ }
+}
+
+Response:
+{
+ "@Message.ExtendedInfo": [
+ {
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "Successfully Completed Request",
+ "MessageArgs": [],
+ "MessageId": "Base.1.8.1.Success",
+ "MessageSeverity": "OK",
+ "Resolution": "None"
+ }
+ ]
+}
+
+By giving GET to 'redfish/v1/Systems/system/Bios'
+Response:
+{
+ "@Redfish.Settings": {
+ "@odata.type": "#Settings.v1_3_0.Settings",
+ "SettingsObject": {
+ "@odata.id":
+ "/redfish/v1/Systems/system/Bios/Settings"
+ }
+ },
+ "@odata.id": "/redfish/v1/Systems/system/Bios",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "Actions": {
+ "#Bios.ChangePassword": {
+ "target":
+ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword"
+ },
+ "#Bios.ResetBios": {
+ "target":
+ "/redfish/v1/Systems/system/Bios/Actions/Bios.ResetBios"
+ }
+ },
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "AEPErrorInjEn": "0x00",
+ "ARIEnable": "0x01",
+ "ARIForward": "0x00",
+ ...
+ ...
+ ...
+ "txEqCalibration": "0x01",
+ "volMemMode": "0x00",
+ "wrVrefCenter": "0x01"
+ },
+ "Description": "BIOS Configuration Service",
+ "Id": "BIOS",
+ "Links": {
+ "ActiveSoftwareImage": {
+ "@odata.id":
+ "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ },
+ "SoftwareImages": [
+ {
+ "@odata.id":
+ "/redfish/v1/UpdateService/FirmwareInventory/bios_active"
+ }
+ ],
+ "SoftwareImages@odata.count": 1
+ },
+ "Name": "BIOS Configuration"
+}
+
+By giving GET to 'redfish/v1/Systems/system/Bios/Settings'
+Response:
+{
+ "@odata.id": "/redfish/v1/Systems/system/Bios/Settings",
+ "@odata.type": "#Bios.v1_1_0.Bios",
+ "AttributeRegistry": "BiosAttributeRegistry",
+ "Attributes": {
+ "ATS": "0x0",
+ "AmpPrefetchEnable": "0x1",
+ "Ce2LmLoggingEn": "0x1",
+ "DfxEadrDebugLogs": "0x2",
+ "PsfUrEnable": "0x1"
+ },
+ "Id": "BiosSettingsV1",
+ "Name": "Bios Settings Version 1"
+}
+
+By giving POST to '/redfish/v1/Systems/system/Bios/Actions
+/Bios.ChangePassword/'
+Response: Success
+
+By running Redfish-Service-Validator
+Result:
+Elapsed time: 0:09:36
+invalidPropertyValue: 108
+metadataNamespaces: 2185
+missingNamespaces: 1
+optionalAction: 9
+pass: 13772
+passAction: 22
+passGet: 541
+reflink: 1
+repeat: 47
+serviceNamespaces: 75
+skipOptional: 9276
+unverifiedComplexAdditional: 1
+warnDeprecated: 230
+warningPresent: 54
+Validation has succeeded.
+
+Signed-off-by: Arun Lal K M <arun.lal@intel.com>
+Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com>
+---
+ redfish-core/lib/bios.hpp | 605 +++++++++++++++++++++++++-------------
+ 1 file changed, 394 insertions(+), 211 deletions(-)
+
+diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp
+index bbf9460..c3192dd 100644
+--- a/redfish-core/lib/bios.hpp
++++ b/redfish-core/lib/bios.hpp
+@@ -12,13 +12,15 @@ map{attributeName,struct{attributeType,readonlyStatus,displayname,
+ description,menuPath,current,default,
+ array{struct{optionstring,optionvalue}}}}
+ */
+-using BiosBaseTableType = std::vector<std::pair<
++
++using BiosBaseTableType = boost::container::flat_map<
+ std::string,
+ std::tuple<
+ std::string, bool, std::string, std::string, std::string,
+ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
+ std::vector<
+- std::tuple<std::string, std::variant<int64_t, std::string>>>>>>;
++ std::tuple<std::string, std::variant<int64_t, std::string>>>>>;
++
+ using BiosBaseTableItemType = std::pair<
+ std::string,
+ std::tuple<
+@@ -29,6 +31,13 @@ using BiosBaseTableItemType = std::pair<
+ using OptionsItemType =
+ std::tuple<std::string, std::variant<int64_t, std::string>>;
+
++using PendingAttributesType = boost::container::flat_map<
++ std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>;
++
++using PendingAttributesItemType =
++ std::pair<std::string,
++ std::tuple<std::string, std::variant<int64_t, std::string>>>;
++
+ enum BiosBaseTableIndex
+ {
+ biosBaseAttrType = 0,
+@@ -45,17 +54,7 @@ enum OptionsItemIndex
+ optItemType = 0,
+ optItemValue
+ };
+-/*
+- The Pending attribute name and new value.
+- ex- { {"QuietBoot",Type.Integer, 0x1},
+- { "DdrFreqLimit",Type.String,"2933"}
+- }
+-*/
+-using PendingAttributesType = std::vector<std::pair<
+- std::string, std::tuple<std::string, std::variant<int64_t, std::string>>>>;
+-using PendingAttributesItemType =
+- std::pair<std::string,
+- std::tuple<std::string, std::variant<int64_t, std::string>>>;
++
+ enum PendingAttributesIndex
+ {
+ pendingAttrType = 0,
+@@ -64,30 +63,20 @@ enum PendingAttributesIndex
+ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
+ {
+ std::string ret;
+- if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager."
+- "AttributeType.Enumeration")
+- {
+- ret = "Enumeration";
+- }
+- else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
+- "Manager.AttributeType.String")
++ if (typeDbus == "xyz.openbmc_project.BIOSConfig."
++ "Manager.AttributeType.String")
+ {
+ ret = "String";
+ }
+- else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
+- "Manager.AttributeType.Password")
+- {
+- ret = "Password";
+- }
+ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
+ "Manager.AttributeType.Integer")
+ {
+ ret = "Integer";
+ }
+ else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
+- "Manager.AttributeType.Boolean")
++ "Manager.AttributeType.Enumeration")
+ {
+- ret = "Boolean";
++ ret = "Enumeration";
+ }
+ else
+ {
+@@ -96,29 +85,7 @@ static std::string mapAttrTypeToRedfish(const std::string_view typeDbus)
+
+ return ret;
+ }
+-static std::string mapRedfishToAttrType(const std::string_view type)
+-{
+- std::string ret;
+- if (type == "string")
+- {
+- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
+- }
+- else if (type == "int")
+- {
+- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer";
+- }
+- else if (type == "enum")
+- {
+- ret = "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
+- "Enumeration";
+- }
+- else
+- {
+- ret = "UNKNOWN";
+- }
+
+- return ret;
+-}
+ static std::string mapBoundTypeToRedfish(const std::string_view typeDbus)
+ {
+ std::string ret;
+@@ -201,6 +168,15 @@ inline void
+
+ return;
+ }
++
++ if (getObjectType.empty())
++ {
++ BMCWEB_LOG_ERROR << "getObjectType is empty.";
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++
+ const std::string& service = getObjectType.begin()->first;
+
+ crow::connections::systemBus->async_method_call(
+@@ -220,7 +196,7 @@ inline void
+ asyncResp->res.jsonValue["Attributes"];
+ if (baseBiosTable == nullptr)
+ {
+- BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
++ BMCWEB_LOG_ERROR << "baseBiosTable is empty";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+@@ -230,7 +206,7 @@ inline void
+ const std::string& itemType =
+ std::get<biosBaseAttrType>(item.second);
+ std::string attrType = mapAttrTypeToRedfish(itemType);
+- if (attrType == "String")
++ if (attrType == "String" || attrType == "Enumeration")
+ {
+ const std::string* currValue =
+ std::get_if<std::string>(
+@@ -248,7 +224,6 @@ inline void
+ else
+ {
+ BMCWEB_LOG_ERROR << "Unsupported attribute type.";
+- messages::internalError(asyncResp->res);
+ }
+ }
+ },
+@@ -275,8 +250,9 @@ inline void requestRoutesBiosService(App& app)
+ */
+ inline void requestRoutesBiosChangePassword(App& app)
+ {
+- BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/")
+- .privileges({{"ConfigureComponents"}})
++ BMCWEB_ROUTE(app,
++ "/redfish/v1/Systems/system/Bios/Actions/Bios.ChangePassword/")
++ .privileges(redfish::privileges::postBios)
+ .methods(boost::beast::http::verb::post)(
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+@@ -342,180 +318,303 @@ inline void requestRoutesBiosSettings(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings")
+ .privileges(redfish::privileges::getBios)
+- .methods(boost::beast::http::verb::get)(
+- [](const crow::Request&,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+- asyncResp->res.jsonValue["@odata.id"] =
+- asyncResp->res.jsonValue["@odata.id"] =
+- "/redfish/v1/Systems/system/Bios/Settings";
+- asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios";
+- asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1";
+- asyncResp->res.jsonValue["Id"] = "BiosSettingsV1";
+- asyncResp->res.jsonValue["AttributeRegistry"] =
+- "BiosAttributeRegistry";
+- asyncResp->res.jsonValue["Attributes"] = {};
++ .methods(
++ boost::beast::http::verb::
++ get)([](const crow::Request&,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/Systems/system/Bios/Settings";
++ asyncResp->res.jsonValue["@odata.type"] = "#Bios.v1_1_0.Bios";
++ asyncResp->res.jsonValue["Name"] = "Bios Settings Version 1";
++ asyncResp->res.jsonValue["Id"] = "BiosSettingsV1";
++ asyncResp->res.jsonValue["AttributeRegistry"] =
++ "BiosAttributeRegistry";
++ asyncResp->res.jsonValue["Attributes"] = {};
+
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec,
+- const GetObjectType& getObjectType) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR
+- << "ObjectMapper::GetObject call failed: "
+- << ec;
+- messages::internalError(asyncResp->res);
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "ObjectMapper::GetObject call failed: " << ec;
++ messages::internalError(asyncResp->res);
+
+- return;
+- }
+- std::string service = getObjectType.begin()->first;
+-
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](
+- const boost::system::error_code ec,
+- const std::variant<PendingAttributesType>&
+- retPendingAttributes) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR
+- << "getBiosSettings DBUS error: " << ec;
+- messages::resourceNotFound(
+- asyncResp->res, "Systems/system/Bios",
+- "Settings");
+- return;
+- }
+- const PendingAttributesType* pendingAttributes =
+- std::get_if<PendingAttributesType>(
+- &retPendingAttributes);
+- nlohmann::json& attributesJson =
+- asyncResp->res.jsonValue["Attributes"];
+- if (pendingAttributes == nullptr)
+- {
+- BMCWEB_LOG_ERROR
+- << "pendingAttributes == nullptr ";
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- for (const PendingAttributesItemType& item :
+- *pendingAttributes)
++ return;
++ }
++
++ if (getObjectType.empty())
++ {
++ BMCWEB_LOG_ERROR << "getObjectType is empty.";
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++
++ std::string service = getObjectType.begin()->first;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec,
++ const std::variant<PendingAttributesType>&
++ retPendingAttributes) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "getBiosSettings DBUS error: " << ec;
++ messages::resourceNotFound(
++ asyncResp->res, "Systems/system/Bios",
++ "Settings");
++ return;
++ }
++
++ const PendingAttributesType* pendingAttributes =
++ std::get_if<PendingAttributesType>(
++ &retPendingAttributes);
++ nlohmann::json& attributesJson =
++ asyncResp->res.jsonValue["Attributes"];
++ if (pendingAttributes == nullptr)
++ {
++ BMCWEB_LOG_ERROR
++ << "pendingAttributes is empty";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ for (const PendingAttributesItemType&
++ pendingAttributesItem : *pendingAttributes)
++ {
++ const std::string& biosAttrType =
++ std::get<pendingAttrType>(
++ pendingAttributesItem.second);
++
++ std::string itemType =
++ mapAttrTypeToRedfish(biosAttrType);
++
++ if (itemType == "String" ||
++ itemType == "Enumeration")
+ {
+- const std::string& key = item.first;
+- const std::string& itemType =
+- std::get<pendingAttrType>(item.second);
+- std::string attrType =
+- mapAttrTypeToRedfish(itemType);
+- if (attrType == "String")
+- {
+- const std::string* currValue =
+- std::get_if<std::string>(
+- &std::get<pendingAttrValue>(
+- item.second));
+- attributesJson.emplace(
+- key, currValue != nullptr
+- ? *currValue
+- : "");
+- }
+- else if (attrType == "Integer")
++ const std::string* currValue =
++ std::get_if<std::string>(
++ &std::get<pendingAttrValue>(
++ pendingAttributesItem.second));
++
++ if (!currValue)
+ {
+- const int64_t* currValue =
+- std::get_if<int64_t>(
+- &std::get<pendingAttrValue>(
+- item.second));
+- attributesJson.emplace(
+- key, currValue != nullptr
+- ? *currValue
+- : 0);
++ BMCWEB_LOG_ERROR
++ << "No string data in pending "
++ "attributes item data";
++ messages::internalError(asyncResp->res);
++ return;
+ }
+- else
++
++ attributesJson.emplace(
++ pendingAttributesItem.first,
++ *currValue);
++ }
++ else if (itemType == "Integer")
++ {
++ const int64_t* currValue =
++ std::get_if<int64_t>(
++ &std::get<pendingAttrValue>(
++ pendingAttributesItem.second));
++
++ if (!currValue)
+ {
+ BMCWEB_LOG_ERROR
+- << "Unsupported attribute type.";
++ << "No int64_t data in pending "
++ "attributes item data";
+ messages::internalError(asyncResp->res);
++ return;
+ }
++
++ attributesJson.emplace(
++ pendingAttributesItem.first,
++ *currValue);
+ }
+- },
+- service, "/xyz/openbmc_project/bios_config/manager",
+- "org.freedesktop.DBus.Properties", "Get",
+- "xyz.openbmc_project.BIOSConfig.Manager",
+- "PendingAttributes");
+- },
+- "xyz.openbmc_project.ObjectMapper",
+- "/xyz/openbmc_project/object_mapper",
+- "xyz.openbmc_project.ObjectMapper", "GetObject",
+- "/xyz/openbmc_project/bios_config/manager",
+- std::array<const char*, 0>());
+- });
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "Unsupported attribute type.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ }
++ },
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "PendingAttributes");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ });
+
+ BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Bios/Settings")
+- .privileges({{"ConfigureComponents"}})
+- .methods(boost::beast::http::verb::patch)(
+- [](const crow::Request& req,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+- nlohmann::json inpJson;
++ .privileges(redfish::privileges::patchBios)
++ .methods(
++ boost::beast::http::verb::
++ patch)([](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ nlohmann::json inpJson;
+
+- if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "data",
+- inpJson))
+- {
+- return;
+- }
++ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "data",
++ inpJson))
++ {
++ BMCWEB_LOG_ERROR << "No 'data' in req!";
++ return;
++ }
+
+- for (auto& attrInfo : inpJson)
+- {
+- std::optional<std::string> attrName;
+- std::optional<std::string> attrType;
+- std::optional<std::string> attrValue;
+- if (!json_util::getValueFromJsonObject(
+- attrInfo, "AttributeName", attrName))
+- {
+- messages::propertyMissing(asyncResp->res,
+- "AttributeName");
+- return;
+- }
+- if (!json_util::getValueFromJsonObject(
+- attrInfo, "AttributeType", attrType))
+- {
+- messages::propertyMissing(asyncResp->res,
+- "AttributeType");
+- return;
+- }
+- if (!json_util::getValueFromJsonObject(
+- attrInfo, "AttributeValue", attrValue))
++ if (inpJson.empty())
++ {
++ messages::invalidObject(asyncResp->res,
++ crow::utility::urlFromPieces("data"));
++ BMCWEB_LOG_ERROR << "No input in req!";
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, inpJson](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
+ {
+- messages::propertyMissing(asyncResp->res,
+- "AttributeValue");
++ BMCWEB_LOG_ERROR
++ << "ObjectMapper::GetObject call failed: " << ec;
++ messages::internalError(asyncResp->res);
++
+ return;
+ }
+- std::string biosAttrType = mapRedfishToAttrType(*attrType);
+
+- if (biosAttrType == "UNKNOWN")
++ if (getObjectType.empty())
+ {
+- BMCWEB_LOG_ERROR << "Invalid attribute type";
+- messages::propertyValueNotInList(
+- asyncResp->res, "AttributeType", *attrType);
++ BMCWEB_LOG_ERROR << "getObjectType is empty.";
++ messages::internalError(asyncResp->res);
++
+ return;
+ }
+
+- PendingAttributesType pendingAttributes;
+- pendingAttributes.emplace_back(std::make_pair(
+- *attrName, std::make_tuple(biosAttrType, *attrValue)));
++ std::string service = getObjectType.begin()->first;
+
+ crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
++ [asyncResp,
++ inpJson](const boost::system::error_code ec,
++ const std::variant<BiosBaseTableType>&
++ retBiosTable) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR
+- << "doPatch resp_handler got error " << ec;
++ << "getBiosAttributes DBUS error: " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ const BiosBaseTableType* baseBiosTable =
++ std::get_if<BiosBaseTableType>(&retBiosTable);
++
++ if (baseBiosTable == nullptr)
++ {
++ BMCWEB_LOG_ERROR << "baseBiosTable is empty.";
+ messages::internalError(asyncResp->res);
+ return;
+ }
++
++ PendingAttributesType pendingAttributes{};
++
++ for (nlohmann::detail::iteration_proxy_value<
++ nlohmann::detail::iter_impl<
++ const nlohmann::basic_json<>>>&
++ attributes : inpJson.items())
++ {
++ BiosBaseTableType::const_iterator knobIter =
++ baseBiosTable->find(attributes.key());
++ if (knobIter == baseBiosTable->end())
++ {
++ BMCWEB_LOG_ERROR << "Cannot find "
++ << attributes.key()
++ << " in baseBiosTable";
++ messages::propertyValueNotInList(
++ asyncResp->res, attributes.key(),
++ "data");
++ return;
++ }
++
++ const std::string& itemType =
++ std::get<biosBaseAttrType>(
++ knobIter->second);
++ std::string attrType =
++ mapAttrTypeToRedfish(itemType);
++
++ if (attrType == "String" ||
++ attrType == "Enumeration")
++ {
++ std::string val = attributes.value();
++
++ pendingAttributes.emplace(
++ attributes.key(),
++ std::make_tuple(itemType, val));
++ }
++ else if (attrType == "Integer")
++ {
++ pendingAttributes.emplace(
++ attributes.key(),
++ std::make_tuple(
++ itemType, static_cast<int64_t>(
++ attributes.value())));
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == "
++ << itemType;
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++ }
++
++ if (pendingAttributes.empty())
++ {
++ BMCWEB_LOG_ERROR
++ << "pendingAttributes is empty.";
++ messages::invalidObject(
++ asyncResp->res,
++ crow::utility::urlFromPieces("data"));
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](
++ const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR
++ << "doPatch resp_handler got error "
++ << ec << "\n";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ messages::success(asyncResp->res);
++ },
++ "xyz.openbmc_project.BIOSConfigManager",
++ "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.BIOSConfig.Manager",
++ "PendingAttributes",
++ std::variant<PendingAttributesType>(
++ pendingAttributes));
+ },
+- "xyz.openbmc_project.BIOSConfigManager",
+- "/xyz/openbmc_project/bios_config/manager",
+- "org.freedesktop.DBus.Properties", "Set",
++ service, "/xyz/openbmc_project/bios_config/manager",
++ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.BIOSConfig.Manager",
+- "PendingAttributes",
+- std::variant<PendingAttributesType>(pendingAttributes));
+- }
+- });
++ "BaseBIOSTable");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/bios_config/manager",
++ std::array<const char*, 0>());
++ });
+ }
+ /**
+ * BiosAttributeRegistry class supports handle get method for BIOS attribute
+@@ -555,6 +654,15 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+
+ return;
+ }
++
++ if (getObjectType.empty())
++ {
++ BMCWEB_LOG_ERROR << "getObjectType is empty.";
++ messages::internalError(asyncResp->res);
++
++ return;
++ }
++
+ std::string service = getObjectType.begin()->first;
+
+ crow::connections::systemBus->async_method_call(
+@@ -575,8 +683,6 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ nlohmann::json& attributeArray =
+ asyncResp->res
+ .jsonValue["RegistryEntries"]["Attributes"];
+- nlohmann::json optionsArray =
+- nlohmann::json::array();
+ if (baseBiosTable == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "baseBiosTable == nullptr ";
+@@ -592,10 +698,11 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ mapAttrTypeToRedfish(itemType);
+ if (attrType == "UNKNOWN")
+ {
+- BMCWEB_LOG_ERROR << "attrType == UNKNOWN";
+- messages::internalError(asyncResp->res);
+- return;
++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == "
++ << itemType;
++ continue;
+ }
++
+ nlohmann::json attributeItem;
+ attributeItem["AttributeName"] = item.first;
+ attributeItem["Type"] = attrType;
+@@ -609,16 +716,37 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ attributeItem["MenuPath"] =
+ std::get<biosBaseMenuPath>(item.second);
+
+- if (attrType == "String")
++ if (attrType == "String" ||
++ attrType == "Enumeration")
+ {
+ const std::string* currValue =
+ std::get_if<std::string>(
+ &std::get<biosBaseCurrValue>(
+ item.second));
++
++ if (!currValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get currValue, no "
++ "std::string data in BIOS "
++ "attributes item data";
++ continue;
++ }
++
+ const std::string* defValue =
+ std::get_if<std::string>(
+ &std::get<biosBaseDefaultValue>(
+ item.second));
++
++ if (!defValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get defValue, no "
++ "std::string data in BIOS "
++ "attributes item data";
++ continue;
++ }
++
+ attributeItem["CurrentValue"] =
+ currValue != nullptr ? *currValue : "";
+ attributeItem["DefaultValue"] =
+@@ -630,10 +758,30 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ std::get_if<int64_t>(
+ &std::get<biosBaseCurrValue>(
+ item.second));
++
++ if (!currValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get currValue, no "
++ "int64_t data in BIOS "
++ "attributes item data";
++ continue;
++ }
++
+ const int64_t* defValue =
+ std::get_if<int64_t>(
+ &std::get<biosBaseDefaultValue>(
+ item.second));
++
++ if (!defValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get defValue, no "
++ "int64_t data in BIOS "
++ "attributes item data";
++ continue;
++ }
++
+ attributeItem["CurrentValue"] =
+ currValue != nullptr ? *currValue : 0;
+ attributeItem["DefaultValue"] =
+@@ -641,12 +789,13 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ }
+ else
+ {
+- BMCWEB_LOG_ERROR
+- << "Unsupported attribute type.";
+- messages::internalError(asyncResp->res);
+- return;
++ BMCWEB_LOG_ERROR << "UNKNOWN attrType == "
++ << itemType;
++ continue;
+ }
+
++ nlohmann::json optionsArray =
++ nlohmann::json::array();
+ const std::vector<OptionsItemType>&
+ optionsVector =
+ std::get<biosBaseOptions>(item.second);
+@@ -661,9 +810,9 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ if (optItemTypeRedfish == "UNKNOWN")
+ {
+ BMCWEB_LOG_ERROR
+- << "optItemTypeRedfish == UNKNOWN";
+- messages::internalError(asyncResp->res);
+- return;
++ << "UNKNOWN optItemTypeRedfish == "
++ << strOptItemType;
++ continue;
+ }
+ if (optItemTypeRedfish == "OneOf")
+ {
+@@ -671,7 +820,21 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ std::get_if<std::string>(
+ &std::get<optItemValue>(
+ optItem));
+- optItemJson[optItemTypeRedfish] =
++
++ if (!currValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get currValue, "
++ "no "
++ "std::string data in option "
++ "item value";
++ continue;
++ }
++
++ optItemJson["ValueDisplayName"] =
++ currValue != nullptr ? *currValue
++ : "";
++ optItemJson["ValueName"] =
+ currValue != nullptr ? *currValue
+ : "";
+ }
+@@ -681,7 +844,21 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ std::get_if<int64_t>(
+ &std::get<optItemValue>(
+ optItem));
+- optItemJson[optItemTypeRedfish] =
++
++ if (!currValue)
++ {
++ BMCWEB_LOG_ERROR
++ << "Unable to get currValue, "
++ "no "
++ "int64_t data in option "
++ "item value";
++ continue;
++ }
++
++ optItemJson["ValueDisplayName"] =
++ currValue != nullptr ? *currValue
++ : 0;
++ optItemJson["ValueName"] =
+ currValue != nullptr ? *currValue
+ : 0;
+ }
+@@ -689,6 +866,12 @@ inline void requestRoutesBiosAttributeRegistry(App& app)
+ optionsArray.push_back(optItemJson);
+ }
+
++ if (optionsArray.empty())
++ {
++ BMCWEB_LOG_ERROR << "optionsArray is empty";
++ continue;
++ }
++
+ attributeItem["Value"] = optionsArray;
+ attributeArray.push_back(attributeItem);
+ }
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0007-Add-BiosAttributeRegistry-node-under-Registries.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0007-Add-BiosAttributeRegistry-node-under-Registries.patch
new file mode 100644
index 000000000..dbd5700be
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/biosconfig/0007-Add-BiosAttributeRegistry-node-under-Registries.patch
@@ -0,0 +1,76 @@
+From dd8160fefbac27d7f2ec99bf8b54c599083bcdbd Mon Sep 17 00:00:00 2001
+From: Snehalatha Venkatesh <snehalathax.v@intel.com>
+Date: Tue, 28 Dec 2021 11:05:50 +0000
+Subject: [PATCH] Add BiosAttributeRegistry node under Registries.
+
+/redfish/v1/Registries/ is missing node BiosAttributeRegistry
+under it. Added code to fix the same.
+
+Tested:
+1.Ran Redfish Validator and passed.
+2.GET - /redfish/v1/Registries/
+
+Response:
+{
+"@odata.id": "/redfish/v1/Registries",
+"@odata.type": "#MessageRegistryFileCollection.MessageRegistryFileCollection",
+"Description": "Collection of MessageRegistryFiles",
+"Members": [
+{
+"@odata.id": "/redfish/v1/Registries/Base"
+},
+{
+"@odata.id": "/redfish/v1/Registries/TaskEvent"
+},
+{
+"@odata.id": "/redfish/v1/Registries/ResourceEvent"
+},
+{
+"@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry"
+},
+{
+"@odata.id": "/redfish/v1/Registries/OpenBMC"
+}
+],
+"Members@odata.count": 5,
+"Name": "MessageRegistryFile Collection"
+}
+
+Signed-off-by: Snehalatha Venkatesh <snehalathax.v@intel.com>
+---
+ redfish-core/lib/message_registries.hpp | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp
+index d76f70b..4ae8383 100644
+--- a/redfish-core/lib/message_registries.hpp
++++ b/redfish-core/lib/message_registries.hpp
+@@ -41,11 +41,12 @@ inline void handleMessageRegistryFileCollectionGet(
+ {"@odata.id", "/redfish/v1/Registries"},
+ {"Name", "MessageRegistryFile Collection"},
+ {"Description", "Collection of MessageRegistryFiles"},
+- {"Members@odata.count", 4},
++ {"Members@odata.count", 5},
+ {"Members",
+ {{{"@odata.id", "/redfish/v1/Registries/Base"}},
+ {{"@odata.id", "/redfish/v1/Registries/TaskEvent"}},
+ {{"@odata.id", "/redfish/v1/Registries/ResourceEvent"}},
++ {{"@odata.id", "/redfish/v1/Registries/BiosAttributeRegistry"}},
+ {{"@odata.id", "/redfish/v1/Registries/OpenBMC"}}}}};
+ }
+
+@@ -89,6 +90,11 @@ inline void handleMessageRoutesMessageRegistryFileGet(
+ header = &registries::resource_event::header;
+ url = registries::resource_event::url;
+ }
++ else if (registry == "BiosAttributeRegistry")
++ {
++ header = &registries::bios::header;
++ dmtf.clear();
++ }
+ else
+ {
+ messages::resourceNotFound(
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket
new file mode 100644
index 000000000..8782e4dd3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/bmcweb.socket
@@ -0,0 +1,9 @@
+[Unit]
+Description=BMC Webserver socket
+
+[Socket]
+ListenStream=443
+ReusePort=true
+
+[Install]
+WantedBy=sockets.target
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch
new file mode 100644
index 000000000..5cb5c538c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch
@@ -0,0 +1,163 @@
+From d5ade2d2032d7bb0c17c92e957c2a4f6e77af2ee Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 6 Dec 2021 19:49:01 +0000
+Subject: [PATCH] Add unmerged changes for http retry support
+
+The http retry support added upstream as a single patch was slpit into
+3 patches, but only 2 of them was merged.
+This commit pulls in the differentail changes required to complete the
+entire http retry support. and also allow for other subsequent patches
+to be appplied easily.
+
+Change-Id: I43e68eeffb8d69c289dd306c1c7cafc87ad766a0
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_client.hpp | 32 ++++++++++++++++---
+ .../include/event_service_manager.hpp | 26 +++++++++------
+ redfish-core/lib/event_service.hpp | 1 +
+ 3 files changed, 45 insertions(+), 14 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index 5f352d3..f0152db 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -188,6 +188,17 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ BMCWEB_LOG_DEBUG << "recvMessage() data: "
+ << self->parser->get();
+
++ // Check if the response and header are received
++ if (!self->parser->is_done())
++ {
++ // The parser failed to receive the response
++ BMCWEB_LOG_ERROR
++ << "recvMessage() parser failed to receive response";
++ self->state = ConnState::recvFailed;
++ self->handleConnState();
++ return;
++ }
++
+ unsigned int respCode = self->parser->get().result_int();
+ BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
+ << respCode;
+@@ -379,15 +390,17 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ public:
+ explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const std::string& destPort,
+- const std::string& destUri,
+- const boost::beast::http::fields& httpHeader) :
++ const std::string& destUri) :
+ conn(ioc),
+- timer(ioc),
+- req(boost::beast::http::verb::post, destUri, 11, "", httpHeader),
+- subId(id), host(destIP), port(destPort)
++ timer(ioc), req(boost::beast::http::verb::post, destUri, 11), subId(id),
++ host(destIP), port(destPort)
+ {
++ // Set the request header
+ req.set(boost::beast::http::field::host, host);
++ req.set(boost::beast::http::field::content_type, "application/json");
+ req.keep_alive(true);
++
++ requestDataQueue.set_capacity(maxRequestQueueSize);
+ }
+
+ void sendData(const std::string& data)
+@@ -408,6 +421,15 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ }
+
++ void setHeaders(const boost::beast::http::fields& httpHeaders)
++ {
++ // Set custom headers
++ for (const auto& header : httpHeaders)
++ {
++ req.set(header.name(), header.value());
++ }
++ }
++
+ void setRetryConfig(const uint32_t retryAttempts,
+ const uint32_t retryTimeoutInterval)
+ {
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index e879f9e..75380dc 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -370,7 +370,10 @@ class Subscription : public persistent_data::UserSubscription
+ eventSeqNum(1),
+ host(inHost), port(inPort), path(inPath), uriProto(inUriProto)
+ {
+- // Subscription constructor
++ // create the HttpClient connection
++ conn = std::make_shared<crow::HttpClient>(
++ crow::connections::systemBus->get_io_context(), id, host, port,
++ path);
+ }
+
+ Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+@@ -391,17 +394,12 @@ class Subscription : public persistent_data::UserSubscription
+ return false;
+ }
+
+- if (conn == nullptr)
++ if (conn != nullptr)
+ {
+- // create the HttpClient connection
+- conn = std::make_shared<crow::HttpClient>(
+- crow::connections::systemBus->get_io_context(), id, host, port,
+- path, httpHeaders);
++ conn->sendData(msg);
++ eventSeqNum++;
+ }
+
+- conn->sendData(msg);
+- eventSeqNum++;
+-
+ if (sseConn != nullptr)
+ {
+ sseConn->sendData(eventSeqNum, msg);
+@@ -548,6 +546,14 @@ class Subscription : public persistent_data::UserSubscription
+ }
+ }
+
++ void updatehttpHeaders()
++ {
++ if (conn != nullptr)
++ {
++ conn->setHeaders(httpHeaders);
++ }
++ }
++
+ uint64_t getEventSeqNum() const
+ {
+ return eventSeqNum;
+@@ -661,6 +667,7 @@ class EventServiceManager
+ // Update retry configuration.
+ subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+ subValue->updateRetryPolicy();
++ subValue->updatehttpHeaders();
+ }
+ }
+
+@@ -915,6 +922,7 @@ class EventServiceManager
+ // Update retry configuration.
+ subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+ subValue->updateRetryPolicy();
++ subValue->updatehttpHeaders();
+
+ return id;
+ }
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index 9ce2f05..0903874 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -619,6 +619,7 @@ inline void requestRoutesEventDestination(App& app)
+ }
+ }
+ subValue->httpHeaders = fields;
++ subValue->updatehttpHeaders();
+ }
+
+ if (retryPolicy)
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch
new file mode 100644
index 000000000..9f51d23d3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch
@@ -0,0 +1,448 @@
+From 0dc82b56bae36aee8ebc1923e909e3ce74f6fdbc Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Mon, 6 Dec 2021 21:39:05 +0000
+Subject: [PATCH] EventService: https client support
+
+Add https client support for push style eventing. Using this BMC can
+push the event logs/telemetry data to event listener over secure http
+channel.
+
+Tested:
+ - Created subscription with https destination url. Using
+ SubmitTestEvent action set the event and can see event on event
+ listener.
+ - Validator passed.
+
+Change-Id: I480085344ba7bed6ec0d94876eda1d252e51cb45
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_client.hpp | 309 ++++++++++++------
+ .../include/event_service_manager.hpp | 2 +-
+ 2 files changed, 204 insertions(+), 107 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index e69d397..7328eae 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -20,6 +20,7 @@
+ #include <boost/beast/core/flat_buffer.hpp>
+ #include <boost/beast/core/tcp_stream.hpp>
+ #include <boost/beast/http/message.hpp>
++#include <boost/beast/ssl/ssl_stream.hpp>
+ #include <boost/beast/version.hpp>
+ #include <boost/circular_buffer.hpp>
+ #include <include/async_resolve.hpp>
+@@ -44,6 +45,8 @@ enum class ConnState
+ resolveFailed,
+ connectInProgress,
+ connectFailed,
++ handshakeInProgress,
++ handshakeFailed,
+ connected,
+ sendInProgress,
+ sendFailed,
+@@ -62,7 +65,9 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ private:
+ crow::async_resolve::Resolver resolver;
++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client};
+ boost::beast::tcp_stream conn;
++ std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream&>> sslConn;
+ boost::asio::steady_timer timer;
+ boost::beast::flat_static_buffer<httpReadBodyLimit> buffer;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+@@ -110,25 +115,52 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ const std::vector<boost::asio::ip::tcp::endpoint>& endpointList)
+ {
+ state = ConnState::connectInProgress;
++ sslConn.emplace(conn, ctx);
+
+ BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port;
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const boost::asio::ip::tcp::endpoint& endpoint) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Connect " << endpoint
++ << " failed: " << ec.message();
++ self->state = ConnState::connectFailed;
++ self->handleConnState();
++ return;
++ }
+
++ BMCWEB_LOG_DEBUG << "Connected to: " << endpoint;
++ if (self->sslConn)
++ {
++ self->performHandshake();
++ }
++ else
++ {
++ self->handleConnState();
++ }
++ };
+ conn.expires_after(std::chrono::seconds(30));
+- conn.async_connect(
+- endpointList, [self(shared_from_this())](
+- const boost::beast::error_code ec,
+- const boost::asio::ip::tcp::endpoint& endpoint) {
++ conn.async_connect(endpointList, std::move(respHandler));
++ }
++
++ void performHandshake()
++ {
++ state = ConnState::handshakeInProgress;
++
++ sslConn->async_handshake(
++ boost::asio::ssl::stream_base::client,
++ [self(shared_from_this())](const boost::beast::error_code ec) {
+ if (ec)
+ {
+- BMCWEB_LOG_ERROR << "Connect "
+- << endpoint.address().to_string()
+- << " failed: " << ec.message();
+- self->state = ConnState::connectFailed;
++ BMCWEB_LOG_ERROR << "SSL handshake failed: "
++ << ec.message();
++ self->state = ConnState::handshakeFailed;
+ self->handleConnState();
+ return;
+ }
+- BMCWEB_LOG_DEBUG << "Connected to: "
+- << endpoint.address().to_string();
++
++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull";
+ self->state = ConnState::connected;
+ self->handleConnState();
+ });
+@@ -141,124 +173,182 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ req.body() = data;
+ req.prepare_payload();
+
+- // Set a timeout on the operation
+- conn.expires_after(std::chrono::seconds(30));
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message();
++ self->state = ConnState::sendFailed;
++ self->handleConnState();
++ return;
++ }
+
+- // Send the HTTP request to the remote host
+- boost::beast::http::async_write(
+- conn, req,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "sendMessage() failed: "
+- << ec.message();
+- self->state = ConnState::sendFailed;
+- self->handleConnState();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
+- << bytesTransferred;
+- boost::ignore_unused(bytesTransferred);
++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
++ self->recvMessage();
++ };
+
+- self->recvMessage();
+- });
++ // Set a timeout on the operation
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_write(*sslConn, req,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_write(conn, req, std::move(respHandler));
++ }
+ }
+
+ void recvMessage()
+ {
+ state = ConnState::recvInProgress;
+
+- parser.emplace(std::piecewise_construct, std::make_tuple());
+- parser->body_limit(httpReadBodyLimit);
++ auto respHandler = [self(shared_from_this())](
++ const boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ if (ec && ec != boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message();
+
+- // Receive the HTTP response
+- boost::beast::http::async_read(
+- conn, buffer, *parser,
+- [self(shared_from_this())](const boost::beast::error_code& ec,
+- const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "recvMessage() failed: "
+- << ec.message();
+- self->state = ConnState::recvFailed;
+- self->handleConnState();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
+- << bytesTransferred;
+- BMCWEB_LOG_DEBUG << "recvMessage() data: "
+- << self->parser->get().body();
++ self->state = ConnState::recvFailed;
++ self->handleConnState();
++ return;
++ }
+
+- // Check if the response and header are received
+- if (!self->parser->is_done())
+- {
+- // The parser failed to receive the response
+- BMCWEB_LOG_ERROR
+- << "recvMessage() parser failed to receive response";
+- self->state = ConnState::recvFailed;
+- self->handleConnState();
+- return;
+- }
++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: "
++ << bytesTransferred;
++ boost::ignore_unused(bytesTransferred);
++
++ // Check if the response and header are received
++ if (!self->parser->is_done())
++ {
++ // The parser failed to receive the response
++ BMCWEB_LOG_ERROR
++ << "recvMessage() parser failed to receive response";
++ self->state = ConnState::recvFailed;
++ self->handleConnState();
++ return;
++ }
+
+- unsigned int respCode = self->parser->get().result_int();
+- BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
+- << respCode;
++ unsigned int respCode = self->parser->get().result_int();
++ BMCWEB_LOG_DEBUG << "recvMessage() Header Response Code: "
++ << respCode;
+
+- // 2XX response is considered to be successful
+- if ((respCode < 200) || (respCode >= 300))
+- {
+- // The listener failed to receive the Sent-Event
+- BMCWEB_LOG_ERROR
+- << "recvMessage() Listener Failed to "
+- "receive Sent-Event. Header Response Code: "
+- << respCode;
+- self->state = ConnState::recvFailed;
+- self->handleConnState();
+- return;
+- }
++ // 2XX response is considered to be successful
++ if ((respCode < 200) || (respCode >= 300))
++ {
++ // The listener failed to receive the Sent-Event
++ BMCWEB_LOG_ERROR << "recvMessage() Listener Failed to "
++ "receive Sent-Event";
++ self->state = ConnState::recvFailed;
++ self->handleConnState();
++ return;
++ }
+
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- if (!self->requestDataQueue.empty())
+- {
+- self->requestDataQueue.pop_front();
+- }
+- self->state = ConnState::idle;
++ // Send is successful, Lets remove data from queue
++ // check for next request data in queue.
++ if (!self->requestDataQueue.empty())
++ {
++ self->requestDataQueue.pop_front();
++ }
++ self->state = ConnState::idle;
++ // Keep the connection alive if server supports it
++ // Else close the connection
++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
++ << self->parser->keep_alive();
++ if (!self->parser->keep_alive())
++ {
++ // Abort the connection since server is not keep-alive enabled
++ self->state = ConnState::abortConnection;
++ }
+
+- // Keep the connection alive if server supports it
+- // Else close the connection
+- BMCWEB_LOG_DEBUG << "recvMessage() keepalive : "
+- << self->parser->keep_alive();
+- if (!self->parser->keep_alive())
+- {
+- // Abort the connection since server is not keep-alive
+- // enabled
+- self->state = ConnState::abortConnection;
+- }
++ // Returns ownership of the parsed message
++ self->parser->release();
+
+- self->handleConnState();
+- });
+- }
++ self->handleConnState();
++ };
++ parser.emplace(std::piecewise_construct, std::make_tuple());
++ parser->body_limit(httpReadBodyLimit);
+
++ // Check only for the response header
++ parser->skip(true);
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
++ {
++ boost::beast::http::async_read(*sslConn, buffer, *parser,
++ std::move(respHandler));
++ }
++ else
++ {
++ boost::beast::http::async_read(conn, buffer, *parser,
++ std::move(respHandler));
++ }
++ }
+ void doClose()
+ {
+ state = ConnState::closeInProgress;
+- boost::beast::error_code ec;
+- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+- conn.close();
+
+- // not_connected happens sometimes so don't bother reporting it.
+- if (ec && ec != boost::beast::errc::not_connected)
++ // Set the timeout on the tcp stream socket for the async operation
++ conn.expires_after(std::chrono::seconds(30));
++ if (sslConn)
+ {
+- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message();
+- return;
++ sslConn->async_shutdown([self = shared_from_this()](
++ const boost::system::error_code ec) {
++ if (ec)
++ {
++ // Many https server closes connection abruptly
++ // i.e witnout close_notify. More details are at
++ // https://github.com/boostorg/beast/issues/824
++ if (ec == boost::asio::ssl::error::stream_truncated)
++ {
++ BMCWEB_LOG_INFO << "doClose(): Connection "
++ "closed by server. ";
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "doClose() failed: "
++ << ec.message();
++ }
++ }
++ else
++ {
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
++ }
++ self->conn.close();
++
++ if ((self->state != ConnState::suspended) &&
++ (self->state != ConnState::terminated))
++ {
++ self->state = ConnState::closed;
++ self->handleConnState();
++ }
++ });
+ }
+- BMCWEB_LOG_DEBUG << "Connection closed gracefully";
+- if ((state != ConnState::suspended) && (state != ConnState::terminated))
++ else
+ {
+- state = ConnState::closed;
+- handleConnState();
++ boost::beast::error_code ec;
++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both,
++ ec);
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "doClose() failed: " << ec.message();
++ }
++ else
++ {
++ BMCWEB_LOG_DEBUG << "Connection closed gracefully...";
++ }
++ conn.close();
++
++ if ((state != ConnState::suspended) &&
++ (state != ConnState::terminated))
++ {
++ state = ConnState::closed;
++ handleConnState();
++ }
+ }
+ }
+
+@@ -330,6 +420,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ {
+ case ConnState::resolveInProgress:
+ case ConnState::connectInProgress:
++ case ConnState::handshakeInProgress:
+ case ConnState::sendInProgress:
+ case ConnState::recvInProgress:
+ case ConnState::closeInProgress:
+@@ -356,6 +447,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ case ConnState::resolveFailed:
+ case ConnState::connectFailed:
++ case ConnState::handshakeFailed:
+ case ConnState::sendFailed:
+ case ConnState::recvFailed:
+ case ConnState::retry:
+@@ -392,7 +484,8 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ public:
+ explicit HttpClient(boost::asio::io_context& ioc, const std::string& id,
+ const std::string& destIP, const std::string& destPort,
+- const std::string& destUri) :
++ const std::string& destUri,
++ const std::string& uriProto) :
+ conn(ioc),
+ timer(ioc), req(boost::beast::http::verb::post, destUri, 11), subId(id),
+ host(destIP), port(destPort)
+@@ -403,6 +496,10 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ req.keep_alive(true);
+
+ requestDataQueue.set_capacity(maxRequestQueueSize);
++ if (uriProto == "https")
++ {
++ sslConn.emplace(conn, ctx);
++ }
+ }
+
+ void sendData(const std::string& data)
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index bc97219..64cb43a 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -372,7 +372,7 @@ class Subscription : public persistent_data::UserSubscription
+ // create the HttpClient connection
+ conn = std::make_shared<crow::HttpClient>(
+ crow::connections::systemBus->get_io_context(), id, host, port,
+- path);
++ path, uriProto);
+ }
+
+ Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch
new file mode 100644
index 000000000..bce9fc939
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch
@@ -0,0 +1,471 @@
+From 6134487ccc46ace1943184f40c42184e3aa85881 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Fri, 12 Mar 2021 18:53:25 +0000
+Subject: [PATCH] Add Server-Sent-Events support
+
+Server-Sent Events is a standard describing how servers can
+initiate data transmission towards clients once an initial
+client connection has been established. Unlike websockets
+(which are bidirectional), Server-Sent Events are
+unidirectional and commonly used to send message updates or
+continuous data streams to a browser client.
+
+This is base patch for adding Server-Sent events support to
+bmcweb. Redfish eventservice SSE style subscription uses
+this and will be loaded on top of this commit.
+
+Tested:
+ - Tested using follow-up patch on top which adds
+ support for Redfish EventService SSE style subscription
+ and observed events are getting sent periodically.
+
+Change-Id: I36956565cbba30c2007852c9471f477f6d1736e9
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_connection.hpp | 10 +-
+ http/http_response.hpp | 7 +-
+ http/routing.hpp | 71 ++++++++++
+ http/server_sent_event.hpp | 282 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 365 insertions(+), 5 deletions(-)
+ create mode 100644 http/server_sent_event.hpp
+
+diff --git a/http/http_connection.hpp b/http/http_connection.hpp
+index a27ec26..882a7a6 100644
+--- a/http/http_connection.hpp
++++ b/http/http_connection.hpp
+@@ -374,11 +374,13 @@ class Connection :
+ self->completeRequest(thisRes);
+ });
+
+- if (thisReq.isUpgrade() &&
+- boost::iequals(
+- thisReq.getHeaderValue(boost::beast::http::field::upgrade),
+- "websocket"))
++ if ((thisReq.isUpgrade() &&
++ boost::iequals(
++ thisReq.getHeaderValue(boost::beast::http::field::upgrade),
++ "websocket")) ||
++ (req->url == "/sse"))
+ {
++ BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded";
+ handler->handleUpgrade(thisReq, res, std::move(adaptor));
+ // delete lambda with self shared_ptr
+ // to enable connection destruction
+diff --git a/http/http_response.hpp b/http/http_response.hpp
+index 3c2a3f9..679dab2 100644
+--- a/http/http_response.hpp
++++ b/http/http_response.hpp
+@@ -15,10 +15,15 @@ namespace crow
+ template <typename Adaptor, typename Handler>
+ class Connection;
+
++template <typename Adaptor>
++class SseConnectionImpl;
++
+ struct Response
+ {
+ template <typename Adaptor, typename Handler>
+ friend class crow::Connection;
++ template <typename Adaptor>
++ friend class crow::SseConnectionImpl;
+ using response_type =
+ boost::beast::http::response<boost::beast::http::string_body>;
+
+@@ -173,8 +178,8 @@ struct Response
+
+ private:
+ bool completed = false;
+- std::function<void(Response&)> completeRequestHandler;
+ std::function<bool()> isAliveHelper;
++ std::function<void(Response&)> completeRequestHandler;
+
+ // In case of a JSON object, set the Content-Type header
+ void jsonMode()
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 250cd33..552e1cf 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -7,6 +7,7 @@
+ #include "http_response.hpp"
+ #include "logging.hpp"
+ #include "privileges.hpp"
++#include "server_sent_event.hpp"
+ #include "sessions.hpp"
+ #include "utility.hpp"
+ #include "websocket.hpp"
+@@ -407,6 +408,68 @@ class WebSocketRule : public BaseRule
+ std::function<void(crow::websocket::Connection&)> errorHandler;
+ };
+
++class SseSocketRule : public BaseRule
++{
++ using self_t = SseSocketRule;
++
++ public:
++ SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn)
++ {}
++
++ void validate() override
++ {}
++
++ void handle(const Request&,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const RoutingParams&) override
++ {
++ asyncResp->res.result(boost::beast::http::status::not_found);
++ }
++
++ void handleUpgrade(const Request& req, Response&,
++ boost::asio::ip::tcp::socket&& adaptor) override
++ {
++ std::shared_ptr<crow::SseConnectionImpl<boost::asio::ip::tcp::socket>>
++ myConnection = std::make_shared<
++ crow::SseConnectionImpl<boost::asio::ip::tcp::socket>>(
++ req, std::move(adaptor), openHandler, closeHandler);
++ myConnection->start();
++ }
++#ifdef BMCWEB_ENABLE_SSL
++ void handleUpgrade(const Request& req, Response&,
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>&&
++ adaptor) override
++ {
++ std::shared_ptr<crow::SseConnectionImpl<
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
++ myConnection = std::make_shared<crow::SseConnectionImpl<
++ boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
++ req, std::move(adaptor), openHandler, closeHandler);
++ myConnection->start();
++ }
++#endif
++
++ template <typename Func>
++ self_t& onopen(Func f)
++ {
++ openHandler = f;
++ return *this;
++ }
++
++ template <typename Func>
++ self_t& onclose(Func f)
++ {
++ closeHandler = f;
++ return *this;
++ }
++
++ private:
++ std::function<void(std::shared_ptr<crow::SseConnection>&,
++ const crow::Request&, crow::Response&)>
++ openHandler;
++ std::function<void(std::shared_ptr<crow::SseConnection>&)> closeHandler;
++};
++
+ template <typename T>
+ struct RuleParameterTraits
+ {
+@@ -419,6 +482,14 @@ struct RuleParameterTraits
+ return *p;
+ }
+
++ SseSocketRule& serverSentEvent()
++ {
++ self_t* self = static_cast<self_t*>(this);
++ SseSocketRule* p = new SseSocketRule(self->rule);
++ self->ruleToUpgrade.reset(p);
++ return *p;
++ }
++
+ self_t& name(const std::string_view name) noexcept
+ {
+ self_t* self = static_cast<self_t*>(this);
+diff --git a/http/server_sent_event.hpp b/http/server_sent_event.hpp
+new file mode 100644
+index 0000000..41d18ed
+--- /dev/null
++++ b/http/server_sent_event.hpp
+@@ -0,0 +1,282 @@
++#pragma once
++#include "http_request.hpp"
++
++#include <boost/algorithm/string/predicate.hpp>
++#include <boost/asio/buffer.hpp>
++#include <boost/beast/http/buffer_body.hpp>
++#include <boost/beast/websocket.hpp>
++
++#include <array>
++#include <functional>
++
++#ifdef BMCWEB_ENABLE_SSL
++#include <boost/beast/websocket/ssl.hpp>
++#endif
++
++namespace crow
++{
++
++struct SseConnection : std::enable_shared_from_this<SseConnection>
++{
++ public:
++ SseConnection(const crow::Request& reqIn) : req(reqIn)
++ {}
++ virtual ~SseConnection() = default;
++
++ virtual boost::asio::io_context& getIoContext() = 0;
++ virtual void sendSSEHeader() = 0;
++ virtual void completeRequest() = 0;
++ virtual void close(const std::string_view msg = "quit") = 0;
++ virtual void sendEvent(const std::string_view id,
++ const std::string_view msg) = 0;
++
++ crow::Request req;
++ crow::Response res;
++};
++
++template <typename Adaptor>
++class SseConnectionImpl : public SseConnection
++{
++ public:
++ SseConnectionImpl(
++ const crow::Request& reqIn, Adaptor adaptorIn,
++ std::function<void(std::shared_ptr<SseConnection>&,
++ const crow::Request&, crow::Response&)>
++ openHandler,
++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler) :
++ SseConnection(reqIn),
++ adaptor(std::move(adaptorIn)), openHandler(std::move(openHandler)),
++ closeHandler(std::move(closeHandler))
++ {
++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE constructor " << this;
++ }
++
++ ~SseConnectionImpl() override
++ {
++ res.setCompleteRequestHandler(nullptr);
++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE destructor " << this;
++ }
++
++ boost::asio::io_context& getIoContext() override
++ {
++ return static_cast<boost::asio::io_context&>(
++ adaptor.get_executor().context());
++ }
++
++ void start()
++ {
++ // Register for completion callback.
++ res.setCompleteRequestHandler([this, self(shared_from_this())](
++ crow::Response& thisRes) {
++ boost::ignore_unused(thisRes);
++ boost::asio::post(this->adaptor.get_executor(), [self] {
++ self->completeRequest();
++ });
++ });
++
++ if (openHandler)
++ {
++ std::shared_ptr<SseConnection> self = this->shared_from_this();
++ openHandler(self, req, res);
++ }
++ }
++
++ void close(const std::string_view msg) override
++ {
++ BMCWEB_LOG_DEBUG << "Closing SSE connection " << this << " - " << msg;
++ boost::beast::get_lowest_layer(adaptor).close();
++
++ // send notification to handler for cleanup
++ if (closeHandler)
++ {
++ std::shared_ptr<SseConnection> self = this->shared_from_this();
++ closeHandler(self);
++ }
++ }
++
++ void sendSSEHeader() override
++ {
++ BMCWEB_LOG_DEBUG << "Starting SSE connection";
++ using BodyType = boost::beast::http::buffer_body;
++ auto response =
++ std::make_shared<boost::beast::http::response<BodyType>>(
++ boost::beast::http::status::ok, 11);
++ auto serializer =
++ std::make_shared<boost::beast::http::response_serializer<BodyType>>(
++ *response);
++
++ response->set(boost::beast::http::field::server, "bmcweb");
++ response->set(boost::beast::http::field::content_type,
++ "text/event-stream");
++ response->body().data = nullptr;
++ response->body().size = 0;
++ response->body().more = true;
++
++ boost::beast::http::async_write_header(
++ adaptor, *serializer,
++ [this, self(shared_from_this()), response, serializer](
++ const boost::beast::error_code& ec, const std::size_t&) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Error sending header" << ec;
++ close("async_write_header failed");
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "SSE header sent - Connection established";
++
++ // SSE stream header sent, So lets setup monitor.
++ // Any read data on this stream will be error in case of SSE.
++ setupRead();
++ });
++ }
++
++ void setupRead()
++ {
++ adaptor.async_read_some(
++ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
++ [this](const boost::system::error_code& ec, std::size_t bytesRead) {
++ BMCWEB_LOG_DEBUG << "async_read_some: Read " << bytesRead
++ << " bytes";
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Read error: " << ec;
++ }
++ outputBuffer.commit(bytesRead);
++ outputBuffer.consume(bytesRead);
++
++ // After establishing SSE stream, Reading data on this
++ // stream means client is disobeys the SSE protocol.
++ // Read the data to avoid buffer attacks and close connection.
++ close("Close SSE connection");
++ return;
++ });
++ }
++
++ void doWrite()
++ {
++ if (doingWrite)
++ {
++ return;
++ }
++ if (inputBuffer.size() == 0)
++ {
++ BMCWEB_LOG_DEBUG << "inputBuffer is empty... Bailing out";
++ return;
++ }
++ doingWrite = true;
++
++ adaptor.async_write_some(
++ inputBuffer.data(), [this, self(shared_from_this())](
++ boost::beast::error_code ec,
++ const std::size_t& bytesTransferred) {
++ doingWrite = false;
++ inputBuffer.consume(bytesTransferred);
++
++ if (ec == boost::asio::error::eof)
++ {
++ BMCWEB_LOG_ERROR << "async_write_some() SSE stream closed";
++ close("SSE stream closed");
++ return;
++ }
++
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "async_write_some() failed: "
++ << ec.message();
++ close("async_write_some failed");
++ return;
++ }
++ BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: "
++ << bytesTransferred;
++
++ doWrite();
++ });
++ }
++
++ void completeRequest() override
++ {
++ BMCWEB_LOG_DEBUG << "SSE completeRequest() handler";
++ if (res.body().empty() && !res.jsonValue.empty())
++ {
++ res.addHeader("Content-Type", "application/json");
++ res.body() = res.jsonValue.dump(
++ 2, ' ', true, nlohmann::json::error_handler_t::replace);
++ }
++
++ res.preparePayload();
++ auto serializer =
++ std::make_shared<boost::beast::http::response_serializer<
++ boost::beast::http::string_body>>(*res.stringResponse);
++
++ boost::beast::http::async_write(
++ adaptor, *serializer,
++ [this, self(shared_from_this()),
++ serializer](const boost::system::error_code& ec,
++ std::size_t bytesTransferred) {
++ BMCWEB_LOG_DEBUG << this << " async_write " << bytesTransferred
++ << " bytes";
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << this << " from async_write failed";
++ return;
++ }
++ res.clear();
++
++ BMCWEB_LOG_DEBUG << this
++ << " Closing SSE connection - Request invalid";
++ close("Request invalid");
++ });
++
++ // delete lambda with self shared_ptr
++ // to enable connection destruction
++ res.setCompleteRequestHandler(nullptr);
++ }
++
++ void sendEvent(const std::string_view id,
++ const std::string_view msg) override
++ {
++ if (msg.empty())
++ {
++ BMCWEB_LOG_DEBUG << "Empty data, bailing out.";
++ return;
++ }
++
++ std::string rawData;
++ if (!id.empty())
++ {
++ rawData += "id: ";
++ rawData.append(id.begin(), id.end());
++ rawData += "\n";
++ }
++
++ rawData += "data: ";
++ for (char character : msg)
++ {
++ rawData += character;
++ if (character == '\n')
++ {
++ rawData += "data: ";
++ }
++ }
++ rawData += "\n\n";
++
++ boost::asio::buffer_copy(inputBuffer.prepare(rawData.size()),
++ boost::asio::buffer(rawData));
++ inputBuffer.commit(rawData.size());
++
++ doWrite();
++ }
++
++ private:
++ Adaptor adaptor;
++
++ boost::beast::flat_static_buffer<1024U * 8U> outputBuffer;
++ boost::beast::flat_static_buffer<1024U * 64U> inputBuffer;
++ bool doingWrite = false;
++
++ std::function<void(std::shared_ptr<SseConnection>&, const crow::Request&,
++ crow::Response&)>
++ openHandler;
++ std::function<void(std::shared_ptr<SseConnection>&)> closeHandler;
++};
++} // namespace crow
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch
new file mode 100644
index 000000000..40b46e74a
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch
@@ -0,0 +1,677 @@
+From 30088cc01b8068419311b171dc51c8ff11fbc185 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Tue, 16 Mar 2021 15:37:24 +0000
+Subject: [PATCH] Add SSE style subscription support to eventservice
+
+This commit adds the SSE style eventservice subscription
+style event. Using this, end user can subscribe for
+Redfish event logs using GET on SSE usri from
+browser.
+URI: /redfish/v1/EventService/Subscriptions/SSE
+
+Tested:
+ - From Browser did GET on above SSE URI and
+ generated some Redfish event logs(power cycle)
+ and saw redfish event logs streaming on browser.
+ - After SSE registration, Check Subscription collections
+ and GET on individual subscription and saw desired
+ response.
+ - Ran RedfishValidation and its passed.
+
+Change-Id: I7f4b7a34974080739c4ba968ed570489af0474de
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Signed-off-by: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_connection.hpp | 2 +-
+ include/eventservice_sse.hpp | 75 +++++
+ .../include/event_service_manager.hpp | 111 +++++--
+ redfish-core/include/server_sent_events.hpp | 286 ------------------
+ redfish-core/lib/event_service.hpp | 8 +-
+ src/webserver_main.cpp | 2 +
+ 6 files changed, 165 insertions(+), 319 deletions(-)
+ create mode 100644 include/eventservice_sse.hpp
+ delete mode 100644 redfish-core/include/server_sent_events.hpp
+
+diff --git a/http/http_connection.hpp b/http/http_connection.hpp
+index 9986e07..0b7b341 100644
+--- a/http/http_connection.hpp
++++ b/http/http_connection.hpp
+@@ -379,7 +379,7 @@ class Connection :
+ boost::iequals(
+ thisReq.getHeaderValue(boost::beast::http::field::upgrade),
+ "websocket")) ||
+- (req->url == "/sse"))
++ (req->url == "/redfish/v1/EventService/Subscriptions/SSE"))
+ {
+ BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded";
+ handler->handleUpgrade(thisReq, res, std::move(adaptor));
+diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp
+new file mode 100644
+index 0000000..14daf00
+--- /dev/null
++++ b/include/eventservice_sse.hpp
+@@ -0,0 +1,75 @@
++#pragma once
++
++#include <app.hpp>
++#include <event_service_manager.hpp>
++
++namespace redfish
++{
++namespace eventservice_sse
++{
++
++static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn,
++ const crow::Request& req, crow::Response& res)
++{
++ if ((EventServiceManager::getInstance().getNumberOfSubscriptions() >=
++ maxNoOfSubscriptions) ||
++ EventServiceManager::getInstance().getNumberOfSSESubscriptions() >=
++ maxNoOfSSESubscriptions)
++ {
++ BMCWEB_LOG_ERROR << "Max SSE subscriptions reached";
++ messages::eventSubscriptionLimitExceeded(res);
++ res.end();
++ return false;
++ }
++ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size();
++
++ std::shared_ptr<redfish::Subscription> subValue =
++ std::make_shared<redfish::Subscription>(std::move(conn));
++
++ // GET on this URI means, Its SSE subscriptionType.
++ subValue->subscriptionType = redfish::subscriptionTypeSSE;
++
++ // TODO: parse $filter query params and fill config.
++ subValue->protocol = "Redfish";
++ subValue->retryPolicy = "TerminateAfterRetries";
++ subValue->eventFormatType = "Event";
++
++ std::string id =
++ redfish::EventServiceManager::getInstance().addSubscription(subValue,
++ false);
++ if (id.empty())
++ {
++ messages::internalError(res);
++ res.end();
++ return false;
++ }
++
++ return true;
++}
++
++static void deleteSubscription(std::shared_ptr<crow::SseConnection>& conn)
++{
++ redfish::EventServiceManager::getInstance().deleteSubscription(conn);
++}
++
++inline void requestRoutes(App& app)
++{
++ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE")
++ .privileges({{"ConfigureComponents", "ConfigureManager"}})
++ .serverSentEvent()
++ .onopen([](std::shared_ptr<crow::SseConnection>& conn,
++ const crow::Request& req, crow::Response& res) {
++ BMCWEB_LOG_DEBUG << "Connection " << conn << " opened.";
++ if (createSubscription(conn, req, res))
++ {
++ // All success, lets send SSE haader
++ conn->sendSSEHeader();
++ }
++ })
++ .onclose([](std::shared_ptr<crow::SseConnection>& conn) {
++ BMCWEB_LOG_DEBUG << "Connection " << conn << " closed";
++ deleteSubscription(conn);
++ });
++}
++} // namespace eventservice_sse
++} // namespace redfish
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 64cb43a..c64bb59 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -23,6 +23,7 @@
+ #include <sys/inotify.h>
+
+ #include <boost/asio/io_context.hpp>
++#include <boost/beast/core/span.hpp>
+ #include <boost/container/flat_map.hpp>
+ #include <dbus_utility.hpp>
+ #include <error_messages.hpp>
+@@ -30,9 +31,10 @@
+ #include <http_client.hpp>
+ #include <persistent_data.hpp>
+ #include <random.hpp>
+-#include <server_sent_events.hpp>
++#include <server_sent_event.hpp>
+ #include <utils/json_utils.hpp>
+
++#include <algorithm>
+ #include <cstdlib>
+ #include <ctime>
+ #include <fstream>
+@@ -48,9 +50,27 @@ using ReadingsObjType =
+ static constexpr const char* eventFormatType = "Event";
+ static constexpr const char* metricReportFormatType = "MetricReport";
+
++static constexpr const char* subscriptionTypeSSE = "SSE";
+ static constexpr const char* eventServiceFile =
+ "/var/lib/bmcweb/eventservice_config.json";
+
++static constexpr const uint8_t maxNoOfSubscriptions = 20;
++static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
++
++#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
++static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
++static constexpr const char* redfishEventLogDir = "/var/log";
++static constexpr const char* redfishEventLogFile = "/var/log/redfish";
++static constexpr const size_t iEventSize = sizeof(inotify_event);
++static int inotifyFd = -1;
++static int dirWatchDesc = -1;
++static int fileWatchDesc = -1;
++
++// <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs>
++using EventLogObjectsType =
++ std::tuple<std::string, std::string, std::string, std::string, std::string,
++ std::vector<std::string>>;
++
+ namespace registries
+ {
+ inline std::span<const MessageEntry>
+@@ -70,24 +90,6 @@ inline std::span<const MessageEntry>
+ }
+ return {openbmc::registry};
+ }
+-} // namespace registries
+-
+-#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+-static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
+-static constexpr const char* redfishEventLogDir = "/var/log";
+-static constexpr const char* redfishEventLogFile = "/var/log/redfish";
+-static constexpr const size_t iEventSize = sizeof(inotify_event);
+-static int inotifyFd = -1;
+-static int dirWatchDesc = -1;
+-static int fileWatchDesc = -1;
+-
+-// <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs>
+-using EventLogObjectsType =
+- std::tuple<std::string, std::string, std::string, std::string, std::string,
+- std::vector<std::string>>;
+-
+-namespace registries
+-{
+ static const Message*
+ getMsgFromRegistry(const std::string& messageKey,
+ const std::span<const MessageEntry>& registry)
+@@ -375,11 +377,9 @@ class Subscription : public persistent_data::UserSubscription
+ path, uriProto);
+ }
+
+- Subscription(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+- eventSeqNum(1)
+- {
+- sseConn = std::make_shared<crow::ServerSentEvents>(adaptor);
+- }
++ Subscription(const std::shared_ptr<crow::SseConnection>& adaptor) :
++ sseConn(adaptor), eventSeqNum(1)
++ {}
+
+ ~Subscription() = default;
+
+@@ -396,13 +396,14 @@ class Subscription : public persistent_data::UserSubscription
+ if (conn != nullptr)
+ {
+ conn->sendData(msg);
+- eventSeqNum++;
+ }
+
+ if (sseConn != nullptr)
+ {
+- sseConn->sendData(eventSeqNum, msg);
++ sseConn->sendEvent(std::to_string(eventSeqNum), msg);
+ }
++
++ eventSeqNum++;
+ return true;
+ }
+
+@@ -558,14 +559,39 @@ class Subscription : public persistent_data::UserSubscription
+ return eventSeqNum;
+ }
+
++ void setSubscriptionId(const std::string& id)
++ {
++ BMCWEB_LOG_DEBUG << "Subscription ID: " << id;
++ subId = id;
++ }
++
++ std::string getSubscriptionId()
++ {
++ return subId;
++ }
++
++ std::optional<std::string>
++ getSubscriptionId(const std::shared_ptr<crow::SseConnection>& connPtr)
++ {
++ if (sseConn != nullptr && connPtr == sseConn)
++ {
++ BMCWEB_LOG_DEBUG << __FUNCTION__
++ << " conn matched, subId: " << subId;
++ return subId;
++ }
++
++ return std::nullopt;
++ }
++
+ private:
++ std::shared_ptr<crow::SseConnection> sseConn = nullptr;
+ uint64_t eventSeqNum;
+ std::string host;
+ std::string port;
+ std::string path;
+ std::string uriProto;
+ std::shared_ptr<crow::HttpClient> conn = nullptr;
+- std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr;
++ std::string subId;
+ };
+
+ class EventServiceManager
+@@ -923,6 +949,8 @@ class EventServiceManager
+ subValue->updateRetryPolicy();
+ subValue->updatehttpHeaders();
+
++ // Set Subscription ID for back trace
++ subValue->setSubscriptionId(id);
+ return id;
+ }
+
+@@ -947,11 +975,40 @@ class EventServiceManager
+ }
+ }
+
++ void deleteSubscription(const std::shared_ptr<crow::SseConnection>& connPtr)
++ {
++ for (const auto& it : this->subscriptionsMap)
++ {
++ std::shared_ptr<Subscription> entry = it.second;
++ if (entry->subscriptionType == subscriptionTypeSSE)
++ {
++ std::optional<std::string> id =
++ entry->getSubscriptionId(connPtr);
++ if (id)
++ {
++ deleteSubscription(*id);
++ return;
++ }
++ }
++ }
++ }
++
+ size_t getNumberOfSubscriptions()
+ {
+ return subscriptionsMap.size();
+ }
+
++ size_t getNumberOfSSESubscriptions() const
++ {
++ auto count = std::count_if(
++ subscriptionsMap.begin(), subscriptionsMap.end(),
++ [this](const std::pair<std::string, std::shared_ptr<Subscription>>&
++ entry) {
++ return (entry.second->subscriptionType == subscriptionTypeSSE);
++ });
++ return static_cast<size_t>(count);
++ }
++
+ std::vector<std::string> getAllIDs()
+ {
+ std::vector<std::string> idList;
+diff --git a/redfish-core/include/server_sent_events.hpp b/redfish-core/include/server_sent_events.hpp
+deleted file mode 100644
+index 13840d3..0000000
+--- a/redfish-core/include/server_sent_events.hpp
++++ /dev/null
+@@ -1,286 +0,0 @@
+-
+-/*
+-// Copyright (c) 2020 Intel Corporation
+-//
+-// Licensed under the Apache License, Version 2.0 (the "License");
+-// you may not use this file except in compliance with the License.
+-// You may obtain a copy of the License at
+-//
+-// http://www.apache.org/licenses/LICENSE-2.0
+-//
+-// Unless required by applicable law or agreed to in writing, software
+-// distributed under the License is distributed on an "AS IS" BASIS,
+-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-// See the License for the specific language governing permissions and
+-// limitations under the License.
+-*/
+-#pragma once
+-
+-#include <boost/asio/strand.hpp>
+-#include <boost/beast/http/buffer_body.hpp>
+-#include <boost/beast/http/message.hpp>
+-#include <boost/beast/version.hpp>
+-
+-#include <cstdlib>
+-#include <functional>
+-#include <iostream>
+-#include <memory>
+-#include <queue>
+-#include <string>
+-
+-namespace crow
+-{
+-
+-static constexpr uint8_t maxReqQueueSize = 50;
+-
+-enum class SseConnState
+-{
+- startInit,
+- initInProgress,
+- initialized,
+- initFailed,
+- sendInProgress,
+- sendFailed,
+- idle,
+- suspended,
+- closed
+-};
+-
+-class ServerSentEvents : public std::enable_shared_from_this<ServerSentEvents>
+-{
+- private:
+- std::shared_ptr<boost::beast::tcp_stream> sseConn;
+- std::queue<std::pair<uint64_t, std::string>> requestDataQueue;
+- std::string outBuffer;
+- SseConnState state{SseConnState::startInit};
+- int retryCount{0};
+- int maxRetryAttempts{5};
+-
+- void sendEvent(const std::string& id, const std::string& msg)
+- {
+- if (msg.empty())
+- {
+- BMCWEB_LOG_DEBUG << "Empty data, bailing out.";
+- return;
+- }
+-
+- if (state == SseConnState::sendInProgress)
+- {
+- return;
+- }
+- state = SseConnState::sendInProgress;
+-
+- if (!id.empty())
+- {
+- outBuffer += "id: ";
+- outBuffer.append(id.begin(), id.end());
+- outBuffer += "\n";
+- }
+-
+- outBuffer += "data: ";
+- for (char character : msg)
+- {
+- outBuffer += character;
+- if (character == '\n')
+- {
+- outBuffer += "data: ";
+- }
+- }
+- outBuffer += "\n\n";
+-
+- doWrite();
+- }
+-
+- void doWrite()
+- {
+- if (outBuffer.empty())
+- {
+- BMCWEB_LOG_DEBUG << "All data sent successfully.";
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- requestDataQueue.pop();
+- state = SseConnState::idle;
+- checkQueue();
+- return;
+- }
+-
+- sseConn->async_write_some(
+- boost::asio::buffer(outBuffer.data(), outBuffer.size()),
+- [self(shared_from_this())](
+- boost::beast::error_code ec,
+- [[maybe_unused]] const std::size_t& bytesTransferred) {
+- self->outBuffer.erase(0, bytesTransferred);
+-
+- if (ec == boost::asio::error::eof)
+- {
+- // Send is successful, Lets remove data from queue
+- // check for next request data in queue.
+- self->requestDataQueue.pop();
+- self->state = SseConnState::idle;
+- self->checkQueue();
+- return;
+- }
+-
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "async_write_some() failed: "
+- << ec.message();
+- self->state = SseConnState::sendFailed;
+- self->checkQueue();
+- return;
+- }
+- BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: "
+- << bytesTransferred;
+-
+- self->doWrite();
+- });
+- }
+-
+- void startSSE()
+- {
+- if (state == SseConnState::initInProgress)
+- {
+- return;
+- }
+- state = SseConnState::initInProgress;
+-
+- BMCWEB_LOG_DEBUG << "starting SSE connection ";
+- using BodyType = boost::beast::http::buffer_body;
+- auto response =
+- std::make_shared<boost::beast::http::response<BodyType>>(
+- boost::beast::http::status::ok, 11);
+- auto serializer =
+- std::make_shared<boost::beast::http::response_serializer<BodyType>>(
+- *response);
+-
+- // TODO: Add hostname in http header.
+- response->set(boost::beast::http::field::server, "iBMC");
+- response->set(boost::beast::http::field::content_type,
+- "text/event-stream");
+- response->body().data = nullptr;
+- response->body().size = 0;
+- response->body().more = true;
+-
+- boost::beast::http::async_write_header(
+- *sseConn, *serializer,
+- [this, response,
+- serializer](const boost::beast::error_code& ec,
+- [[maybe_unused]] const std::size_t& bytesTransferred) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Error sending header" << ec;
+- state = SseConnState::initFailed;
+- checkQueue();
+- return;
+- }
+-
+- BMCWEB_LOG_DEBUG << "startSSE Header sent.";
+- state = SseConnState::initialized;
+- checkQueue();
+- });
+- }
+-
+- void checkQueue(const bool newRecord = false)
+- {
+- if (requestDataQueue.empty())
+- {
+- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n";
+- return;
+- }
+-
+- if (retryCount >= maxRetryAttempts)
+- {
+- BMCWEB_LOG_ERROR << "Maximum number of retries is reached.";
+-
+- // Clear queue.
+- while (!requestDataQueue.empty())
+- {
+- requestDataQueue.pop();
+- }
+-
+- // TODO: Take 'DeliveryRetryPolicy' action.
+- // For now, doing 'SuspendRetries' action.
+- state = SseConnState::suspended;
+- return;
+- }
+-
+- if ((state == SseConnState::initFailed) ||
+- (state == SseConnState::sendFailed))
+- {
+- if (newRecord)
+- {
+- // We are already running async wait and retry.
+- // Since record is added to queue, it gets the
+- // turn in FIFO.
+- return;
+- }
+-
+- retryCount++;
+- // TODO: Perform async wait for retryTimeoutInterval before proceed.
+- }
+- else
+- {
+- // reset retry count.
+- retryCount = 0;
+- }
+-
+- switch (state)
+- {
+- case SseConnState::initInProgress:
+- case SseConnState::sendInProgress:
+- case SseConnState::suspended:
+- case SseConnState::startInit:
+- case SseConnState::closed:
+- // do nothing
+- break;
+- case SseConnState::initFailed:
+- {
+- startSSE();
+- break;
+- }
+- case SseConnState::initialized:
+- case SseConnState::idle:
+- case SseConnState::sendFailed:
+- {
+- std::pair<uint64_t, std::string> reqData =
+- requestDataQueue.front();
+- sendEvent(std::to_string(reqData.first), reqData.second);
+- break;
+- }
+- }
+- }
+-
+- public:
+- ServerSentEvents(const ServerSentEvents&) = delete;
+- ServerSentEvents& operator=(const ServerSentEvents&) = delete;
+- ServerSentEvents(ServerSentEvents&&) = delete;
+- ServerSentEvents& operator=(ServerSentEvents&&) = delete;
+-
+- ServerSentEvents(const std::shared_ptr<boost::beast::tcp_stream>& adaptor) :
+- sseConn(adaptor)
+- {
+- startSSE();
+- }
+-
+- ~ServerSentEvents() = default;
+-
+- void sendData(const uint64_t& id, const std::string& data)
+- {
+- if (state == SseConnState::suspended)
+- {
+- return;
+- }
+-
+- if (requestDataQueue.size() <= maxReqQueueSize)
+- {
+- requestDataQueue.push(std::pair(id, data));
+- checkQueue(true);
+- }
+- else
+- {
+- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data.";
+- }
+- }
+-};
+-
+-} // namespace crow
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index c7392bd..2181346 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -40,8 +40,6 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = {
+ "Task"};
+ #endif
+
+-static constexpr const uint8_t maxNoOfSubscriptions = 20;
+-
+ inline void requestRoutesEventService(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
+@@ -54,6 +52,8 @@ inline void requestRoutesEventService(App& app)
+ {"@odata.type", "#EventService.v1_5_0.EventService"},
+ {"Id", "EventService"},
+ {"Name", "Event Service"},
++ {"ServerSentEventUri",
++ "/redfish/v1/EventService/Subscriptions/SSE"},
+ {"Subscriptions",
+ {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}},
+ {"Actions",
+@@ -92,9 +92,7 @@ inline void requestRoutesEventService(App& app)
+ .privileges(redfish::privileges::patchEventService)
+ .methods(boost::beast::http::verb::patch)(
+ [](const crow::Request& req,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+-
+- {
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ std::optional<bool> serviceEnabled;
+ std::optional<uint32_t> retryAttemps;
+ std::optional<uint32_t> retryInterval;
+diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp
+index 6bdce98..d8b4f4e 100644
+--- a/src/webserver_main.cpp
++++ b/src/webserver_main.cpp
+@@ -6,6 +6,7 @@
+ #include <cors_preflight.hpp>
+ #include <dbus_monitor.hpp>
+ #include <dbus_singleton.hpp>
++#include <eventservice_sse.hpp>
+ #include <google/google_service_root.hpp>
+ #include <hostname_monitor.hpp>
+ #include <ibm/management_console_rest.hpp>
+@@ -83,6 +84,7 @@ static int run()
+ #endif
+
+ #ifdef BMCWEB_ENABLE_REDFISH
++ redfish::eventservice_sse::requestRoutes(app);
+ redfish::requestRoutes(app);
+ redfish::RedfishService redfish(app);
+
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch
new file mode 100644
index 000000000..816456cec
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch
@@ -0,0 +1,326 @@
+From e207fca58bf0d05b160ea6735f4f576737d7a9cb Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Wed, 17 Mar 2021 01:16:50 +0000
+Subject: [PATCH] Add EventService SSE filter support
+
+This commit implements the Event Service SSE stream
+filters support. As per redfish specification:
+The SSE streams have these formats:
+ - Metric report SSE stream
+ - Event message SSE stream
+
+To reduce the amount of data, service supports $filter
+query parameter in SSE URI.
+Below properties support as filter criteria:
+ - EventFormatType( Event & MetricReport)
+ - MessageId
+ - RegistryPrefix
+ - MetricReportDefinition
+
+For more details, refer Redfish specification section 13.5.2
+
+Tested:
+ Created SSE stream with different filters and observed
+ desired events on SSE stream client(browser), some examples
+ - To get all Redfish events,
+ URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)
+ - To get Redfish events with RegistryPrefix "OpenBMC"
+ URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(RegistryPrefix%20eq%20OpenBMC)
+ - To get only DC power of Events,
+ URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)%20and%20(MessageId%20eq%20DCPowerOff)
+
+Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Change-Id: I55c6f53bb5e57aa1f2d1601f1a16525a33b13bd2
+---
+ include/eventservice_sse.hpp | 172 +++++++++++++++++-
+ redfish-core/include/error_messages.hpp | 9 +
+ .../include/event_service_manager.hpp | 5 +
+ redfish-core/lib/event_service.hpp | 5 -
+ redfish-core/src/error_messages.cpp | 27 +++
+ 5 files changed, 209 insertions(+), 9 deletions(-)
+
+diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp
+index 14daf00..5847766 100644
+--- a/include/eventservice_sse.hpp
++++ b/include/eventservice_sse.hpp
+@@ -21,18 +21,182 @@ static bool createSubscription(std::shared_ptr<crow::SseConnection>& conn,
+ res.end();
+ return false;
+ }
++#ifdef NEW_BOOST_URL
++ BMCWEB_LOG_DEBUG << "Request query param size: "
++ << req.urlView.params().size();
++
++ // EventService SSE supports only "$filter" query param.
++ if (req.urlView.params().size() > 1)
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ std::string eventFormatType;
++ std::string queryFilters;
++ if (req.urlView.params().size())
++ {
++ boost::urls::params_view::iterator it =
++ req.urlView.params().find("$filter");
++ if (it == req.urlView.params().end())
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ queryFilters = std::string((*it).value);
++ }
++ else
++ {
++ eventFormatType = "Event";
++ }
++#else
+ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size();
+
++ // EventService SSE supports only "$filter" query param.
++ if (req.urlParams.size() > 1)
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ std::string eventFormatType;
++ std::string queryFilters;
++ if (req.urlParams.size())
++ {
++ boost::urls::query_params_view::iterator it =
++ req.urlParams.find("$filter");
++ if (it == req.urlParams.end())
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++ queryFilters = it->value();
++ }
++ else
++ {
++ eventFormatType = "Event";
++ }
++#endif
++
++ std::vector<std::string> msgIds;
++ std::vector<std::string> regPrefixes;
++ std::vector<std::string> mrdsArray;
++ if (!queryFilters.empty())
++ {
++ // Reading from query params.
++ bool status = readSSEQueryParams(queryFilters, eventFormatType, msgIds,
++ regPrefixes, mrdsArray);
++ if (!status)
++ {
++ messages::invalidQueryFilter(res);
++ res.end();
++ return false;
++ }
++
++ // RegsitryPrefix and messageIds are mutuly exclusive as per redfish
++ // specification.
++ if (!regPrefixes.empty() && !msgIds.empty())
++ {
++ messages::propertyValueConflict(res, "RegistryPrefix", "MessageId");
++ res.end();
++ return false;
++ }
++
++ if (!eventFormatType.empty())
++ {
++ if (std::find(supportedEvtFormatTypes.begin(),
++ supportedEvtFormatTypes.end(),
++ eventFormatType) == supportedEvtFormatTypes.end())
++ {
++ messages::propertyValueNotInList(res, eventFormatType,
++ "EventFormatType");
++ res.end();
++ return false;
++ }
++ }
++ else
++ {
++ // If nothing specified, using default "Event"
++ eventFormatType = "Event";
++ }
++
++ if (!regPrefixes.empty())
++ {
++ for (const std::string& it : regPrefixes)
++ {
++ if (std::find(supportedRegPrefixes.begin(),
++ supportedRegPrefixes.end(),
++ it) == supportedRegPrefixes.end())
++ {
++ messages::propertyValueNotInList(res, it, "RegistryPrefix");
++ res.end();
++ return false;
++ }
++ }
++ }
++
++ if (!msgIds.empty())
++ {
++ std::vector<std::string> registryPrefix;
++
++ // If no registry prefixes are mentioned, consider all supported
++ // prefixes to validate message ID
++ if (regPrefixes.empty())
++ {
++ registryPrefix.assign(supportedRegPrefixes.begin(),
++ supportedRegPrefixes.end());
++ }
++ else
++ {
++ registryPrefix = regPrefixes;
++ }
++
++ for (const std::string& id : msgIds)
++ {
++ bool validId = false;
++
++ // Check for Message ID in each of the selected Registry
++ for (const std::string& it : registryPrefix)
++ {
++ const std::span<const redfish::registries::MessageEntry>
++ registry =
++ redfish::registries::getRegistryFromPrefix(it);
++
++ if (std::any_of(
++ registry.begin(), registry.end(),
++ [&id](const redfish::registries::MessageEntry&
++ messageEntry) {
++ return !id.compare(messageEntry.first);
++ }))
++ {
++ validId = true;
++ break;
++ }
++ }
++
++ if (!validId)
++ {
++ messages::propertyValueNotInList(res, id, "MessageIds");
++ res.end();
++ return false;
++ }
++ }
++ }
++ }
++
+ std::shared_ptr<redfish::Subscription> subValue =
+ std::make_shared<redfish::Subscription>(std::move(conn));
+
+ // GET on this URI means, Its SSE subscriptionType.
+- subValue->subscriptionType = redfish::subscriptionTypeSSE;
+-
+- // TODO: parse $filter query params and fill config.
++ subValue->subscriptionType = subscriptionTypeSSE;
+ subValue->protocol = "Redfish";
+ subValue->retryPolicy = "TerminateAfterRetries";
+- subValue->eventFormatType = "Event";
++ subValue->eventFormatType = eventFormatType;
++ subValue->registryMsgIds = msgIds;
++ subValue->registryPrefixes = regPrefixes;
++ subValue->metricReportDefinitions = mrdsArray;
+
+ std::string id =
+ redfish::EventServiceManager::getInstance().addSubscription(subValue,
+diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp
+index b3b2305..9b3ee49 100644
+--- a/redfish-core/include/error_messages.hpp
++++ b/redfish-core/include/error_messages.hpp
+@@ -980,6 +980,15 @@ nlohmann::json invalidUpload(std::string_view arg1, std::string_view arg2);
+ void invalidUpload(crow::Response& res, std::string_view arg1,
+ std::string_view arg2);
+
++/**
++ * @brief Formats InvalidQueryFilter message into JSON
++ * Message body: "The requested URL contains the invalid query filters"
++ *
++ * @returns Message InvalidQueryFilter formatted to JSON */
++nlohmann::json invalidQueryFilter();
++
++void invalidQueryFilter(crow::Response& res);
++
+ } // namespace messages
+
+ } // namespace redfish
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index c64bb59..e03dd9b 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -57,6 +57,11 @@ static constexpr const char* eventServiceFile =
+ static constexpr const uint8_t maxNoOfSubscriptions = 20;
+ static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
+
++static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
++ eventFormatType, metricReportFormatType};
++static constexpr const std::array<const char*, 2> supportedRegPrefixes = {
++ "OpenBMC", "TaskEvent"};
++
+ #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+ static std::optional<boost::asio::posix::stream_descriptor> inotifyConn;
+ static constexpr const char* redfishEventLogDir = "/var/log";
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index 2181346..e9ee78f 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -24,11 +24,6 @@
+
+ namespace redfish
+ {
+-
+-static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
+- eventFormatType, metricReportFormatType};
+-static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
+- "Base", "OpenBMC", "TaskEvent"};
+ static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
+ "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
+
+diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp
+index 381b8a9..cad145a 100644
+--- a/redfish-core/src/error_messages.cpp
++++ b/redfish-core/src/error_messages.cpp
+@@ -1668,6 +1668,33 @@ nlohmann::json invalidUpload(std::string_view arg1, std::string_view arg2)
+ {"MessageSeverity", "Warning"},
+ {"Resolution", "None."}};
+ }
++
++/**
++ * @internal
++ * @brief Formats InvalidQueryFilter into JSON
++ *
++ * See header file for more information
++ * @endinternal
++ */
++nlohmann::json invalidQueryFilter()
++{
++ return nlohmann::json{
++ {"@odata.type", "#Message.v1_0_0.Message"},
++ {"MessageId", "Base.1.5.0.InvalidQueryFilter"},
++ {"Message", "The requested url contains the invalid query filter."},
++ {"MessageArgs", nlohmann::json::array()},
++ {"Severity", "Warning"},
++ {"Resolution",
++ "Ensure the correct query filter is specified in requested url "
++ "and resubmit the request."}};
++}
++
++void invalidQueryFilter(crow::Response& res)
++{
++ res.result(boost::beast::http::status::bad_request);
++ addMessageToErrorJson(res.jsonValue, invalidQueryFilter());
++}
++
+ } // namespace messages
+
+ } // namespace redfish
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch
new file mode 100644
index 000000000..3818b99ee
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0007-EventService-Log-events-for-subscription-actions.patch
@@ -0,0 +1,133 @@
+From ec3349511d66a7136a054c1a4e7a6f24ea3b6722 Mon Sep 17 00:00:00 2001
+From: AppaRao Puli <apparao.puli@linux.intel.com>
+Date: Fri, 27 Aug 2021 16:02:01 +0000
+Subject: [PATCH] EventService: Log events for subscription actions
+
+Log redfish event for below 3 actions
+ - Add new subscription
+ - Update existing subscription properties
+ - Delete existing subscription
+in order to notify the subscribed clients on the subscription related
+information.
+
+Modified method name accordingly to indicate the clear purpose and
+added updateSubscription method with subscription id param
+to log event for subscription update.
+
+Tested:
+ - Performed all the above actions and verified the redfish event
+ messages are logged.
+
+Change-Id: I3745fa6357bd215379781a9818d9acc02a853d79
+Signed-off-by: AppaRao Puli <apparao.puli@intel.com>
+Signed-off-by: Ayushi Smriti <smriti.ayushi@intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ .../include/event_service_manager.hpp | 35 ++++++++++++++++---
+ redfish-core/lib/event_service.hpp | 2 +-
+ 2 files changed, 32 insertions(+), 5 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index f2b7803..5483a74 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -21,6 +21,7 @@
+ #include "registries/task_event_message_registry.hpp"
+
+ #include <sys/inotify.h>
++#include <systemd/sd-journal.h>
+
+ #include <boost/asio/io_context.hpp>
+ #include <boost/beast/core/span.hpp>
+@@ -782,7 +783,7 @@ class EventServiceManager
+ }
+ }
+
+- void updateSubscriptionData() const
++ void persistSubscriptionData()
+ {
+ persistent_data::EventServiceStore::getInstance()
+ .eventServiceConfig.enabled = serviceEnabled;
+@@ -829,7 +830,7 @@ class EventServiceManager
+
+ if (updateConfig)
+ {
+- updateSubscriptionData();
++ persistSubscriptionData();
+ }
+
+ if (updateRetryCfg)
+@@ -941,7 +942,7 @@ class EventServiceManager
+
+ if (updateFile)
+ {
+- updateSubscriptionData();
++ persistSubscriptionData();
+ }
+
+ #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+@@ -957,6 +958,13 @@ class EventServiceManager
+
+ // Set Subscription ID for back trace
+ subValue->setSubscriptionId(id);
++
++ /* Log event for subscription addition */
++ sd_journal_send("MESSAGE=Event subscription added(Id: %s)", id.c_str(),
++ "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.EventSubscriptionAdded",
++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL);
++
+ return id;
+ }
+
+@@ -981,7 +989,14 @@ class EventServiceManager
+ persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap.erase(obj2);
+ updateNoOfSubscribersCount();
+- updateSubscriptionData();
++
++ persistSubscriptionData();
++ /* Log event for subscription delete. */
++ sd_journal_send("MESSAGE=Event subscription removed.(Id = %s)",
++ id.c_str(), "PRIORITY=%i", LOG_INFO,
++ "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.EventSubscriptionRemoved",
++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL);
+ }
+ }
+
+@@ -1003,6 +1018,18 @@ class EventServiceManager
+ }
+ }
+
++ void updateSubscription(const std::string& id)
++ {
++ persistSubscriptionData();
++
++ /* Log event for subscription update. */
++ sd_journal_send("MESSAGE=Event subscription updated.(Id = %s)",
++ id.c_str(), "PRIORITY=%i", LOG_INFO,
++ "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.EventSubscriptionUpdated",
++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL);
++ }
++
+ size_t getNumberOfSubscriptions()
+ {
+ return subscriptionsMap.size();
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index ab85796..91ce22c 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -630,7 +630,7 @@ inline void requestRoutesEventDestination(App& app)
+ subValue->updateRetryPolicy();
+ }
+
+- EventServiceManager::getInstance().updateSubscriptionData();
++ EventServiceManager::getInstance().updateSubscription(param);
+ });
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
+ // The below privilege is wrong, it should be ConfigureManager OR
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch
new file mode 100644
index 000000000..65bfafdcc
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch
@@ -0,0 +1,85 @@
+From a22fcc269c475b0ff8a80a6aa2c837b247eca907 Mon Sep 17 00:00:00 2001
+From: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com>
+Date: Mon, 28 Jun 2021 19:59:57 +0000
+Subject: [PATCH] Add checks on Event Subscription input parameters
+
+There is no check on the size of input parameters(Context,
+Destination and Header) during Event Subscription.This
+creates out of memory situation.
+This commit checks for the size of input parameters and
+rejects if it is exceeding the input size limits.
+
+Tested
+ - Validated using POST on Event Subscription.
+ - When Context, Destination and Headers were too long,
+ received a error message denoting the same.
+
+Change-Id: Iec2cd766c0e137b72706fc2da468d4fefd8fbaae
+Signed-off-by: Nitin Wankhade <nitinx.arunrao.wankhade@intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ redfish-core/lib/event_service.hpp | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
+index 88ebd01..373c873 100644
+--- a/redfish-core/lib/event_service.hpp
++++ b/redfish-core/lib/event_service.hpp
+@@ -22,6 +22,10 @@
+
+ #include <span>
+
++#define MAX_CONTEXT_SIZE 256
++#define MAX_DESTINATION_SIZE 1024
++#define MAX_HEADER_SIZE 8096
++
+ namespace redfish
+ {
+ static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
+@@ -229,6 +233,12 @@ inline void requestRoutesEventDestinationCollection(App& app)
+ return;
+ }
+
++ if (destUrl.size() > MAX_DESTINATION_SIZE)
++ {
++ messages::propertySizeExceeded(asyncResp->res, "Destination");
++ return;
++ }
++
+ if (regPrefixes && msgIds)
+ {
+ if (!regPrefixes->empty() && !msgIds->empty())
+@@ -336,13 +346,30 @@ inline void requestRoutesEventDestinationCollection(App& app)
+
+ if (context)
+ {
++ if (context->size() > MAX_CONTEXT_SIZE)
++ {
++ messages::propertySizeExceeded(asyncResp->res, "Context");
++ return;
++ }
+ subValue->customText = *context;
+ }
+
+ if (headers)
+ {
++ size_t cumulativeLen = 0;
++
+ for (const nlohmann::json& headerChunk : *headers)
+ {
++ std::string hdr{headerChunk.dump(
++ -1, ' ', true,
++ nlohmann::json::error_handler_t::replace)};
++ cumulativeLen += hdr.length();
++ if (cumulativeLen > MAX_HEADER_SIZE)
++ {
++ messages::propertySizeExceeded(asyncResp->res,
++ "HttpHeaders");
++ return;
++ }
+ for (const auto& item : headerChunk.items())
+ {
+ const std::string* value =
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch
new file mode 100644
index 000000000..ede279868
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0010-Remove-Terminated-Event-Subscriptions.patch
@@ -0,0 +1,267 @@
+From fc4b7e964f5f26e7af2eb19f120b1762b70b8179 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Tue, 12 Oct 2021 08:19:51 +0000
+Subject: [PATCH] Delete/Remove Terminated Event Subscription(s)
+
+Added functionality to delete/remove event subscription(s) which are
+configured to Terminate after retries.
+
+Currently, when an Event is subscribed with Retry Policy as
+"TerminateAfterRetries", the state of the connection is set to
+"Terminated" after retrying, but the Subscription is not removed.
+This commit adds the functionality to detect terminated connection and
+remove the respective subscription.
+
+Tested:
+ - Created a Subscription with
+ DeliveryRetryPolicy: "TerminateAfterRetries"
+ - Received Events successfully on Event listener
+ - Once the Event listener was stopped, the Subscription was
+ removed/deleted after retries.
+
+Change-Id: If447acb2db74fb29a5d1cfe6194b77cda82bc8a1
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_client.hpp | 48 +++++++++++++++----
+ .../include/event_service_manager.hpp | 36 ++++++++++++++
+ 2 files changed, 75 insertions(+), 9 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index 7328eae..db99faa 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -56,6 +56,8 @@ enum class ConnState
+ closeInProgress,
+ closed,
+ suspended,
++ terminate,
++ terminateInProgress,
+ terminated,
+ abortConnection,
+ retry
+@@ -290,7 +292,14 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ void doClose()
+ {
+- state = ConnState::closeInProgress;
++ if (state == ConnState::terminate)
++ {
++ state = ConnState::terminateInProgress;
++ }
++ else if (state != ConnState::suspended)
++ {
++ state = ConnState::closeInProgress;
++ }
+
+ // Set the timeout on the tcp stream socket for the async operation
+ conn.expires_after(std::chrono::seconds(30));
+@@ -320,8 +329,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ self->conn.close();
+
+- if ((self->state != ConnState::suspended) &&
+- (self->state != ConnState::terminated))
++ if (self->state == ConnState::terminateInProgress)
++ {
++ self->state = ConnState::terminated;
++ }
++ else if (self->state == ConnState::closeInProgress)
+ {
+ self->state = ConnState::closed;
+ self->handleConnState();
+@@ -343,8 +355,11 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ conn.close();
+
+- if ((state != ConnState::suspended) &&
+- (state != ConnState::terminated))
++ if (state == ConnState::terminateInProgress)
++ {
++ state = ConnState::terminated;
++ }
++ else if (state == ConnState::closeInProgress)
+ {
+ state = ConnState::closed;
+ handleConnState();
+@@ -367,8 +382,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ BMCWEB_LOG_DEBUG << "Retry policy: " << retryPolicyAction;
+ if (retryPolicyAction == "TerminateAfterRetries")
+ {
+- // TODO: delete subscription
+- state = ConnState::terminated;
++ state = ConnState::terminate;
+ }
+ if (retryPolicyAction == "SuspendRetries")
+ {
+@@ -424,6 +438,7 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ case ConnState::sendInProgress:
+ case ConnState::recvInProgress:
+ case ConnState::closeInProgress:
++ case ConnState::terminateInProgress:
+ {
+ BMCWEB_LOG_DEBUG << "Async operation is already in progress";
+ break;
+@@ -440,11 +455,16 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ break;
+ }
+ case ConnState::suspended:
+- case ConnState::terminated:
++ case ConnState::terminate:
+ {
+ doClose();
+ break;
+ }
++ case ConnState::terminated:
++ {
++ BMCWEB_LOG_DEBUG << "Connection Terminated";
++ break;
++ }
+ case ConnState::resolveFailed:
+ case ConnState::connectFailed:
+ case ConnState::handshakeFailed:
+@@ -504,7 +524,8 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ void sendData(const std::string& data)
+ {
+- if ((state == ConnState::suspended) || (state == ConnState::terminated))
++ if ((state == ConnState::terminate) ||
++ (state == ConnState::terminated) || (state == ConnState::suspended))
+ {
+ return;
+ }
+@@ -520,6 +541,15 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ }
+ }
+
++ bool isTerminated()
++ {
++ if (state == ConnState::terminated)
++ {
++ return true;
++ }
++ return false;
++ }
++
+ void setHeaders(const boost::beast::http::fields& httpHeaders)
+ {
+ // Set custom headers
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 5483a74..92abaa5 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -590,6 +590,14 @@ class Subscription : public persistent_data::UserSubscription
+ return std::nullopt;
+ }
+
++ bool isTerminated()
++ {
++ if (conn != nullptr)
++ return conn->isTerminated();
++
++ return false;
++ }
++
+ private:
+ std::shared_ptr<crow::SseConnection> sseConn = nullptr;
+ uint64_t eventSeqNum;
+@@ -845,6 +853,22 @@ class EventServiceManager
+ }
+ }
+
++ void deleteTerminatedSubcriptions()
++ {
++ boost::container::flat_map<std::string,
++ std::shared_ptr<Subscription>>::iterator it =
++ subscriptionsMap.begin();
++ while (it != subscriptionsMap.end())
++ {
++ std::shared_ptr<Subscription> entry = it->second;
++ if (entry->isTerminated())
++ {
++ subscriptionsMap.erase(it);
++ }
++ it++;
++ }
++ }
++
+ void updateNoOfSubscribersCount()
+ {
+ size_t eventLogSubCount = 0;
+@@ -879,6 +903,7 @@ class EventServiceManager
+
+ std::shared_ptr<Subscription> getSubscription(const std::string& id)
+ {
++ deleteTerminatedSubcriptions();
+ auto obj = subscriptionsMap.find(id);
+ if (obj == subscriptionsMap.end())
+ {
+@@ -970,6 +995,7 @@ class EventServiceManager
+
+ bool isSubscriptionExist(const std::string& id)
+ {
++ deleteTerminatedSubcriptions();
+ auto obj = subscriptionsMap.find(id);
+ if (obj == subscriptionsMap.end())
+ {
+@@ -1032,6 +1058,7 @@ class EventServiceManager
+
+ size_t getNumberOfSubscriptions()
+ {
++ deleteTerminatedSubcriptions();
+ return subscriptionsMap.size();
+ }
+
+@@ -1048,6 +1075,7 @@ class EventServiceManager
+
+ std::vector<std::string> getAllIDs()
+ {
++ deleteTerminatedSubcriptions();
+ std::vector<std::string> idList;
+ for (const auto& it : subscriptionsMap)
+ {
+@@ -1058,6 +1086,7 @@ class EventServiceManager
+
+ bool isDestinationExist(const std::string& destUrl)
+ {
++ deleteTerminatedSubcriptions();
+ for (const auto& it : subscriptionsMap)
+ {
+ std::shared_ptr<Subscription> entry = it.second;
+@@ -1072,6 +1101,7 @@ class EventServiceManager
+
+ bool sendTestEventLog()
+ {
++ deleteTerminatedSubcriptions();
+ for (const auto& it : this->subscriptionsMap)
+ {
+ std::shared_ptr<Subscription> entry = it.second;
+@@ -1108,6 +1138,8 @@ class EventServiceManager
+ }
+ eventRecord.push_back(eventMessage);
+
++ deleteTerminatedSubcriptions();
++
+ for (const auto& it : this->subscriptionsMap)
+ {
+ std::shared_ptr<Subscription> entry = it.second;
+@@ -1151,6 +1183,8 @@ class EventServiceManager
+ }
+ void sendBroadcastMsg(const std::string& broadcastMsg)
+ {
++ deleteTerminatedSubcriptions();
++
+ for (const auto& it : this->subscriptionsMap)
+ {
+ std::shared_ptr<Subscription> entry = it.second;
+@@ -1263,6 +1297,8 @@ class EventServiceManager
+ return;
+ }
+
++ deleteTerminatedSubcriptions();
++
+ for (const auto& it : this->subscriptionsMap)
+ {
+ std::shared_ptr<Subscription> entry = it.second;
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch
new file mode 100644
index 000000000..87f0a4fd9
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch
@@ -0,0 +1,141 @@
+From ff562320d23e1c1e075689a636505f22eb4890d4 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Thu, 14 Oct 2021 02:56:11 +0530
+Subject: [PATCH] Fix bmcweb crash while deleting terminated subscriptions
+
+This commit fixes bmcweb crash while deleting the terminated
+subscriptions. In the earlier implementation, detection of subscription
+to be deleted and the deletion(erase) was happening in the same loop.
+Due to this, if the Subscription to be deleted is the last one in the
+list, the loop will enter into infinite loop. The fix is to keep the
+detection and deletion loop separate.
+Also, this commit adds code to :
+ - Delete from persistent storage
+ - Add journal entry for deleted entry
+ - update number of subcribers and update persistent storage.
+
+Apart from this, this commit also moves the retry timer check to the top
+to avoid multiple calls to close when the retry count is 3 and timer is
+running.
+
+Tested:
+ - Checked journal logs to confirm each retry is actually spanned to be
+ 30 secs
+ - Verified Journal entry for deleted subscription after retires.
+ - Verified Event service functionality by making three subscriptions:
+ retry for ever, terminate after retires and suspend after retries.
+
+Change-Id: I425a6c749923ce86c457a36394deb0fbbee232db
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/http_client.hpp | 11 ++--
+ .../include/event_service_manager.hpp | 59 ++++++++++++++++---
+ 2 files changed, 58 insertions(+), 12 deletions(-)
+
+diff --git a/http/http_client.hpp b/http/http_client.hpp
+index 745eeb6..5575765 100644
+--- a/http/http_client.hpp
++++ b/http/http_client.hpp
+@@ -369,6 +369,12 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+
+ void waitAndRetry()
+ {
++ if (runningTimer)
++ {
++ BMCWEB_LOG_DEBUG << "Retry timer is already running.";
++ return;
++ }
++
+ if (retryCount >= maxRetryAttempts)
+ {
+ BMCWEB_LOG_ERROR << "Maximum number of retries reached.";
+@@ -395,11 +401,6 @@ class HttpClient : public std::enable_shared_from_this<HttpClient>
+ return;
+ }
+
+- if (runningTimer)
+- {
+- BMCWEB_LOG_DEBUG << "Retry timer is already running.";
+- return;
+- }
+ runningTimer = true;
+
+ retryCount++;
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 5d71c63..f97909c 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -853,18 +853,63 @@ class EventServiceManager
+
+ void deleteTerminatedSubcriptions()
+ {
+- boost::container::flat_map<std::string,
+- std::shared_ptr<Subscription>>::iterator it =
+- subscriptionsMap.begin();
+- while (it != subscriptionsMap.end())
++ BMCWEB_LOG_ERROR << "Map size Before Delete : "
++ << subscriptionsMap.size();
++
++ std::vector<std::string> deleteIds;
++
++ // Determine Subscription ID's to be deleted.
++ for (const auto& it : subscriptionsMap)
+ {
+- std::shared_ptr<Subscription> entry = it->second;
++ std::shared_ptr<Subscription> entry = it.second;
+ if (entry->isTerminated())
+ {
+- subscriptionsMap.erase(it);
++ deleteIds.emplace_back(it.first);
++ }
++ }
++
++ // Delete the Terminated Subcriptions
++ for (std::string& id : deleteIds)
++ {
++ auto map1 = subscriptionsMap.find(id);
++ if (map1 != subscriptionsMap.end())
++ {
++ subscriptionsMap.erase(map1);
++ auto map2 = persistent_data::EventServiceStore::getInstance()
++ .subscriptionsConfigMap.find(id);
++ if (map2 != persistent_data::EventServiceStore::getInstance()
++ .subscriptionsConfigMap.end())
++ {
++ persistent_data::EventServiceStore::getInstance()
++ .subscriptionsConfigMap.erase(map2);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Couldn't find ID: " << id
++ << " in subscriptionsConfigMap";
++ }
++
++ /* Log event for subscription delete. */
++ sd_journal_send("MESSAGE=Event subscription removed.(Id = %s)",
++ id.c_str(), "PRIORITY=%i", LOG_INFO,
++ "REDFISH_MESSAGE_ID=%s",
++ "OpenBMC.0.1.EventSubscriptionRemoved",
++ "REDFISH_MESSAGE_ARGS=%s", id.c_str(), NULL);
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR << "Couldn't find ID: " << id
++ << " in subscriptionsMap";
+ }
+- it++;
+ }
++ if (deleteIds.size())
++ {
++ updateNoOfSubscribersCount();
++ persistSubscriptionData();
++ }
++
++ BMCWEB_LOG_ERROR << "Map size After Delete : "
++ << subscriptionsMap.size();
+ }
+
+ void updateNoOfSubscribersCount()
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch
new file mode 100644
index 000000000..61503904c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch
@@ -0,0 +1,66 @@
+From b132263ef2f3bb3fd2766154c00b7b00c1e49fb6 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Tue, 7 Dec 2021 09:48:07 +0100
+Subject: [PATCH] Add support for deleting terminated subscriptions
+
+Added functionality to delete/remove event subscription(s) which are
+configured to Terminate after retries.
+
+Currently, when an Event is subscribed with Retry Policy as
+"TerminateAfterRetries", the state of the connection is set to
+"Terminated" after retrying, but the Subscription is not removed.
+This commit adds the functionality to detect terminated connection and
+remove the respective subscription.
+
+This commit adds this check for metric reports.
+
+Tested:
+ - Created a Subscription with
+ DeliveryRetryPolicy: "TerminateAfterRetries"
+ - Received Events successfully on Event listener
+ - Once the Event listener was stopped, the Subscription was
+ removed/deleted after retries.
+
+Change-Id: I3cb0af5bc24411cddcdb3d1d9de25e8e9144106c
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ redfish-core/include/event_service_manager.hpp | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index c68b707..6a93fc0 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -1486,7 +1486,7 @@ class EventServiceManager
+ }
+
+ #endif
+- static void getReadingsForReport(sdbusplus::message::message& msg)
++ void getReadingsForReport(sdbusplus::message::message& msg)
+ {
+ if (msg.is_method_error())
+ {
+@@ -1525,6 +1525,8 @@ class EventServiceManager
+ return;
+ }
+
++ deleteTerminatedSubcriptions();
++
+ for (const auto& it :
+ EventServiceManager::getInstance().subscriptionsMap)
+ {
+@@ -1560,7 +1562,10 @@ class EventServiceManager
+ "arg0=xyz.openbmc_project.Telemetry.Report";
+
+ matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>(
+- *crow::connections::systemBus, matchStr, getReadingsForReport);
++ *crow::connections::systemBus, matchStr,
++ [this](sdbusplus::message::message& msg) {
++ getReadingsForReport(msg);
++ });
+ }
+ };
+
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0013-event-service-fix-added-Context-field-to-response.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0013-event-service-fix-added-Context-field-to-response.patch
new file mode 100644
index 000000000..019ccbe64
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0013-event-service-fix-added-Context-field-to-response.patch
@@ -0,0 +1,33 @@
+From ce9b52791e76d73050f053f8fc607c6e1eb5d8c4 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Thu, 16 Dec 2021 10:46:55 +0100
+Subject: [PATCH] event service fix, added Context field to response
+
+Tested:
+ - Context field is present
+ - No regression detected
+
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+---
+ redfish-core/include/event_service_manager.hpp | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index 881d2db..1ba9f21 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -530,6 +530,11 @@ class Subscription : public persistent_data::UserSubscription
+ return;
+ }
+
++ if (!customText.empty())
++ {
++ msg["Context"] = customText;
++ }
++
+ this->sendEvent(
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace));
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README
new file mode 100644
index 000000000..617d6f3e8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README
@@ -0,0 +1,37 @@
+Eventservice specific patches: Temporary pulling down
+the upstream patches. These will be remove as soon as
+thee gets merged upstream.
+
+Upstream revision information:
+ - EventService : Add unmerged changes for http retry support (Downstream patch)
+ file://eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch
+
+ - EventService: https client support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31735/40 (Rebased on latest bmcweb)
+
+ - Add Server-Sent-Events support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41258/9
+
+ - Add SSE style subscription support to eventservice
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41319/10
+
+ - Add EventService SSE filter support
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41349/7 (Modified boost::urls::query_params_view to boost::urls::url_view::params_type)
+
+ - EventService Log events for subscription actions (Downstream patch)
+ file://eventservice/0007-EventService-Log-events-for-subscription-actions.patch
+
+ - Add checks on Event-Subscription input parameters (Downstream patch)
+ file://eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch
+
+ - Remove Terminated Event Subscriptions (Downstream patch)
+ file://eventservice/0010-Remove-Terminated-Event-Subscriptions.patch
+
+ - Fix bmcweb crash while deleting terminated subscriptions (Downstream patch)
+ file://eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch
+
+ - Add support for deleting terminated subscriptions
+ file://eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch
+
+ - event service fix added Context field to response
+ file://eventservice/0013-event-service-fix-added-Context-field-to-response.patch
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch
new file mode 100644
index 000000000..6a6882971
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch
@@ -0,0 +1,194 @@
+From aa8aed7069c3654d288b8198d1cd21fadb6e2a36 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 18 Oct 2021 22:45:37 +0530
+Subject: [PATCH] Add asyncResp support during handleUpgrade
+
+The current implementation uses the earlier method of using the response
+object and calling response.end() to initiate completion handler.
+This commit modifies the implementation to use asyncResp, where the
+completion handler gets called asynchronously as the response object
+goes out of scope.
+
+Tested :
+ - websocket_test.py Passed
+ - KVM was functional in WebUI.
+ - POST to /redfish/v1/EventService/Subscriptions/SSE returned an error
+ message as expected and the connection was kept alive.
+ - GET on /redfish/v1/EventService/Subscriptions/SSE (SSE subscription)
+ was successful. The existing connection was successfully closed and
+ upgraded to SSE connection.
+
+Change-Id: I2d76b34a49a6432c507d939b21b37c1ced761f8e
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/app.hpp | 6 ++++--
+ http/http_connection.hpp | 30 +++++++++++++++++++++++----
+ http/routing.hpp | 37 +++++++++++++++++++++----------------
+ 3 files changed, 51 insertions(+), 22 deletions(-)
+
+diff --git a/http/app.hpp b/http/app.hpp
+index c1472e5..7e946dc 100644
+--- a/http/app.hpp
++++ b/http/app.hpp
+@@ -51,9 +51,11 @@ class App
+ App& operator=(const App&&) = delete;
+
+ template <typename Adaptor>
+- void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
++ void handleUpgrade(const Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ Adaptor&& adaptor)
+ {
+- router.handleUpgrade(req, res, std::forward<Adaptor>(adaptor));
++ router.handleUpgrade(req, asyncResp, std::forward<Adaptor>(adaptor));
+ }
+
+ void handle(Request& req,
+diff --git a/http/http_connection.hpp b/http/http_connection.hpp
+index 8d76b25..9911ced 100644
+--- a/http/http_connection.hpp
++++ b/http/http_connection.hpp
+@@ -381,10 +381,32 @@ class Connection :
+ (req->url == "/redfish/v1/EventService/Subscriptions/SSE"))
+ {
+ BMCWEB_LOG_DEBUG << "Request: " << this << " is getting upgraded";
+- handler->handleUpgrade(thisReq, res, std::move(adaptor));
+- // delete lambda with self shared_ptr
+- // to enable connection destruction
+- asyncResp->res.setCompleteRequestHandler(nullptr);
++ asyncResp->res.setCompleteRequestHandler(
++ [self(shared_from_this())](crow::Response& thisRes) {
++ if (self->res.resultInt() != 200)
++ {
++ // When any error occurs during handle upgradation,
++ // the result in response will be set to respective
++ // error. By default the Result will be OK (200),
++ // which implies successful handle upgrade. Response
++ // needs to be sent over this connection only on
++ // failure.
++ boost::asio::post(self->adaptor.get_executor(),
++ [self, &thisRes] {
++ self->completeRequest(thisRes);
++ });
++ }
++ else
++ {
++ // Set Complete request handler to NULL to remove
++ // the shared pointer of connection to enable
++ // connection destruction. As the connection would
++ // get upgraded, we wouldn't need this connection
++ // any longer
++ self->res.setCompleteRequestHandler(nullptr);
++ }
++ });
++ handler->handleUpgrade(thisReq, asyncResp, std::move(adaptor));
+ return;
+ }
+ handler->handle(thisReq, asyncResp);
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 552e1cf..4bb81f2 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -1215,12 +1215,13 @@ class Router
+ }
+
+ template <typename Adaptor>
+- void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor)
++ void handleUpgrade(const Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ Adaptor&& adaptor)
+ {
+ if (static_cast<size_t>(req.method()) >= perMethods.size())
+ {
+- res.result(boost::beast::http::status::not_found);
+- res.end();
++ asyncResp->res.result(boost::beast::http::status::not_found);
+ return;
+ }
+
+@@ -1233,8 +1234,7 @@ class Router
+ if (ruleIndex == 0U)
+ {
+ BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url;
+- res.result(boost::beast::http::status::not_found);
+- res.end();
++ asyncResp->res.result(boost::beast::http::status::not_found);
+ return;
+ }
+
+@@ -1247,23 +1247,24 @@ class Router
+ {
+ BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: "
+ << req.url;
+- res.result(boost::beast::http::status::moved_permanently);
++ asyncResp->res.result(
++ boost::beast::http::status::moved_permanently);
+
+ // TODO absolute url building
+ if (req.getHeaderValue("Host").empty())
+ {
+- res.addHeader("Location", std::string(req.url) + "/");
++ asyncResp->res.addHeader("Location",
++ std::string(req.url) + "/");
+ }
+ else
+ {
+- res.addHeader(
++ asyncResp->res.addHeader(
+ "Location",
+ req.isSecure
+ ? "https://"
+ : "http://" + std::string(req.getHeaderValue("Host")) +
+ std::string(req.url) + "/");
+ }
+- res.end();
+ return;
+ }
+
+@@ -1274,8 +1275,7 @@ class Router
+ << " with " << req.methodString() << "("
+ << static_cast<uint32_t>(req.method()) << ") / "
+ << rules[ruleIndex]->getMethods();
+- res.result(boost::beast::http::status::not_found);
+- res.end();
++ asyncResp->res.result(boost::beast::http::status::not_found);
+ return;
+ }
+
+@@ -1286,14 +1286,19 @@ class Router
+ // any uncaught exceptions become 500s
+ try
+ {
+- rules[ruleIndex]->handleUpgrade(req, res,
++ // Creating temporary response object to call handleUpgrade
++ // We cannot pass the asyncResp as it will be destroyed
++ // The response object is not initialized as handleUpgrade wouldn't
++ // be using this object
++ crow::Response resp;
++ rules[ruleIndex]->handleUpgrade(req, resp,
+ std::forward<Adaptor>(adaptor));
+ }
+ catch (const std::exception& e)
+ {
+ BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
+- res.result(boost::beast::http::status::internal_server_error);
+- res.end();
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ catch (...)
+@@ -1301,8 +1306,8 @@ class Router
+ BMCWEB_LOG_ERROR
+ << "An uncaught exception occurred. The type was unknown "
+ "so no information was available.";
+- res.result(boost::beast::http::status::internal_server_error);
+- res.end();
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ }
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch
new file mode 100644
index 000000000..1217147b4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0002-Move-privileges-to-separate-entity.patch
@@ -0,0 +1,109 @@
+From 6483f0af926391e8d1f256ba0f23f3640260cfd1 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 18 Oct 2021 22:52:17 +0530
+Subject: [PATCH] Move privileges to separate entity
+
+The privilege property of a rule is currently part of RuleParameterTraits
+structure. Moving this property (member function) out into a separate
+entity PrivilegeParameterTraits.
+This move is required to enable inheriting this entity into Weksockets
+and SseSockets.
+
+Tested:
+ - bmcweb is functional and is responding to Redfish URI's
+ - User Privilege check for URI's is functional.
+
+Change-Id: I288ab12258c15ae5a626f4409fc3b4a9cc574ea3
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/routing.hpp | 53 +++++++++++++++++++++++++++---------------------
+ 1 file changed, 30 insertions(+), 23 deletions(-)
+
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 858f146..acc99dc 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -102,6 +102,8 @@ class BaseRule
+ friend class Router;
+ template <typename T>
+ friend struct RuleParameterTraits;
++ template <typename T>
++ friend struct PrivilegeParameterTraits;
+ };
+
+ namespace detail
+@@ -316,6 +318,33 @@ struct Wrapped
+ } // namespace routing_handler_call_helper
+ } // namespace detail
+
++template <typename T>
++struct PrivilegeParameterTraits
++{
++ using self_t = T;
++ self_t& privileges(
++ const std::initializer_list<std::initializer_list<const char*>>& p)
++ {
++ self_t* self = static_cast<self_t*>(this);
++ for (const std::initializer_list<const char*>& privilege : p)
++ {
++ self->privilegesSet.emplace_back(privilege);
++ }
++ return *self;
++ }
++
++ template <size_t N, typename... MethodArgs>
++ self_t& privileges(const std::array<redfish::Privileges, N>& p)
++ {
++ self_t* self = static_cast<self_t*>(this);
++ for (const redfish::Privileges& privilege : p)
++ {
++ self->privilegesSet.emplace_back(privilege);
++ }
++ return *self;
++ }
++};
++
+ class WebSocketRule : public BaseRule
+ {
+ using self_t = WebSocketRule;
+@@ -462,7 +491,7 @@ class SseSocketRule : public BaseRule
+ };
+
+ template <typename T>
+-struct RuleParameterTraits
++struct RuleParameterTraits : public PrivilegeParameterTraits<T>
+ {
+ using self_t = T;
+ WebSocketRule& websocket()
+@@ -503,28 +532,6 @@ struct RuleParameterTraits
+ self->methodsBitfield |= 1U << static_cast<size_t>(method);
+ return *self;
+ }
+-
+- self_t& privileges(
+- const std::initializer_list<std::initializer_list<const char*>>& p)
+- {
+- self_t* self = static_cast<self_t*>(this);
+- for (const std::initializer_list<const char*>& privilege : p)
+- {
+- self->privilegesSet.emplace_back(privilege);
+- }
+- return *self;
+- }
+-
+- template <size_t N, typename... MethodArgs>
+- self_t& privileges(const std::array<redfish::Privileges, N>& p)
+- {
+- self_t* self = static_cast<self_t*>(this);
+- for (const redfish::Privileges& privilege : p)
+- {
+- self->privilegesSet.emplace_back(privilege);
+- }
+- return *self;
+- }
+ };
+
+ class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule>
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch
new file mode 100644
index 000000000..4fe19e919
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch
@@ -0,0 +1,220 @@
+From 93f66a039d4e840d7597288b296f68d467039006 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 18 Oct 2021 22:55:38 +0530
+Subject: [PATCH] Add Support for privilege check in handleUpgrade
+
+This commit enables privilege check for user(s) in case of upgraded
+connections.
+Currently users with no privileges will also be able to access
+Websockets connections (Ex: KVM).
+
+Tested:
+ - websocket_test.py Passed
+ - Admin and Operator users were able to access KVM on WebUI
+ - Readonly User was unable to access KVM on WebUI
+
+Change-Id: Id9d33aeca24d8fafb2e9dcc28c46a48930740cd6
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/app.hpp | 2 +-
+ http/routing.hpp | 164 +++++++++++++++++++++++++++++++++++++++--------
+ 2 files changed, 137 insertions(+), 29 deletions(-)
+
+diff --git a/http/app.hpp b/http/app.hpp
+index 7e946dc..d63c6cb 100644
+--- a/http/app.hpp
++++ b/http/app.hpp
+@@ -51,7 +51,7 @@ class App
+ App& operator=(const App&&) = delete;
+
+ template <typename Adaptor>
+- void handleUpgrade(const Request& req,
++ void handleUpgrade(Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Adaptor&& adaptor)
+ {
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 9443657..048734d 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -1217,7 +1217,7 @@ class Router
+ }
+
+ template <typename Adaptor>
+- void handleUpgrade(const Request& req,
++ void handleUpgrade(Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ Adaptor&& adaptor)
+ {
+@@ -1285,33 +1285,141 @@ class Router
+ << "' " << static_cast<uint32_t>(req.method()) << " / "
+ << rules[ruleIndex]->getMethods();
+
+- // any uncaught exceptions become 500s
+- try
+- {
+- // Creating temporary response object to call handleUpgrade
+- // We cannot pass the asyncResp as it will be destroyed
+- // The response object is not initialized as handleUpgrade wouldn't
+- // be using this object
+- crow::Response resp;
+- rules[ruleIndex]->handleUpgrade(req, resp,
+- std::forward<Adaptor>(adaptor));
+- }
+- catch (const std::exception& e)
+- {
+- BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what();
+- asyncResp->res.result(
+- boost::beast::http::status::internal_server_error);
+- return;
+- }
+- catch (...)
+- {
+- BMCWEB_LOG_ERROR
+- << "An uncaught exception occurred. The type was unknown "
+- "so no information was available.";
+- asyncResp->res.result(
+- boost::beast::http::status::internal_server_error);
+- return;
+- }
++ crow::connections::systemBus->async_method_call(
++ [&req, asyncResp, &rules, ruleIndex, &adaptor](
++ const boost::system::error_code ec,
++ std::map<std::string, std::variant<bool, std::string,
++ std::vector<std::string>>>
++ userInfo) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "GetUserInfo failed...";
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
++ return;
++ }
++
++ const std::string* userRolePtr = nullptr;
++ auto userInfoIter = userInfo.find("UserPrivilege");
++ if (userInfoIter != userInfo.end())
++ {
++ userRolePtr =
++ std::get_if<std::string>(&userInfoIter->second);
++ }
++
++ std::string userRole{};
++ if (userRolePtr != nullptr)
++ {
++ userRole = *userRolePtr;
++ BMCWEB_LOG_DEBUG << "userName = " << req.session->username
++ << " userRole = " << *userRolePtr;
++ }
++
++ bool* remoteUserPtr = nullptr;
++ auto remoteUserIter = userInfo.find("RemoteUser");
++ if (remoteUserIter != userInfo.end())
++ {
++ remoteUserPtr = std::get_if<bool>(&remoteUserIter->second);
++ }
++ if (remoteUserPtr == nullptr)
++ {
++ BMCWEB_LOG_ERROR
++ << "RemoteUser property missing or wrong type";
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
++ return;
++ }
++ bool remoteUser = *remoteUserPtr;
++
++ bool passwordExpired = false; // default for remote user
++ if (!remoteUser)
++ {
++ bool* passwordExpiredPtr = nullptr;
++ auto passwordExpiredIter =
++ userInfo.find("UserPasswordExpired");
++ if (passwordExpiredIter != userInfo.end())
++ {
++ passwordExpiredPtr =
++ std::get_if<bool>(&passwordExpiredIter->second);
++ }
++ if (passwordExpiredPtr != nullptr)
++ {
++ passwordExpired = *passwordExpiredPtr;
++ }
++ else
++ {
++ BMCWEB_LOG_ERROR
++ << "UserPasswordExpired property is expected for"
++ " local user but is missing or wrong type";
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
++ return;
++ }
++ }
++
++ // Get the userprivileges from the role
++ redfish::Privileges userPrivileges =
++ redfish::getUserPrivileges(userRole);
++
++ // Set isConfigureSelfOnly based on D-Bus results. This
++ // ignores the results from both pamAuthenticateUser and the
++ // value from any previous use of this session.
++ req.session->isConfigureSelfOnly = passwordExpired;
++
++ // Modifyprivileges if isConfigureSelfOnly.
++ if (req.session->isConfigureSelfOnly)
++ {
++ // Remove allprivileges except ConfigureSelf
++ userPrivileges = userPrivileges.intersection(
++ redfish::Privileges{"ConfigureSelf"});
++ BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf";
++ }
++
++ if (!rules[ruleIndex]->checkPrivileges(userPrivileges))
++ {
++ asyncResp->res.result(
++ boost::beast::http::status::forbidden);
++ if (req.session->isConfigureSelfOnly)
++ {
++ redfish::messages::passwordChangeRequired(
++ asyncResp->res,
++ crow::utility::urlFromPieces(
++ "redfish", "v1", "AccountService", "Accounts",
++ req.session->username));
++ }
++ return;
++ }
++
++ req.userRole = userRole;
++
++ // any uncaught exceptions become 500s
++ try
++ {
++ crow::Response resp;
++ rules[ruleIndex]->handleUpgrade(req, resp,
++ std::move(adaptor));
++ }
++ catch (std::exception& e)
++ {
++ BMCWEB_LOG_ERROR << "An uncaught exception occurred: "
++ << e.what();
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
++ return;
++ }
++ catch (...)
++ {
++ BMCWEB_LOG_ERROR
++ << "An uncaught exception occurred. The type was "
++ "unknown so no information was available.";
++ asyncResp->res.result(
++ boost::beast::http::status::internal_server_error);
++ return;
++ }
++ },
++ "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
++ "xyz.openbmc_project.User.Manager", "GetUserInfo",
++ req.session->username);
+ }
+
+ void handle(Request& req,
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch
new file mode 100644
index 000000000..19c671754
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0004-Add-Privileges-to-Websockets.patch
@@ -0,0 +1,140 @@
+From 9b27d3e7c1670d53cfb1c0a88cc75155ebfba71a Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 18 Oct 2021 22:58:29 +0530
+Subject: [PATCH] Add Privileges to Websockets
+
+This commit adds Privileges to Websockets.
+In the current implementation, once a rule is upgraded (i.e. from
+BaseRule to WebSocket), there is no provosion to add priviliges.
+In this commit, WebSocket inherits PrivilegeParameterTraits to enable
+privileges.
+
+Also, in the earlier implementation, .privilege() was called after
+BMCWEB_ROUTE(). This results in adding those privileges to the Base rule
+that is created. By moving the privileges() below websocket(), the
+privileges are applied to the websocket.
+
+Tested:
+ - websocket_test.py Passed
+ - Admin and Operator users were able to access KVM on WebUI
+ - Readonly User was unable to access KVM on WebUI
+
+Change-Id: Iff2051dbb7d363c902fd463fa446f280adc6d648
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/routing.hpp | 4 +++-
+ include/dbus_monitor.hpp | 3 ++-
+ include/kvm_websocket.hpp | 4 +++-
+ include/obmc_console.hpp | 4 +++-
+ include/vm_websocket.hpp | 4 +++-
+ 5 files changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/http/routing.hpp b/http/routing.hpp
+index e2a8fbb..6ea3185 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -345,7 +345,9 @@ struct PrivilegeParameterTraits
+ }
+ };
+
+-class WebSocketRule : public BaseRule
++class WebSocketRule :
++ public BaseRule,
++ public PrivilegeParameterTraits<WebSocketRule>
+ {
+ using self_t = WebSocketRule;
+
+diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp
+index a6c86c6..163f884 100644
+--- a/include/dbus_monitor.hpp
++++ b/include/dbus_monitor.hpp
+@@ -5,6 +5,7 @@
+ #include <boost/container/flat_set.hpp>
+ #include <dbus_singleton.hpp>
+ #include <openbmc_dbus_rest.hpp>
++#include <registries/privilege_registry.hpp>
+ #include <sdbusplus/bus/match.hpp>
+ #include <sdbusplus/message/types.hpp>
+ #include <websocket.hpp>
+@@ -105,8 +106,8 @@ inline int onPropertyUpdate(sd_bus_message* m, void* userdata,
+ inline void requestRoutes(App& app)
+ {
+ BMCWEB_ROUTE(app, "/subscribe")
+- .privileges({{"Login"}})
+ .websocket()
++ .privileges(redfish::privileges::privilegeSetLogin)
+ .onopen([&](crow::websocket::Connection& conn,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
+diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp
+index a9dc8ea..3f124a2 100644
+--- a/include/kvm_websocket.hpp
++++ b/include/kvm_websocket.hpp
+@@ -4,6 +4,7 @@
+ #include <app.hpp>
+ #include <async_resp.hpp>
+ #include <boost/container/flat_map.hpp>
++#include <registries/privilege_registry.hpp>
+ #include <websocket.hpp>
+
+ namespace crow
+@@ -159,8 +160,9 @@ inline void requestRoutes(App& app)
+ sessions.reserve(maxSessions);
+
+ BMCWEB_ROUTE(app, "/kvm/0")
+- .privileges({{"ConfigureComponents", "ConfigureManager"}})
+ .websocket()
++ .privileges(redfish::privileges::
++ privilegeSetConfigureManagerOrConfigureComponents)
+ .onopen([](crow::websocket::Connection& conn,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
+diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp
+index ff0a51f..22a49a8 100644
+--- a/include/obmc_console.hpp
++++ b/include/obmc_console.hpp
+@@ -6,6 +6,7 @@
+ #include <boost/asio/local/stream_protocol.hpp>
+ #include <boost/container/flat_map.hpp>
+ #include <boost/container/flat_set.hpp>
++#include <registries/privilege_registry.hpp>
+ #include <websocket.hpp>
+
+ namespace crow
+@@ -136,8 +137,9 @@ inline void connectHandler(const boost::system::error_code& ec)
+ inline void requestRoutes(App& app)
+ {
+ BMCWEB_ROUTE(app, "/console0")
+- .privileges({{"ConfigureComponents", "ConfigureManager"}})
+ .websocket()
++ .privileges(redfish::privileges::
++ privilegeSetConfigureManagerOrConfigureComponents)
+ .onopen([](crow::websocket::Connection& conn,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
+diff --git a/include/vm_websocket.hpp b/include/vm_websocket.hpp
+index 02f958a..ebbe68f 100644
+--- a/include/vm_websocket.hpp
++++ b/include/vm_websocket.hpp
+@@ -3,6 +3,7 @@
+ #include <boost/process/async_pipe.hpp>
+ #include <boost/process/child.hpp>
+ #include <boost/process/io.hpp>
++#include <registries/privilege_registry.hpp>
+ #include <websocket.hpp>
+
+ #include <csignal>
+@@ -156,8 +157,9 @@ static std::shared_ptr<Handler> handler;
+ inline void requestRoutes(App& app)
+ {
+ BMCWEB_ROUTE(app, "/vm/0/0")
+- .privileges({{"ConfigureComponents", "ConfigureManager"}})
+ .websocket()
++ .privileges(redfish::privileges::
++ privilegeSetConfigureManagerOrConfigureComponents)
+ .onopen([](crow::websocket::Connection& conn,
+ const std::shared_ptr<bmcweb::AsyncResp>&) {
+ BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch
new file mode 100644
index 000000000..06ffb3a46
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/http_routing/0005-Add-Privileges-to-SseSockets.patch
@@ -0,0 +1,63 @@
+From 0ceb343809ff498cbfa389c54a158d255a2cca88 Mon Sep 17 00:00:00 2001
+From: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+Date: Mon, 18 Oct 2021 23:02:00 +0530
+Subject: [PATCH] Add Privileges to SseSockets
+
+This commit adds Privileges to Ssesockets.
+In the current implementation, once a rule is upgraded (i.e. from
+BaseRule to SseSocket), there is no provision to add priviliges.
+In this commit, SseSocket inherits PrivilegeParameterTraits to
+enable privileges.
+
+Also, in the earlier implementation, .privilege() was called after
+BMCWEB_ROUTE(). This results in adding those privileges to the Base
+rule that is created. By moving the privileges() below websocket(),
+the privileges are applied to the Ssesocket.
+
+Tested:
+ - SSE Subscription was successful with Admin and Operator Users
+ - SSE Subscription was rejected while using Readonly User
+ - websocket_test.py Passed
+ - Admin and Operator users were able to access KVM on WebUI
+ - Readonly User was unable to access KVM on WebUI
+
+Change-Id: I41739401893b1c2bf718e11ec7676d69f954c98f
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ http/routing.hpp | 4 +++-
+ include/eventservice_sse.hpp | 3 ++-
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/http/routing.hpp b/http/routing.hpp
+index 6ea3185..13174b2 100644
+--- a/http/routing.hpp
++++ b/http/routing.hpp
+@@ -430,7 +430,9 @@ class WebSocketRule :
+ std::function<void(crow::websocket::Connection&)> errorHandler;
+ };
+
+-class SseSocketRule : public BaseRule
++class SseSocketRule :
++ public BaseRule,
++ public PrivilegeParameterTraits<SseSocketRule>
+ {
+ using self_t = SseSocketRule;
+
+diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp
+index 2f22f98..f880344 100644
+--- a/include/eventservice_sse.hpp
++++ b/include/eventservice_sse.hpp
+@@ -192,8 +192,9 @@ static void deleteSubscription(std::shared_ptr<crow::SseConnection>& conn)
+ inline void requestRoutes(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE")
+- .privileges({{"ConfigureComponents", "ConfigureManager"}})
+ .serverSentEvent()
++ .privileges(redfish::privileges::
++ privilegeSetConfigureManagerOrConfigureComponents)
+ .onopen([](std::shared_ptr<crow::SseConnection>& conn,
+ const crow::Request& req, crow::Response& res) {
+ BMCWEB_LOG_DEBUG << "Connection " << conn << " opened.";
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch
new file mode 100644
index 000000000..6b57dc912
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch
@@ -0,0 +1,26 @@
+From 71b55e9773c387d6510650e7cf64f050a853ac77 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Tue, 30 Nov 2021 16:29:12 +0100
+Subject: [PATCH] Revert "Remove LogService from TelemetryService"
+
+This reverts commit 2b3da45876aac57a36d3093379a992d699e7e396.
+---
+ redfish-core/lib/telemetry_service.hpp | 2 +
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index b79a5cd..e3e4a25 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -25,6 +25,8 @@ inline void handleTelemetryServiceGet(
+ "/redfish/v1/TelemetryService/MetricReports";
+ asyncResp->res.jsonValue["Triggers"]["@odata.id"] =
+ "/redfish/v1/TelemetryService/Triggers";
++ asyncResp->res.jsonValue["LogService"]["@odata.id"] =
++ "/redfish/v1/Managers/bmc/LogServices/Journal";
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-ref-use-url_view-for-telemetry-uris.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-ref-use-url_view-for-telemetry-uris.patch
new file mode 100644
index 000000000..242bec0dc
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-ref-use-url_view-for-telemetry-uris.patch
@@ -0,0 +1,193 @@
+From a6d32959d5420f3fdca0d391feb54ac89f65dca3 Mon Sep 17 00:00:00 2001
+From: Szymon Dompke <szymon.dompke@intel.com>
+Date: Tue, 1 Mar 2022 16:34:08 +0100
+Subject: [PATCH] ref: use url_view for telemetry uris
+
+This change refactor telemetry code to use bmcweb utility function for
+uri construction, which is safe and preferred way, instead of string
+operations.
+
+Testing done:
+- Some basic GET operations done on Telemetry, no regression.
+
+Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
+Change-Id: I6de5d79a078944d398357f27dc0c201c130c4302
+---
+ redfish-core/include/event_service_manager.hpp | 6 ++++--
+ redfish-core/include/utils/telemetry_utils.hpp | 5 +----
+ redfish-core/lib/metric_report.hpp | 14 +++++++++++---
+ redfish-core/lib/metric_report_definition.hpp | 13 ++++++++++---
+ redfish-core/lib/trigger.hpp | 15 +++++++++------
+ 5 files changed, 35 insertions(+), 18 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index e879f9e..0ed404f 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -505,14 +505,16 @@ class Subscription : public persistent_data::UserSubscription
+ void filterAndSendReports(const std::string& reportId,
+ const telemetry::TimestampReadings& var)
+ {
+- std::string mrdUri = telemetry::metricReportDefinitionUri + ("/" + id);
++ boost::urls::url mrdUri =
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", reportId);
+
+ // Empty list means no filter. Send everything.
+ if (!metricReportDefinitions.empty())
+ {
+ if (std::find(metricReportDefinitions.begin(),
+ metricReportDefinitions.end(),
+- mrdUri) == metricReportDefinitions.end())
++ mrdUri.string()) == metricReportDefinitions.end())
+ {
+ return;
+ }
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 8aeff0d..6930d0a 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -1,6 +1,7 @@
+ #pragma once
+
+ #include "dbus_utility.hpp"
++#include "utility.hpp"
+
+ namespace redfish
+ {
+@@ -9,10 +10,6 @@ namespace telemetry
+ {
+ constexpr const char* service = "xyz.openbmc_project.Telemetry";
+ constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report";
+-constexpr const char* metricReportDefinitionUri =
+- "/redfish/v1/TelemetryService/MetricReportDefinitions";
+-constexpr const char* metricReportUri =
+- "/redfish/v1/TelemetryService/MetricReports";
+
+ inline std::string getDbusReportPath(const std::string& id)
+ {
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index 89bd8db..2fb8d82 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -14,6 +14,9 @@ namespace redfish
+ namespace telemetry
+ {
+
++constexpr const char* metricReportUri =
++ "/redfish/v1/TelemetryService/MetricReports";
++
+ using Readings =
+ std::vector<std::tuple<std::string, std::string, double, uint64_t>>;
+ using TimestampReadings = std::tuple<uint64_t, Readings>;
+@@ -39,11 +42,16 @@ inline bool fillReport(nlohmann::json& json, const std::string& id,
+ const TimestampReadings& timestampReadings)
+ {
+ json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport";
+- json["@odata.id"] = telemetry::metricReportUri + std::string("/") + id;
++ json["@odata.id"] =
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReports", id)
++ .string();
+ json["Id"] = id;
+ json["Name"] = id;
+ json["MetricReportDefinition"]["@odata.id"] =
+- telemetry::metricReportDefinitionUri + std::string("/") + id;
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", id)
++ .string();
+
+ const auto& [timestamp, readings] = timestampReadings;
+ json["Timestamp"] = crow::utility::getDateTimeUintMs(timestamp);
+@@ -62,7 +70,7 @@ inline void requestRoutesMetricReportCollection(App& app)
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#MetricReportCollection.MetricReportCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+- "/redfish/v1/TelemetryService/MetricReports";
++ telemetry::metricReportUri;
+ asyncResp->res.jsonValue["Name"] = "Metric Report Collection";
+ const std::vector<const char*> interfaces{
+ telemetry::reportInterface};
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index ab7c88b..1a520f3 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -18,6 +18,9 @@ namespace redfish
+ namespace telemetry
+ {
+
++constexpr const char* metricReportDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions";
++
+ using ReadingParameters =
+ std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+ std::string, std::string>>;
+@@ -30,11 +33,15 @@ inline void fillReportDefinition(
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
+ asyncResp->res.jsonValue["@odata.id"] =
+- telemetry::metricReportDefinitionUri + std::string("/") + id;
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", id)
++ .string();
+ asyncResp->res.jsonValue["Id"] = id;
+ asyncResp->res.jsonValue["Name"] = id;
+ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
+- telemetry::metricReportUri + std::string("/") + id;
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReports", id)
++ .string();
+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite";
+
+@@ -365,7 +372,7 @@ inline void requestRoutesMetricReportDefinitionCollection(App& app)
+ "#MetricReportDefinitionCollection."
+ "MetricReportDefinitionCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+- "/redfish/v1/TelemetryService/MetricReportDefinitions";
++ telemetry::metricReportDefinitionUri;
+ asyncResp->res.jsonValue["Name"] =
+ "Metric Definition Collection";
+ const std::vector<const char*> interfaces{
+diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp
+index cdd5781..da6a5db 100644
+--- a/redfish-core/lib/trigger.hpp
++++ b/redfish-core/lib/trigger.hpp
+@@ -143,9 +143,11 @@ inline nlohmann::json
+ nlohmann::json reports = nlohmann::json::array();
+ for (const std::string& name : reportNames)
+ {
+- reports.push_back({
+- {"@odata.id", metricReportDefinitionUri + std::string("/") + name},
+- });
++ reports.push_back(
++ {{"@odata.id",
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", name)
++ .string()}});
+ }
+
+ return reports;
+@@ -214,7 +216,9 @@ inline bool fillTrigger(
+ }
+
+ json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
+- json["@odata.id"] = triggerUri + std::string("/") + id;
++ json["@odata.id"] = crow::utility::urlFromPieces(
++ "redfish", "v1", "TelemetryService", "Triggers", id)
++ .string();
+ json["Id"] = id;
+ json["Name"] = *name;
+
+@@ -282,8 +286,7 @@ inline void requestRoutesTriggerCollection(App& app)
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#TriggersCollection.TriggersCollection";
+- asyncResp->res.jsonValue["@odata.id"] =
+- "/redfish/v1/TelemetryService/Triggers";
++ asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
+ asyncResp->res.jsonValue["Name"] = "Triggers Collection";
+ const std::vector<const char*> interfaces{
+ telemetry::triggerInterface};
+--
+2.25.1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Fix-Trigger-GET-functionality.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Fix-Trigger-GET-functionality.patch
new file mode 100644
index 000000000..f741142c4
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Fix-Trigger-GET-functionality.patch
@@ -0,0 +1,127 @@
+From cc10d221a17e1136bf3ec7f62583a4ca44212adc Mon Sep 17 00:00:00 2001
+From: Szymon Dompke <szymon.dompke@intel.com>
+Date: Tue, 22 Feb 2022 13:58:00 +0100
+Subject: [PATCH] Fix Trigger GET functionality
+
+This change is fixing 2 issues related to GET on Trigger schema:
+- Links to MetricReportDefintions were not parsed properly. Dbus is
+ returning collection of strings prefixed with "TelemetryService/".
+ This prefix was supposed to be removed.
+- In case of error occurring during internal data parsing, GET could
+ return both "Internal Error" message and incomplete Trigger
+ representation. By swapping few lines of code, this issue is fixed:
+ now either error is returned or full Trigger representation, but never
+ both of them.
+
+Testing done:
+- Links are now returning proper uris of existing MRD.
+- Internal Error is returned for malformed data returned by service.
+- Other Trigger GET functionality remained unchanged.
+
+Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
+Change-Id: I81ebbf3889a152199bef7230de56a73bb112731b
+---
+ redfish-core/lib/trigger.hpp | 65 ++++++++++++++++++++++--------------
+ 1 file changed, 40 insertions(+), 25 deletions(-)
+
+diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp
+index da6a5db..5e0897e 100644
+--- a/redfish-core/lib/trigger.hpp
++++ b/redfish-core/lib/trigger.hpp
+@@ -137,20 +137,29 @@ inline std::optional<nlohmann::json>
+ return std::make_optional(thresholds);
+ }
+
+-inline nlohmann::json
++inline std::optional<nlohmann::json>
+ getMetricReportDefinitions(const std::vector<std::string>& reportNames)
+ {
+ nlohmann::json reports = nlohmann::json::array();
++
+ for (const std::string& name : reportNames)
+ {
+- reports.push_back(
+- {{"@odata.id",
+- crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
+- "MetricReportDefinitions", name)
+- .string()}});
++ sdbusplus::message::object_path path(name);
++ if (path.parent_path() != "TelemetryService")
++ {
++ BMCWEB_LOG_ERROR << "Property ReportNames contains invalid value: "
++ << name;
++ return std::nullopt;
++ }
++ reports.push_back({
++ {"@odata.id", crow::utility::urlFromPieces(
++ "redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", path.filename())
++ .string()},
++ });
+ }
+
+- return reports;
++ return std::make_optional(reports);
+ }
+
+ inline std::vector<std::string>
+@@ -215,12 +224,23 @@ inline bool fillTrigger(
+ return false;
+ }
+
+- json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
+- json["@odata.id"] = crow::utility::urlFromPieces(
+- "redfish", "v1", "TelemetryService", "Triggers", id)
+- .string();
+- json["Id"] = id;
+- json["Name"] = *name;
++ std::optional<std::vector<std::string>> triggerActions =
++ getTriggerActions(*actions);
++ if (!triggerActions)
++ {
++ BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
++ << id;
++ return false;
++ }
++
++ std::optional<nlohmann::json> linkedReports =
++ getMetricReportDefinitions(*reports);
++ if (!linkedReports)
++ {
++ BMCWEB_LOG_ERROR << "Property ReportNames is invalid in Trigger: "
++ << id;
++ return false;
++ }
+
+ if (*discrete)
+ {
+@@ -257,20 +277,15 @@ inline bool fillTrigger(
+ json["MetricType"] = "Numeric";
+ }
+
+- std::optional<std::vector<std::string>> triggerActions =
+- getTriggerActions(*actions);
+-
+- if (!triggerActions)
+- {
+- BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
+- << id;
+- return false;
+- }
+-
++ json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
++ json["@odata.id"] = crow::utility::urlFromPieces(
++ "redfish", "v1", "TelemetryService", "Triggers", id)
++ .string();
++ json["Id"] = id;
++ json["Name"] = *name;
+ json["TriggerActions"] = *triggerActions;
+ json["MetricProperties"] = getMetricProperties(*sensors);
+- json["Links"]["MetricReportDefinitions"] =
+- getMetricReportDefinitions(*reports);
++ json["Links"]["MetricReportDefinitions"] = *linkedReports;
+
+ return true;
+ }
+--
+2.25.1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-POST-on-TriggersCollection.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-POST-on-TriggersCollection.patch
new file mode 100644
index 000000000..735033c70
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-POST-on-TriggersCollection.patch
@@ -0,0 +1,848 @@
+From c3da912209b3c6d8d3a3724652a09b77e85e8897 Mon Sep 17 00:00:00 2001
+From: Szymon Dompke <szymon.dompke@intel.com>
+Date: Fri, 4 Mar 2022 13:11:38 +0100
+Subject: [PATCH] Add support for POST on TriggersCollection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Added POST method on /redfish/v1/TelemetryService/Triggers uri, which
+creates new trigger in telemetry service, by using dbus call AddTrigger.
+
+By DMTF, most of the properties are not required, and as such are
+treated as optional. Some values can be deduced from others (like
+'MetricType', depending on 'DiscreteTriggers' or 'NumericThresholds').
+All properties provided in POST body by user will be verified against
+each other, and errors will be raised. Few examples of such situations:
+- 'MetricType' is set to 'Discrete' but 'NumericThresholds' was passed.
+- 'MetricType' is set to 'Numeric' but "DiscreteTriggers' or
+ 'DiscreteTriggerCondition' were passed
+- 'DiscreteTriggerCondition' is set to 'Specified' but
+ 'DiscreteTriggers' is an empty array or was not passed.
+- 'DiscreteTriggerCondition' is set to 'Changed' but 'DiscreteTriggers'
+ is passed and is not an empty array.
+
+Example 1 – Trigger with discrete values:
+{
+ "Id": "TestTrigger",
+ "MetricType": "Discrete",
+ "TriggerActions": [
+ "RedfishEvent"
+ ],
+ "DiscreteTriggerCondition": "Specified",
+ "DiscreteTriggers": [
+ {
+ "Value": "55.88",
+ "DwellTime": "PT0.001S",
+ "Severity": "Warning"
+ },
+ {
+ "Name": "My discrete trigger",
+ "Value": "55.88",
+ "DwellTime": "PT0.001S",
+ "Severity": "OK"
+ },
+ {
+ "Value": "55.88",
+ "DwellTime": "PT0.001S",
+ "Severity": "Critical"
+ }
+ ],
+ "MetricProperties": [
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading"
+ ],
+ "Links": {
+ "MetricReportDefinitions": []
+ }
+}
+
+Example 2 – trigger with numeric threshold:
+{
+ "Id": "TestTrigger2",
+ "Name": "My Numeric Trigger",
+ "MetricType": "Numeric",
+ "TriggerActions": [
+ "RedfishEvent",
+ "RedfishMetricReport"
+ ],
+ "NumericThresholds": {
+ "UpperCritical": {
+ "Reading": 50,
+ "Activation": "Increasing",
+ "DwellTime": "PT0.001S"
+ },
+ "UpperWarning": {
+ "Reading": 48.1,
+ "Activation": "Increasing",
+ "DwellTime": "PT0.004S"
+ }
+ },
+ "MetricProperties": [
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/17/Reading"
+ ],
+ "Links": {
+ "MetricReportDefinitions": [
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/PowerMetrics",
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/PowerMetricStats",
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/PlatformPowerUsage"
+ ]
+ }
+}
+
+Tested:
+- Triggers were successfully created with above example message bodies.
+ This can be checked by calling:
+ 'busctl tree xyz.openbmc_project.Telemetry'.
+- Expected errors were returned for messages with incorrect or mutually
+ exclusive properties and incorrect values.
+- Redfish service validator is passing.
+
+Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
+Change-Id: Ief8c76de8aa660ae0d2dbe4610c26a28186a290a
+---
+ redfish-core/include/utils/finalizer.hpp | 38 ++
+ .../include/utils/telemetry_utils.hpp | 77 +++
+ redfish-core/lib/trigger.hpp | 544 +++++++++++++++++-
+ 3 files changed, 653 insertions(+), 6 deletions(-)
+ create mode 100644 redfish-core/include/utils/finalizer.hpp
+
+diff --git a/redfish-core/include/utils/finalizer.hpp b/redfish-core/include/utils/finalizer.hpp
+new file mode 100644
+index 0000000..f14a34a
+--- /dev/null
++++ b/redfish-core/include/utils/finalizer.hpp
+@@ -0,0 +1,38 @@
++#pragma once
++
++#include <functional>
++
++namespace redfish
++{
++
++namespace utils
++{
++
++class Finalizer
++{
++ public:
++ Finalizer() = delete;
++ Finalizer(std::function<void()> finalizer) : finalizer(std::move(finalizer))
++ {}
++
++ Finalizer(const Finalizer&) = delete;
++ Finalizer(Finalizer&&) = delete;
++
++ Finalizer& operator=(const Finalizer&) = delete;
++ Finalizer& operator=(Finalizer&&) = delete;
++
++ ~Finalizer()
++ {
++ if (finalizer)
++ {
++ finalizer();
++ }
++ }
++
++ private:
++ std::function<void()> finalizer;
++};
++
++} // namespace utils
++
++} // namespace redfish
+\ No newline at end of file
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 6930d0a..908ccfc 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -25,5 +25,82 @@ inline std::string getDbusTriggerPath(const std::string& id)
+ return {triggersPath / id};
+ }
+
++inline std::optional<std::string>
++ getReportNameFromReportDefinitionUri(const std::string& uri)
++{
++ constexpr std::string_view uriPattern =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
++ if (uri.starts_with(uriPattern))
++ {
++ return uri.substr(uriPattern.length());
++ }
++ return std::nullopt;
++}
++
++inline std::optional<std::string>
++ getTriggerIdFromDbusPath(const std::string& dbusPath)
++{
++ constexpr std::string_view triggerTree =
++ "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService/";
++ if (dbusPath.starts_with(triggerTree))
++ {
++ return dbusPath.substr(triggerTree.length());
++ }
++ return std::nullopt;
++}
++
++inline bool getChassisSensorNode(
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::vector<std::string>& uris,
++ boost::container::flat_set<std::pair<std::string, std::string>>& matched)
++{
++ size_t uriIdx = 0;
++ for (const std::string& uri : uris)
++ {
++ std::string chassis;
++ std::string node;
++
++ if (!uri.starts_with("/redfish/v1/Chassis/") ||
++ !dbus::utility::getNthStringFromPath(uri, 3, chassis) ||
++ !dbus::utility::getNthStringFromPath(uri, 4, node))
++ {
++ BMCWEB_LOG_ERROR << "Failed to get chassis and sensor Node "
++ "from "
++ << uri;
++ messages::propertyValueIncorrect(asyncResp->res, uri,
++ "MetricProperties/" +
++ std::to_string(uriIdx));
++ return false;
++ }
++
++ if (node.ends_with('#'))
++ {
++ node.pop_back();
++ }
++
++ matched.emplace(std::move(chassis), std::move(node));
++ uriIdx++;
++ }
++ return true;
++}
++
++inline std::optional<std::string>
++ redfishActionToDbusAction(const std::string& redfishAction)
++{
++ if (redfishAction == "RedfishMetricReport")
++ {
++ return "UpdateReport";
++ }
++ if (redfishAction == "RedfishEvent")
++ {
++ return "RedfishEvent";
++ }
++ if (redfishAction == "LogToLogService")
++ {
++ return "LogToLogService";
++ }
++ return std::nullopt;
++}
++
+ } // namespace telemetry
+ } // namespace redfish
+diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp
+index 5e0897e..67aa5e6 100644
+--- a/redfish-core/lib/trigger.hpp
++++ b/redfish-core/lib/trigger.hpp
+@@ -1,7 +1,9 @@
+ #pragma once
+
+-#include "utils/collection.hpp"
++#include "sensors.hpp"
++#include "utils/finalizer.hpp"
+ #include "utils/telemetry_utils.hpp"
++#include "utils/time_utils.hpp"
+
+ #include <app.hpp>
+ #include <registries/privilege_registry.hpp>
+@@ -14,9 +16,13 @@ namespace redfish
+ {
+ namespace telemetry
+ {
++
+ constexpr const char* triggerInterface =
+ "xyz.openbmc_project.Telemetry.Trigger";
+-constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers";
++
++static constexpr std::array<std::string_view, 4>
++ supportedNumericThresholdNames = {"UpperCritical", "LowerCritical",
++ "UpperWarning", "LowerWarning"};
+
+ using NumericThresholdParams =
+ std::tuple<std::string, uint64_t, std::string, double>;
+@@ -24,6 +30,10 @@ using NumericThresholdParams =
+ using DiscreteThresholdParams =
+ std::tuple<std::string, std::string, uint64_t, std::string>;
+
++using TriggerThresholdParams =
++ std::variant<std::vector<NumericThresholdParams>,
++ std::vector<DiscreteThresholdParams>>;
++
+ using TriggerThresholdParamsExt =
+ std::variant<std::monostate, std::vector<NumericThresholdParams>,
+ std::vector<DiscreteThresholdParams>>;
+@@ -35,6 +45,463 @@ using TriggerGetParamsVariant =
+ std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
+ TriggerSensorsParams, std::vector<std::string>>;
+
++namespace add_trigger
++{
++
++enum class MetricType
++{
++ Discrete,
++ Numeric
++};
++
++enum class DiscreteCondition
++{
++ Specified,
++ Changed
++};
++
++struct Context
++{
++ struct
++ {
++ std::string id;
++ std::string name;
++ std::vector<std::string> actions;
++ std::vector<std::pair<sdbusplus::message::object_path, std::string>>
++ sensors;
++ std::vector<std::string> reportNames;
++ TriggerThresholdParams thresholds;
++ } dbusArgs;
++
++ struct
++ {
++ std::optional<DiscreteCondition> discreteCondition;
++ std::optional<MetricType> metricType;
++ std::optional<std::vector<std::string>> metricProperties;
++ } parsedInfo;
++
++ boost::container::flat_map<std::string, std::string> uriToDbusMerged{};
++};
++
++inline std::optional<MetricType> getMetricType(const std::string& metricType)
++{
++ if (metricType == "Discrete")
++ {
++ return MetricType::Discrete;
++ }
++ if (metricType == "Numeric")
++ {
++ return MetricType::Numeric;
++ }
++ return std::nullopt;
++}
++
++inline std::optional<DiscreteCondition>
++ getDiscreteCondition(const std::string& discreteTriggerCondition)
++{
++ if (discreteTriggerCondition == "Specified")
++ {
++ return DiscreteCondition::Specified;
++ }
++ if (discreteTriggerCondition == "Changed")
++ {
++ return DiscreteCondition::Changed;
++ }
++ return std::nullopt;
++}
++
++inline bool parseNumericThresholds(crow::Response& res,
++ nlohmann::json& numericThresholds,
++ Context& ctx)
++{
++ if (!numericThresholds.is_object())
++ {
++ messages::propertyValueTypeError(res, numericThresholds.dump(),
++ "NumericThresholds");
++ return false;
++ }
++
++ std::vector<NumericThresholdParams> parsedParams;
++ parsedParams.reserve(numericThresholds.size());
++
++ for (auto& [thresholdName, thresholdData] : numericThresholds.items())
++ {
++ if (std::find(supportedNumericThresholdNames.begin(),
++ supportedNumericThresholdNames.end(),
++ thresholdName) == supportedNumericThresholdNames.end())
++ {
++ messages::propertyUnknown(res, thresholdName);
++ return false;
++ }
++
++ double reading = .0;
++ std::string activation;
++ std::string dwellTimeStr;
++
++ if (!json_util::readJson(thresholdData, res, "Reading", reading,
++ "Activation", activation, "DwellTime",
++ dwellTimeStr))
++ {
++ return false;
++ }
++
++ std::optional<std::chrono::milliseconds> dwellTime =
++ time_utils::fromDurationString(dwellTimeStr);
++ if (!dwellTime)
++ {
++ messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
++ return false;
++ }
++
++ parsedParams.emplace_back(thresholdName,
++ static_cast<uint64_t>(dwellTime->count()),
++ activation, reading);
++ }
++
++ ctx.dbusArgs.thresholds = std::move(parsedParams);
++ return true;
++}
++
++inline bool parseDiscreteTriggers(
++ crow::Response& res,
++ std::optional<std::vector<nlohmann::json>>& discreteTriggers, Context& ctx)
++{
++ std::vector<DiscreteThresholdParams> parsedParams;
++ if (!discreteTriggers)
++ {
++ ctx.dbusArgs.thresholds = std::move(parsedParams);
++ return true;
++ }
++
++ parsedParams.reserve(discreteTriggers->size());
++ for (nlohmann::json& thresholdInfo : *discreteTriggers)
++ {
++ std::optional<std::string> name;
++ std::string value;
++ std::string dwellTimeStr;
++ std::string severity;
++
++ if (!json_util::readJson(thresholdInfo, res, "Name", name, "Value",
++ value, "DwellTime", dwellTimeStr, "Severity",
++ severity))
++ {
++ return false;
++ }
++
++ std::optional<std::chrono::milliseconds> dwellTime =
++ time_utils::fromDurationString(dwellTimeStr);
++ if (!dwellTime)
++ {
++ messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
++ return false;
++ }
++
++ if (!name)
++ {
++ name = "";
++ }
++
++ parsedParams.emplace_back(
++ *name, severity, static_cast<uint64_t>(dwellTime->count()), value);
++ }
++
++ ctx.dbusArgs.thresholds = std::move(parsedParams);
++ return true;
++}
++
++inline bool parseTriggerThresholds(
++ crow::Response& res,
++ std::optional<std::vector<nlohmann::json>>& discreteTriggers,
++ std::optional<nlohmann::json>& numericThresholds, Context& ctx)
++{
++ if (discreteTriggers && numericThresholds)
++ {
++ messages::propertyValueConflict(res, "DiscreteTriggers",
++ "NumericThresholds");
++ messages::propertyValueConflict(res, "NumericThresholds",
++ "DiscreteTriggers");
++ return false;
++ }
++
++ if (ctx.parsedInfo.discreteCondition)
++ {
++ if (numericThresholds)
++ {
++ messages::propertyValueConflict(res, "DiscreteTriggerCondition",
++ "NumericThresholds");
++ messages::propertyValueConflict(res, "NumericThresholds",
++ "DiscreteTriggerCondition");
++ return false;
++ }
++ }
++
++ if (ctx.parsedInfo.metricType)
++ {
++ if (*ctx.parsedInfo.metricType == MetricType::Discrete &&
++ numericThresholds)
++ {
++ messages::propertyValueConflict(res, "NumericThresholds",
++ "MetricType");
++ return false;
++ }
++ if (*ctx.parsedInfo.metricType == MetricType::Numeric &&
++ discreteTriggers)
++ {
++ messages::propertyValueConflict(res, "DiscreteTriggers",
++ "MetricType");
++ return false;
++ }
++ if (*ctx.parsedInfo.metricType == MetricType::Numeric &&
++ ctx.parsedInfo.discreteCondition)
++ {
++ messages::propertyValueConflict(res, "DiscreteTriggers",
++ "DiscreteTriggerCondition");
++ return false;
++ }
++ }
++
++ if (discreteTriggers || ctx.parsedInfo.discreteCondition ||
++ (ctx.parsedInfo.metricType &&
++ *ctx.parsedInfo.metricType == MetricType::Discrete))
++ {
++ if (ctx.parsedInfo.discreteCondition)
++ {
++ if (*ctx.parsedInfo.discreteCondition ==
++ DiscreteCondition::Specified &&
++ !discreteTriggers)
++ {
++ messages::createFailedMissingReqProperties(res,
++ "DiscreteTriggers");
++ return false;
++ }
++ if (discreteTriggers && ((*ctx.parsedInfo.discreteCondition ==
++ DiscreteCondition::Specified &&
++ discreteTriggers->empty()) ||
++ (*ctx.parsedInfo.discreteCondition ==
++ DiscreteCondition::Changed &&
++ !discreteTriggers->empty())))
++ {
++ messages::propertyValueConflict(res, "DiscreteTriggers",
++ "DiscreteTriggerCondition");
++ return false;
++ }
++ }
++ if (!parseDiscreteTriggers(res, discreteTriggers, ctx))
++ {
++ return false;
++ }
++ }
++ else if (numericThresholds)
++ {
++ if (!parseNumericThresholds(res, *numericThresholds, ctx))
++ {
++ return false;
++ }
++ }
++ else
++ {
++ messages::createFailedMissingReqProperties(
++ res, "'DiscreteTriggers', 'NumericThresholds', "
++ "'DiscreteTriggerCondition' or 'MetricType'");
++ return false;
++ }
++ return true;
++}
++
++inline bool parseLinks(crow::Response& res, nlohmann::json& links, Context& ctx)
++{
++ if (links.empty())
++ {
++ return true;
++ }
++
++ std::optional<std::vector<std::string>> metricReportDefinitions;
++ if (!json_util::readJson(links, res, "MetricReportDefinitions",
++ metricReportDefinitions))
++ {
++ return false;
++ }
++
++ if (metricReportDefinitions)
++ {
++ ctx.dbusArgs.reportNames.reserve(metricReportDefinitions->size());
++ for (std::string& reportDefinionUri : *metricReportDefinitions)
++ {
++ std::optional<std::string> reportName =
++ getReportNameFromReportDefinitionUri(reportDefinionUri);
++ if (!reportName)
++ {
++ messages::propertyValueIncorrect(res, "MetricReportDefinitions",
++ reportDefinionUri);
++ return false;
++ }
++ ctx.dbusArgs.reportNames.emplace_back("TelemetryService/" +
++ *reportName);
++ }
++ }
++ return true;
++}
++
++inline bool parseMetricProperties(crow::Response& res, Context& ctx)
++{
++ if (!ctx.parsedInfo.metricProperties)
++ {
++ return true;
++ }
++
++ ctx.dbusArgs.sensors.reserve(ctx.parsedInfo.metricProperties->size());
++
++ size_t uriIdx = 0;
++ for (const std::string& uri : *ctx.parsedInfo.metricProperties)
++ {
++ auto el = ctx.uriToDbusMerged.find(uri);
++ if (el == ctx.uriToDbusMerged.end())
++ {
++ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
++ "corsresponding to URI "
++ << uri;
++ messages::propertyValueNotInList(
++ res, uri, "MetricProperties/" + std::to_string(uriIdx));
++ return false;
++ }
++
++ const std::string& dbusPath = el->second;
++ ctx.dbusArgs.sensors.emplace_back(dbusPath, uri);
++ uriIdx++;
++ }
++ return true;
++}
++
++inline bool parsePostTriggerParams(crow::Response& res,
++ const crow::Request& req, Context& ctx)
++{
++ std::optional<std::string> id;
++ std::optional<std::string> name;
++ std::optional<std::string> metricType;
++ std::optional<std::vector<std::string>> triggerActions;
++ std::optional<std::string> discreteTriggerCondition;
++ std::optional<std::vector<nlohmann::json>> discreteTriggers;
++ std::optional<nlohmann::json> numericThresholds;
++ std::optional<nlohmann::json> links;
++ if (!json_util::readJsonPatch(
++ req, res, "Id", id, "Name", name, "MetricType", metricType,
++ "TriggerActions", triggerActions, "DiscreteTriggerCondition",
++ discreteTriggerCondition, "DiscreteTriggers", discreteTriggers,
++ "NumericThresholds", numericThresholds, "MetricProperties",
++ ctx.parsedInfo.metricProperties, "Links", links))
++ {
++ return false;
++ }
++
++ ctx.dbusArgs.id = id.value_or("");
++ ctx.dbusArgs.name = name.value_or("");
++
++ if (metricType)
++ {
++ if (!(ctx.parsedInfo.metricType = getMetricType(*metricType)))
++ {
++ messages::propertyValueIncorrect(res, "MetricType", *metricType);
++ return false;
++ }
++ }
++
++ if (discreteTriggerCondition)
++ {
++ if (!(ctx.parsedInfo.discreteCondition =
++ getDiscreteCondition(*discreteTriggerCondition)))
++ {
++ messages::propertyValueIncorrect(res, "DiscreteTriggerCondition",
++ *discreteTriggerCondition);
++ return false;
++ }
++ }
++
++ if (triggerActions)
++ {
++ ctx.dbusArgs.actions.reserve(triggerActions->size());
++ for (const std::string& action : *triggerActions)
++ {
++ if (const std::optional<std::string>& dbusAction =
++ redfishActionToDbusAction(action))
++ {
++ ctx.dbusArgs.actions.emplace_back(*dbusAction);
++ }
++ else
++ {
++ messages::propertyValueNotInList(res, action, "TriggerActions");
++ return false;
++ }
++ }
++ }
++
++ if (!parseTriggerThresholds(res, discreteTriggers, numericThresholds, ctx))
++ {
++ return false;
++ }
++
++ if (links)
++ {
++ if (!parseLinks(res, *links, ctx))
++ {
++ return false;
++ }
++ }
++ return true;
++}
++
++inline void createTrigger(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ Context& ctx)
++{
++ crow::connections::systemBus->async_method_call(
++ [aResp = asyncResp, id = ctx.dbusArgs.id](
++ const boost::system::error_code ec, const std::string& dbusPath) {
++ if (ec == boost::system::errc::file_exists)
++ {
++ messages::resourceAlreadyExists(aResp->res, "Trigger", "Id",
++ id);
++ return;
++ }
++ if (ec == boost::system::errc::too_many_files_open)
++ {
++ messages::createLimitReachedForResource(aResp->res);
++ return;
++ }
++ if (ec)
++ {
++ messages::internalError(aResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ const std::optional<std::string>& triggerId =
++ getTriggerIdFromDbusPath(dbusPath);
++ if (!triggerId)
++ {
++ messages::internalError(aResp->res);
++ BMCWEB_LOG_ERROR << "Unknown data returned by "
++ "AddTrigger DBus method";
++ return;
++ }
++
++ messages::created(aResp->res);
++ const boost::url locationUrl = crow::utility::urlFromPieces(
++ "redfish", "v1", "TelemetryService", "Triggers", *triggerId);
++ aResp->res.addHeader("Location",
++ std::string_view(locationUrl.string().data(),
++ locationUrl.string().size()));
++ },
++ service, "/xyz/openbmc_project/Telemetry/Triggers",
++ "xyz.openbmc_project.Telemetry.TriggerManager", "AddTrigger",
++ "TelemetryService/" + ctx.dbusArgs.id, ctx.dbusArgs.name,
++ ctx.dbusArgs.actions, ctx.dbusArgs.sensors, ctx.dbusArgs.reportNames,
++ ctx.dbusArgs.thresholds);
++}
++
++} // namespace add_trigger
++
++namespace get_trigger
++{
++
+ inline std::optional<std::string>
+ getRedfishFromDbusAction(const std::string& dbusAction)
+ {
+@@ -290,6 +757,8 @@ inline bool fillTrigger(
+ return true;
+ }
+
++} // namespace get_trigger
++
+ } // namespace telemetry
+
+ inline void requestRoutesTriggerCollection(App& app)
+@@ -301,14 +770,77 @@ inline void requestRoutesTriggerCollection(App& app)
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#TriggersCollection.TriggersCollection";
+- asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
++ asyncResp->res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/Triggers";
+ asyncResp->res.jsonValue["Name"] = "Triggers Collection";
+ const std::vector<const char*> interfaces{
+ telemetry::triggerInterface};
+ collection_util::getCollectionMembers(
+- asyncResp, telemetry::triggerUri, interfaces,
++ asyncResp, "/redfish/v1/TelemetryService/Triggers",
++ interfaces,
+ "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
+ });
++
++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
++ .privileges(redfish::privileges::postTriggersCollection)
++ .methods(boost::beast::http::verb::post)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
++ const auto ctx =
++ std::make_shared<telemetry::add_trigger::Context>();
++ if (!telemetry::add_trigger::parsePostTriggerParams(
++ asyncResp->res, req, *ctx))
++ {
++ return;
++ }
++
++ if (!ctx->parsedInfo.metricProperties ||
++ ctx->parsedInfo.metricProperties->empty())
++ {
++ telemetry::add_trigger::createTrigger(asyncResp, *ctx);
++ return;
++ }
++
++ boost::container::flat_set<std::pair<std::string, std::string>>
++ chassisSensors;
++ if (!telemetry::getChassisSensorNode(
++ asyncResp, *ctx->parsedInfo.metricProperties,
++ chassisSensors))
++ {
++ return;
++ }
++
++ const auto finalizer =
++ std::make_shared<utils::Finalizer>([asyncResp, ctx] {
++ if ((asyncResp->res).result() !=
++ boost::beast::http::status::ok)
++ {
++ return;
++ }
++ if (!telemetry::add_trigger::parseMetricProperties(
++ asyncResp->res, *ctx))
++ {
++ return;
++ }
++ telemetry::add_trigger::createTrigger(asyncResp, *ctx);
++ });
++
++ for (const auto& [chassis, sensorType] : chassisSensors)
++ {
++ retrieveUriToDbusMap(
++ chassis, sensorType,
++ [asyncResp, ctx,
++ finalizer](const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ if (status == boost::beast::http::status::ok)
++ {
++ ctx->uriToDbusMerged.insert(uriToDbus.begin(),
++ uriToDbus.end());
++ }
++ });
++ }
++ });
+ }
+
+ inline void requestRoutesTrigger(App& app)
+@@ -339,8 +871,8 @@ inline void requestRoutesTrigger(App& app)
+ return;
+ }
+
+- if (!telemetry::fillTrigger(asyncResp->res.jsonValue,
+- id, ret))
++ if (!telemetry::get_trigger::fillTrigger(
++ asyncResp->res.jsonValue, id, ret))
+ {
+ messages::internalError(asyncResp->res);
+ }
+--
+2.25.1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch
new file mode 100644
index 000000000..0ce5a3ea6
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch
@@ -0,0 +1,559 @@
+From 4bd7f53cc01315774eedef637f5d1824cd7f662a Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Thu, 17 Jun 2021 13:37:57 +0000
+Subject: [PATCH] Switched bmcweb to use new telemetry service API
+
+Added support for multiple MetricProperties. Added support for new
+parameters: CollectionTimeScope, CollectionDuration.
+
+Tested:
+ - It is possible to create MetricReportDefinitions with multiple
+ MetricProperties.
+ - Stub values for new parameters are correctly passed to telemetry
+ service.
+ - All existing telemetry service functionalities remain unchanged.
+
+Change-Id: I2cd17069e3ea015c8f5571c29278f1d50536272a
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
+---
+ include/dbus_utility.hpp | 5 +-
+ redfish-core/lib/metric_report_definition.hpp | 330 ++++++++++++------
+ redfish-core/lib/telemetry_service.hpp | 13 +
+ 3 files changed, 240 insertions(+), 108 deletions(-)
+
+diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp
+index 481b33d..32153f8 100644
+--- a/include/dbus_utility.hpp
++++ b/include/dbus_utility.hpp
+@@ -50,8 +50,9 @@ using DbusVariantType = std::variant<
+ std::vector<std::tuple<std::string, std::string>>,
+ std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>,
+ std::vector<std::tuple<uint32_t, size_t>>,
+- std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+- std::string, std::string>>
++ std::vector<std::tuple<
++ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
++ std::string, std::string, std::string, uint64_t>>
+ >;
+
+ // clang-format on
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index 1a520f3..ac980aa 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -17,50 +17,68 @@ namespace redfish
+
+ namespace telemetry
+ {
+-
+ constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
+
+-using ReadingParameters =
+- std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+- std::string, std::string>>;
++using ReadingParameters = std::vector<std::tuple<
++ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
++ std::string, std::string, std::string, uint64_t>>;
++
++std::string toReadfishReportAction(std::string_view action)
++{
++ if (action == "EmitsReadingsUpdate")
++ {
++ return "RedfishEvent";
++ }
++ if (action == "LogToMetricReportsCollection")
++ {
++ return "LogToMetricReportsCollection";
++ }
++ return "";
++}
++
++std::string toDbusReportAction(std::string_view action)
++{
++ if (action == "RedfishEvent")
++ {
++ return "EmitsReadingsUpdate";
++ }
++ if (action == "LogToMetricReportsCollection")
++ {
++ return "LogToMetricReportsCollection";
++ }
++ return "";
++}
+
+ inline void fillReportDefinition(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
+ const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
+- ret)
++ properties)
+ {
+- asyncResp->res.jsonValue["@odata.type"] =
+- "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
+- asyncResp->res.jsonValue["@odata.id"] =
+- crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
+- "MetricReportDefinitions", id)
+- .string();
+- asyncResp->res.jsonValue["Id"] = id;
+- asyncResp->res.jsonValue["Name"] = id;
+- asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
+- crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
+- "MetricReports", id)
+- .string();
+- asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+- asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite";
+-
+- const bool* emitsReadingsUpdate = nullptr;
+- const bool* logToMetricReportsCollection = nullptr;
++ const std::vector<std::string>* reportActions = nullptr;
+ const ReadingParameters* readingParams = nullptr;
+ const std::string* reportingType = nullptr;
++ const std::string* reportUpdates = nullptr;
++ const std::string* name = nullptr;
++ const uint64_t* appendLimit = nullptr;
+ const uint64_t* interval = nullptr;
+- for (const auto& [key, var] : ret)
++ const bool* enabled = nullptr;
++
++ for (const auto& [key, var] : properties)
+ {
+- if (key == "EmitsReadingsUpdate")
++ if (key == "ReportActions")
++ {
++ reportActions = std::get_if<std::vector<std::string>>(&var);
++ }
++ else if (key == "ReportUpdates")
+ {
+- emitsReadingsUpdate = std::get_if<bool>(&var);
++ reportUpdates = std::get_if<std::string>(&var);
+ }
+- else if (key == "LogToMetricReportsCollection")
++ else if (key == "AppendLimit")
+ {
+- logToMetricReportsCollection = std::get_if<bool>(&var);
++ appendLimit = std::get_if<uint64_t>(&var);
+ }
+- else if (key == "ReadingParameters")
++ else if (key == "ReadingParametersFutureVersion")
+ {
+ readingParams = std::get_if<ReadingParameters>(&var);
+ }
+@@ -72,73 +90,149 @@ inline void fillReportDefinition(
+ {
+ interval = std::get_if<uint64_t>(&var);
+ }
++ else if (key == "Name")
++ {
++ name = std::get_if<std::string>(&var);
++ }
++ else if (key == "Enabled")
++ {
++ enabled = std::get_if<bool>(&var);
++ }
+ }
+- if (emitsReadingsUpdate == nullptr ||
+- logToMetricReportsCollection == nullptr || readingParams == nullptr ||
+- reportingType == nullptr || interval == nullptr)
++
++ std::vector<std::string> redfishReportActions;
++ if (reportActions != nullptr)
+ {
+- BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
+- messages::internalError(asyncResp->res);
+- return;
++ for (const std::string& action : *reportActions)
++ {
++ std::string redfishAction = toReadfishReportAction(action);
++
++ if (redfishAction.empty())
++ {
++ BMCWEB_LOG_ERROR << "Unknown ReportActions element: " << action;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ redfishReportActions.emplace_back(std::move(redfishAction));
++ }
+ }
+
+- std::vector<std::string> redfishReportActions;
+- redfishReportActions.reserve(2);
+- if (*emitsReadingsUpdate)
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
++ asyncResp->res.jsonValue["@odata.id"] =
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReportDefinitions", id)
++ .string();
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "MetricReports", id)
++ .string();
++
++ if (enabled != nullptr)
+ {
+- redfishReportActions.emplace_back("RedfishEvent");
++ asyncResp->res.jsonValue["Status"]["State"] =
++ *enabled ? "Enabled" : "Disabled";
++ asyncResp->res.jsonValue["MetricReportDefinitionEnabled"] = *enabled;
+ }
+- if (*logToMetricReportsCollection)
++
++ if (appendLimit != nullptr)
+ {
+- redfishReportActions.emplace_back("LogToMetricReportsCollection");
++ asyncResp->res.jsonValue["AppendLimit"] = *appendLimit;
+ }
+
+- nlohmann::json metrics = nlohmann::json::array();
+- for (const auto& [sensorPath, operationType, id, metadata] : *readingParams)
++ if (reportUpdates != nullptr)
+ {
+- metrics.push_back({
+- {"MetricId", id},
+- {"MetricProperties", {metadata}},
+- });
++ asyncResp->res.jsonValue["ReportUpdates"] = *reportUpdates;
++ }
++
++ if (name != nullptr)
++ {
++ asyncResp->res.jsonValue["Name"] = *name;
++ }
++
++ if (reportActions != nullptr)
++ {
++ asyncResp->res.jsonValue["ReportActions"] =
++ std::move(redfishReportActions);
++ }
++
++ if (reportingType != nullptr)
++ {
++ asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType;
++ }
++
++ if (interval != nullptr)
++ {
++ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
++ time_utils::toDurationString(std::chrono::milliseconds(*interval));
++ }
++
++ if (readingParams != nullptr)
++ {
++ nlohmann::json& metrics = asyncResp->res.jsonValue["Metrics"];
++ metrics = nlohmann::json::array();
++ for (auto& [sensorData, collectionFunction, id, collectionTimeScope,
++ collectionDuration] : *readingParams)
++ {
++ std::vector<std::string> metricProperties;
++
++ for (const auto& [sensorPath, sensorMetadata] : sensorData)
++ {
++ metricProperties.emplace_back(sensorMetadata);
++ }
++
++ metrics.push_back(
++ {{"MetricId", id},
++ {"MetricProperties", std::move(metricProperties)},
++ {"CollectionFunction", collectionFunction},
++ {"CollectionDuration",
++ time_utils::toDurationString(
++ std::chrono::milliseconds(collectionDuration))},
++ {"CollectionTimeScope", collectionTimeScope}});
++ }
+ }
+- asyncResp->res.jsonValue["Metrics"] = metrics;
+- asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType;
+- asyncResp->res.jsonValue["ReportActions"] = redfishReportActions;
+- asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
+- time_utils::toDurationString(std::chrono::milliseconds(*interval));
+ }
+
+ struct AddReportArgs
+ {
+- std::string name;
++ struct MetricArgs
++ {
++ std::string id;
++ std::vector<std::string> uris;
++ std::optional<std::string> collectionFunction;
++ std::optional<std::string> collectionTimeScope;
++ std::optional<uint64_t> collectionDuration;
++ };
++
++ std::optional<std::string> id;
++ std::optional<std::string> name;
+ std::string reportingType;
+- bool emitsReadingsUpdate = false;
+- bool logToMetricReportsCollection = false;
++ std::optional<std::string> reportUpdates;
++ std::optional<uint64_t> appendLimit;
++ std::vector<std::string> reportActions;
+ uint64_t interval = 0;
+- std::vector<std::pair<std::string, std::vector<std::string>>> metrics;
++ std::vector<MetricArgs> metrics;
+ };
+
+ inline bool toDbusReportActions(crow::Response& res,
+- std::vector<std::string>& actions,
++ const std::vector<std::string>& actions,
+ AddReportArgs& args)
+ {
+ size_t index = 0;
+- for (auto& action : actions)
++ for (const auto& action : actions)
+ {
+- if (action == "RedfishEvent")
+- {
+- args.emitsReadingsUpdate = true;
+- }
+- else if (action == "LogToMetricReportsCollection")
+- {
+- args.logToMetricReportsCollection = true;
+- }
+- else
++ std::string dbusReportAction = toDbusReportAction(action);
++
++ if (dbusReportAction.empty())
+ {
+ messages::propertyValueNotInList(
+ res, action, "ReportActions/" + std::to_string(index));
+ return false;
+ }
++
++ args.reportActions.emplace_back(std::move(dbusReportAction));
+ index++;
+ }
+ return true;
+@@ -150,27 +244,17 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+ std::vector<nlohmann::json> metrics;
+ std::vector<std::string> reportActions;
+ std::optional<nlohmann::json> schedule;
+- if (!json_util::readJsonPatch(req, res, "Id", args.name, "Metrics", metrics,
+- "MetricReportDefinitionType",
+- args.reportingType, "ReportActions",
+- reportActions, "Schedule", schedule))
+- {
+- return false;
+- }
+-
+- constexpr const char* allowedCharactersInName =
+- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+- if (args.name.empty() || args.name.find_first_not_of(
+- allowedCharactersInName) != std::string::npos)
++ if (!json_util::readJsonPatch(
++ req, res, "Id", args.id, "Name", args.name, "Metrics", metrics,
++ "MetricReportDefinitionType", args.reportingType, "ReportUpdates",
++ args.reportUpdates, "AppendLimit", args.appendLimit,
++ "ReportActions", reportActions, "Schedule", schedule))
+ {
+- BMCWEB_LOG_ERROR << "Failed to match " << args.name
+- << " with allowed character "
+- << allowedCharactersInName;
+- messages::propertyValueIncorrect(res, "Id", args.name);
+ return false;
+ }
+
+- if (args.reportingType != "Periodic" && args.reportingType != "OnRequest")
++ if (args.reportingType != "Periodic" && args.reportingType != "OnRequest" &&
++ args.reportingType != "OnChange")
+ {
+ messages::propertyValueNotInList(res, args.reportingType,
+ "MetricReportDefinitionType");
+@@ -211,15 +295,35 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+ args.metrics.reserve(metrics.size());
+ for (auto& m : metrics)
+ {
+- std::string id;
+- std::vector<std::string> uris;
+- if (!json_util::readJson(m, res, "MetricId", id, "MetricProperties",
+- uris))
++ std::optional<std::string> collectionDurationStr;
++ AddReportArgs::MetricArgs metricArgs;
++ if (!json_util::readJson(
++ m, res, "MetricId", metricArgs.id, "MetricProperties",
++ metricArgs.uris, "CollectionFunction",
++ metricArgs.collectionFunction, "CollectionTimeScope",
++ metricArgs.collectionTimeScope, "CollectionDuration",
++ collectionDurationStr))
+ {
+ return false;
+ }
+
+- args.metrics.emplace_back(std::move(id), std::move(uris));
++ if (collectionDurationStr)
++ {
++ std::optional<std::chrono::milliseconds> duration =
++ time_utils::fromDurationString(*collectionDurationStr);
++
++ if (!duration || duration->count() < 0)
++ {
++ messages::propertyValueIncorrect(res, "CollectionDuration",
++ *collectionDurationStr);
++ return false;
++ }
++
++ metricArgs.collectionDuration =
++ static_cast<uint64_t>(duration->count());
++ }
++
++ args.metrics.emplace_back(std::move(metricArgs));
+ }
+
+ return true;
+@@ -227,15 +331,14 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+
+ inline bool getChassisSensorNode(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::vector<std::pair<std::string, std::vector<std::string>>>&
+- metrics,
++ const std::vector<AddReportArgs::MetricArgs>& metrics,
+ boost::container::flat_set<std::pair<std::string, std::string>>& matched)
+ {
+- for (const auto& [id, uris] : metrics)
++ for (const auto& metric : metrics)
+ {
+- for (size_t i = 0; i < uris.size(); i++)
++ for (size_t i = 0; i < metric.uris.size(); i++)
+ {
+- const std::string& uri = uris[i];
++ const std::string& uri = metric.uris[i];
+ std::string chassis;
+ std::string node;
+
+@@ -280,11 +383,16 @@ class AddReport
+ telemetry::ReadingParameters readingParams;
+ readingParams.reserve(args.metrics.size());
+
+- for (const auto& [id, uris] : args.metrics)
++ for (auto& metric : args.metrics)
+ {
+- for (size_t i = 0; i < uris.size(); i++)
++ std::vector<
++ std::tuple<sdbusplus::message::object_path, std::string>>
++ sensorParams;
++ sensorParams.reserve(metric.uris.size());
++
++ for (size_t i = 0; i < metric.uris.size(); i++)
+ {
+- const std::string& uri = uris[i];
++ const std::string& uri = metric.uris[i];
+ auto el = uriToDbus.find(uri);
+ if (el == uriToDbus.end())
+ {
+@@ -298,17 +406,23 @@ class AddReport
+ }
+
+ const std::string& dbusPath = el->second;
+- readingParams.emplace_back(dbusPath, "SINGLE", id, uri);
++ sensorParams.emplace_back(dbusPath, uri);
+ }
++
++ readingParams.emplace_back(
++ std::move(sensorParams), metric.collectionFunction.value_or(""),
++ std::move(metric.id), metric.collectionTimeScope.value_or(""),
++ metric.collectionDuration.value_or(0U));
+ }
+ const std::shared_ptr<bmcweb::AsyncResp> aResp = asyncResp;
+ crow::connections::systemBus->async_method_call(
+- [aResp, name = args.name, uriToDbus = std::move(uriToDbus)](
++ [aResp, id = args.id.value_or(""),
++ uriToDbus = std::move(uriToDbus)](
+ const boost::system::error_code ec, const std::string&) {
+ if (ec == boost::system::errc::file_exists)
+ {
+ messages::resourceAlreadyExists(
+- aResp->res, "MetricReportDefinition", "Id", name);
++ aResp->res, "MetricReportDefinition", "Id", id);
+ return;
+ }
+ if (ec == boost::system::errc::too_many_files_open)
+@@ -338,10 +452,12 @@ class AddReport
+ messages::created(aResp->res);
+ },
+ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
+- "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
+- "TelemetryService/" + args.name, args.reportingType,
+- args.emitsReadingsUpdate, args.logToMetricReportsCollection,
+- args.interval, readingParams);
++ "xyz.openbmc_project.Telemetry.ReportManager",
++ "AddReportFutureVersion",
++ "TelemetryService/" + args.id.value_or(""), args.name.value_or(""),
++ args.reportingType, args.reportUpdates.value_or("Overwrite"),
++ args.appendLimit.value_or(0), args.reportActions, args.interval,
++ readingParams);
+ }
+
+ AddReport(const AddReport&) = delete;
+@@ -436,10 +552,10 @@ inline void requestRoutesMetricReportDefinition(App& app)
+ const std::string& id) {
+ crow::connections::systemBus->async_method_call(
+ [asyncResp,
+- id](const boost::system::error_code ec,
++ id](boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string, dbus::utility::DbusVariantType>>&
+- ret) {
++ properties) {
+ if (ec.value() == EBADR ||
+ ec == boost::system::errc::host_unreachable)
+ {
+@@ -454,12 +570,14 @@ inline void requestRoutesMetricReportDefinition(App& app)
+ return;
+ }
+
+- telemetry::fillReportDefinition(asyncResp, id, ret);
++ telemetry::fillReportDefinition(asyncResp, id,
++ properties);
+ },
+ telemetry::service, telemetry::getDbusReportPath(id),
+ "org.freedesktop.DBus.Properties", "GetAll",
+ telemetry::reportInterface);
+ });
++
+ BMCWEB_ROUTE(app,
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
+ .privileges(redfish::privileges::deleteMetricReportDefinitionCollection)
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index c1fe7d0..64d712b 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -49,6 +49,8 @@ inline void handleTelemetryServiceGet(
+
+ const size_t* maxReports = nullptr;
+ const uint64_t* minInterval = nullptr;
++ const std::vector<std::string>* supportedCollectionFunction =
++ nullptr;
+ for (const auto& [key, var] : ret)
+ {
+ if (key == "MaxReports")
+@@ -59,6 +61,11 @@ inline void handleTelemetryServiceGet(
+ {
+ minInterval = std::get_if<uint64_t>(&var);
+ }
++ else if (key == "SupportedOperationTypes")
++ {
++ supportedCollectionFunction =
++ std::get_if<std::vector<std::string>>(&var);
++ }
+ }
+ if (maxReports == nullptr || minInterval == nullptr)
+ {
+@@ -72,6 +79,12 @@ inline void handleTelemetryServiceGet(
+ asyncResp->res.jsonValue["MinCollectionInterval"] =
+ time_utils::toDurationString(std::chrono::milliseconds(
+ static_cast<time_t>(*minInterval)));
++
++ if (supportedCollectionFunction != nullptr)
++ {
++ asyncResp->res.jsonValue["SupportedCollectionFunction"] =
++ *supportedCollectionFunction;
++ }
+ },
+ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
+ "org.freedesktop.DBus.Properties", "GetAll",
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Add-PUT-and-PATCH-for-MetricReportDefinition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Add-PUT-and-PATCH-for-MetricReportDefinition.patch
new file mode 100644
index 000000000..ea409fe09
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Add-PUT-and-PATCH-for-MetricReportDefinition.patch
@@ -0,0 +1,948 @@
+From 4c39922af8bf73b13150455166e7bd1fd8645a47 Mon Sep 17 00:00:00 2001
+From: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
+Date: Fri, 17 Dec 2021 13:02:23 +0100
+Subject: [PATCH] Add PUT and PATCH for MetricReportDefinition
+
+Support for PUT and PATCH methods is added to Metric Report Definition,
+now Report can be replaced by PUT or selected read/write properties can
+be modified by PATCH method
+
+Tested:
+- Added new Report via PUT and extracted Report via GET checking if
+ received data is appropriate
+- Added Report via POST, overwrite it via PUT and extracted Report via
+ GET checking if received data is appropriate
+- Added Report via POST, overwrite editable properties via PATCH and
+ fetched Report via GET checking if received data is properly modified
+
+Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
+Change-Id: If75110a92c55c9e4f2415f0ed4471baa802643ff
+---
+ redfish-core/lib/metric_report_definition.hpp | 774 ++++++++++++++++--
+ 1 file changed, 692 insertions(+), 82 deletions(-)
+
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index 30d83b0..e726c8c 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -8,9 +8,9 @@
+ #include <boost/container/flat_map.hpp>
+ #include <dbus_utility.hpp>
+ #include <registries/privilege_registry.hpp>
++#include <utils/stl_utils.hpp>
+
+ #include <tuple>
+-#include <variant>
+
+ namespace redfish
+ {
+@@ -22,6 +22,12 @@ using ReadingParameters = std::vector<std::tuple<
+ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
+ std::string, std::string, std::string, uint64_t>>;
+
++enum class addReportType
++{
++ create,
++ replace
++};
++
+ std::string toReadfishReportAction(std::string_view action)
+ {
+ if (action == "EmitsReadingsUpdate")
+@@ -48,6 +54,38 @@ std::string toDbusReportAction(std::string_view action)
+ return "";
+ }
+
++inline bool verifyCommonErrors(crow::Response& res, const std::string& id,
++ const boost::system::error_code ec)
++{
++ if (ec.value() == EBADR || ec == boost::system::errc::host_unreachable)
++ {
++ messages::resourceNotFound(res, "MetricReportDefinition", id);
++ return false;
++ }
++
++ if (ec == boost::system::errc::file_exists)
++ {
++ messages::resourceAlreadyExists(res, "MetricReportDefinition", "Id",
++ id);
++ return false;
++ }
++
++ if (ec == boost::system::errc::too_many_files_open)
++ {
++ messages::createLimitReachedForResource(res);
++ return false;
++ }
++
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "DBUS response error " << ec;
++ messages::internalError(res);
++ return false;
++ }
++
++ return true;
++}
++
+ inline void fillReportDefinition(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
+ const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
+@@ -193,17 +231,17 @@ inline void fillReportDefinition(
+ }
+ }
+
+-struct AddReportArgs
++struct MetricArgs
+ {
+- struct MetricArgs
+- {
+- std::string id;
+- std::vector<std::string> uris;
+- std::optional<std::string> collectionFunction;
+- std::optional<std::string> collectionTimeScope;
+- std::optional<uint64_t> collectionDuration;
+- };
++ std::string id;
++ std::vector<std::string> uris;
++ std::optional<std::string> collectionFunction;
++ std::optional<std::string> collectionTimeScope;
++ std::optional<uint64_t> collectionDuration;
++};
+
++struct AddReportArgs
++{
+ std::optional<std::string> id;
+ std::optional<std::string> name;
+ std::string reportingType;
+@@ -215,22 +253,22 @@ struct AddReportArgs
+ };
+
+ inline bool toDbusReportActions(crow::Response& res,
+- const std::vector<std::string>& actions,
+- AddReportArgs& args)
++ const std::vector<std::string>& redfishActions,
++ std::vector<std::string>& dbusActions)
+ {
+ size_t index = 0;
+- for (const auto& action : actions)
++ for (const auto& redfishAction : redfishActions)
+ {
+- std::string dbusReportAction = toDbusReportAction(action);
++ std::string dbusAction = toDbusReportAction(redfishAction);
+
+- if (dbusReportAction.empty())
++ if (dbusAction.empty())
+ {
+ messages::propertyValueNotInList(
+- res, action, "ReportActions/" + std::to_string(index));
++ res, redfishAction, "ReportActions/" + std::to_string(index));
+ return false;
+ }
+
+- args.reportActions.emplace_back(std::move(dbusReportAction));
++ dbusActions.emplace_back(std::move(dbusAction));
+ index++;
+ }
+ return true;
+@@ -259,7 +297,7 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+ return false;
+ }
+
+- if (!toDbusReportActions(res, reportActions, args))
++ if (!toDbusReportActions(res, reportActions, args.reportActions))
+ {
+ return false;
+ }
+@@ -294,7 +332,7 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+ for (auto& m : metrics)
+ {
+ std::optional<std::string> collectionDurationStr;
+- AddReportArgs::MetricArgs metricArgs;
++ MetricArgs metricArgs;
+ if (!json_util::readJson(
+ m, res, "MetricId", metricArgs.id, "MetricProperties",
+ metricArgs.uris, "CollectionFunction",
+@@ -329,7 +367,7 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req,
+
+ inline bool getChassisSensorNode(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::vector<AddReportArgs::MetricArgs>& metrics,
++ const std::vector<MetricArgs>& metrics,
+ boost::container::flat_set<std::pair<std::string, std::string>>& matched)
+ {
+ for (const auto& metric : metrics)
+@@ -363,13 +401,122 @@ inline bool getChassisSensorNode(
+ return true;
+ }
+
++inline bool getReadingParametersFromMetrics(
++ crow::Response& res, const std::vector<MetricArgs>& metrics,
++ const boost::container::flat_map<std::string, std::string>& uriToDbus,
++ ReadingParameters& readingParams)
++{
++ if (metrics.empty())
++ {
++ return true;
++ }
++
++ readingParams.reserve(metrics.size());
++ for (const auto& metric : metrics)
++ {
++ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>
++ sensorParams;
++ sensorParams.reserve(metric.uris.size());
++
++ for (size_t i = 0; i < metric.uris.size(); i++)
++ {
++ const std::string& uri = metric.uris[i];
++ auto el = uriToDbus.find(uri);
++ if (el == uriToDbus.end())
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to find DBus sensor corresponding to URI "
++ << uri;
++ messages::propertyValueNotInList(
++ res, uri, "MetricProperties/" + std::to_string(i));
++ return false;
++ }
++
++ const std::string& dbusPath = el->second;
++ sensorParams.emplace_back(dbusPath, uri);
++ }
++
++ readingParams.emplace_back(
++ std::move(sensorParams), metric.collectionFunction.value_or(""),
++ metric.id, metric.collectionTimeScope.value_or(""),
++ metric.collectionDuration.value_or(0U));
++ }
++
++ return true;
++}
++
++class UpdateMetrics
++{
++ public:
++ UpdateMetrics(const std::string& idIn, std::vector<MetricArgs> metricsIn,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) :
++ asyncResp(asyncResp),
++ id(idIn), metrics{std::move(metricsIn)}
++ {}
++
++ ~UpdateMetrics()
++ {
++ setReadingParams();
++ }
++
++ UpdateMetrics(const UpdateMetrics&) = delete;
++ UpdateMetrics(UpdateMetrics&&) = delete;
++ UpdateMetrics& operator=(const UpdateMetrics&) = delete;
++ UpdateMetrics& operator=(UpdateMetrics&&) = delete;
++
++ void insert(const boost::container::flat_map<std::string, std::string>& el)
++ {
++ uriToDbus.insert(el.begin(), el.end());
++ }
++
++ void setReadingParams()
++ {
++ if (asyncResp->res.result() != boost::beast::http::status::ok)
++ {
++ return;
++ }
++
++ if (!getReadingParametersFromMetrics(asyncResp->res, metrics, uriToDbus,
++ readingParams))
++ {
++ return;
++ }
++
++ const std::shared_ptr<bmcweb::AsyncResp> aResp = asyncResp;
++ crow::connections::systemBus->async_method_call(
++ [aResp, id = id](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ messages::propertyValueModified(aResp->res, "Metrics",
++ "Updated");
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report",
++ "ReadingParametersFutureVersion",
++ dbus::utility::DbusVariantType{readingParams});
++ }
++
++ private:
++ const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
++ std::string id;
++ std::vector<MetricArgs> metrics;
++ boost::container::flat_map<std::string, std::string> uriToDbus{};
++ ReadingParameters readingParams{};
++};
++
+ class AddReport
+ {
+ public:
+ AddReport(AddReportArgs argsIn,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) :
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ addReportType type) :
+ asyncResp(asyncResp),
+- args{std::move(argsIn)}
++ args{std::move(argsIn)}, type(type)
+ {}
+ ~AddReport()
+ {
+@@ -378,7 +525,7 @@ class AddReport
+ return;
+ }
+
+- telemetry::ReadingParameters readingParams;
++ ReadingParameters readingParams;
+ readingParams.reserve(args.metrics.size());
+
+ for (auto& metric : args.metrics)
+@@ -412,22 +559,12 @@ class AddReport
+ std::move(metric.id), metric.collectionTimeScope.value_or(""),
+ metric.collectionDuration.value_or(0U));
+ }
++
+ const std::shared_ptr<bmcweb::AsyncResp> aResp = asyncResp;
+ crow::connections::systemBus->async_method_call(
+- [aResp, id = args.id.value_or(""),
+- uriToDbus = std::move(uriToDbus)](
+- const boost::system::error_code ec, const std::string&) {
+- if (ec == boost::system::errc::file_exists)
+- {
+- messages::resourceAlreadyExists(
+- aResp->res, "MetricReportDefinition", "Id", id);
+- return;
+- }
+- if (ec == boost::system::errc::too_many_files_open)
+- {
+- messages::createLimitReachedForResource(aResp->res);
+- return;
+- }
++ [aResp, id = args.id.value_or(""), uriToDbus = std::move(uriToDbus),
++ type = type](const boost::system::error_code ec,
++ const std::string&) {
+ if (ec == boost::system::errc::argument_list_too_long)
+ {
+ nlohmann::json metricProperties = nlohmann::json::array();
+@@ -440,16 +577,22 @@ class AddReport
+ "MetricProperties");
+ return;
+ }
+- if (ec)
++
++ if (!verifyCommonErrors(aResp->res, id, ec))
+ {
+- messages::internalError(aResp->res);
+- BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
+ return;
+ }
+
+- messages::created(aResp->res);
++ if (type == addReportType::create)
++ {
++ messages::created(aResp->res);
++ }
++ else
++ {
++ messages::success(aResp->res);
++ }
+ },
+- telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
++ service, "/xyz/openbmc_project/Telemetry/Reports",
+ "xyz.openbmc_project.Telemetry.ReportManager",
+ "AddReportFutureVersion",
+ "TelemetryService/" + args.id.value_or(""), args.name.value_or(""),
+@@ -471,8 +614,491 @@ class AddReport
+ private:
+ const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
+ AddReportArgs args;
++ const addReportType type;
+ boost::container::flat_map<std::string, std::string> uriToDbus{};
+ };
++
++inline void setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id, bool enabled)
++{
++ crow::connections::systemBus->async_method_call(
++ [aResp, id,
++ enabled](const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& currEnabledVar) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ const bool* currEnabled = std::get_if<bool>(&currEnabledVar);
++ if (currEnabled == nullptr || *currEnabled == enabled)
++ {
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, enabled](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ messages::propertyValueModified(
++ aResp->res, "MetricReportDefinitionEnabled",
++ enabled ? "True" : "False");
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report", "Enabled",
++ dbus::utility::DbusVariantType{enabled});
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "Enabled");
++}
++
++inline void setReportType(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id, const std::string& type)
++{
++ if (type != "Periodic" && type != "OnChange" && type != "OnRequest")
++ {
++ messages::propertyValueNotInList(aResp->res, type,
++ "MetricReportDefinitionType");
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, type](const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& currTypeVar) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ const std::string* currType =
++ std::get_if<std::string>(&currTypeVar);
++ if (currType == nullptr || *currType == type)
++ {
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, type](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ messages::propertyValueModified(
++ aResp->res, "MetricReportDefinitionType", type);
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report", "ReportingType",
++ dbus::utility::DbusVariantType{type});
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "ReportingType");
++}
++
++inline void setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id, const std::string& updates)
++{
++ if (updates != "Overwrite" && updates != "AppendWrapsWhenFull" &&
++ updates != "AppendStopsWhenFull")
++ {
++ messages::propertyValueNotInList(aResp->res, updates, "ReportUpdates");
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id,
++ updates](const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& currUpdatesVar) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ const std::string* currUpdates =
++ std::get_if<std::string>(&currUpdatesVar);
++ if (currUpdates == nullptr || *currUpdates == updates)
++ {
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, updates](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ messages::propertyValueModified(aResp->res, "ReportUpdates",
++ updates);
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report", "ReportUpdates",
++ dbus::utility::DbusVariantType{updates});
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "ReportUpdates");
++}
++
++inline void setReportActions(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id,
++ std::vector<std::string>& redfishActions)
++{
++ if (redfishActions.size() > 1)
++ {
++ stl_utils::removeDuplicate(redfishActions);
++ }
++
++ std::vector<std::string> newDbusActions;
++ if (!toDbusReportActions(aResp->res, redfishActions, newDbusActions))
++ {
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, redfishActions = std::move(redfishActions),
++ newDbusActions = std::move(newDbusActions)](
++ const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& currDbusActionsVar) mutable {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ std::vector<std::string> currDbusActions;
++ if (const std::vector<std::string>* tmp =
++ std::get_if<std::vector<std::string>>(&currDbusActionsVar))
++ {
++ currDbusActions = *tmp;
++ }
++ else
++ {
++ messages::internalError(aResp->res);
++ return;
++ }
++
++ if (newDbusActions.size() == currDbusActions.size())
++ {
++ std::sort(newDbusActions.begin(), newDbusActions.end());
++ if (newDbusActions == currDbusActions)
++ {
++ return;
++ }
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id,
++ redfishActions](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ std::string redfishActionsStr;
++ for (const auto& redfishAction : redfishActions)
++ {
++ redfishActionsStr += redfishAction + std::string(" ");
++ }
++ if (!redfishActionsStr.empty())
++ {
++ redfishActionsStr.pop_back();
++ }
++ messages::propertyValueModified(aResp->res, "ReportActions",
++ redfishActionsStr);
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report", "ReportActions",
++ dbus::utility::DbusVariantType{newDbusActions});
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "ReportActions");
++}
++
++inline void setReportInterval(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id, nlohmann::json& schedule)
++{
++ crow::connections::systemBus->async_method_call(
++ [aResp, schedule = std::move(schedule),
++ id](const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& typeVar) mutable {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ const std::string* reportingType =
++ std::get_if<std::string>(&typeVar);
++ if (reportingType == nullptr || *reportingType != "Periodic")
++ {
++ return;
++ }
++
++ std::string durationStr;
++ if (!json_util::readJson(schedule, aResp->res, "RecurrenceInterval",
++ durationStr))
++ {
++ return;
++ }
++
++ std::optional<std::chrono::milliseconds> durationNum =
++ time_utils::fromDurationString(durationStr);
++ uint64_t interval = static_cast<uint64_t>(durationNum->count());
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, interval, durationStr](
++ const boost::system::error_code ec,
++ const dbus::utility::DbusVariantType& currIntervalVar) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ const uint64_t* currInterval =
++ std::get_if<uint64_t>(&currIntervalVar);
++ if (currInterval == nullptr || *currInterval == interval)
++ {
++ return;
++ }
++ crow::connections::systemBus->async_method_call(
++ [aResp, id,
++ durationStr](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++
++ messages::propertyValueModified(
++ aResp->res, "RecurrenceInterval", durationStr);
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/"
++ "TelemetryService/" +
++ id,
++ "org.freedesktop.DBus.Properties", "Set",
++ "xyz.openbmc_project.Telemetry.Report", "Interval",
++ dbus::utility::DbusVariantType{interval});
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "Interval");
++ },
++ "xyz.openbmc_project.Telemetry",
++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id,
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Telemetry.Report", "ReportingType");
++}
++
++inline void setReportMetrics(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id,
++ std::vector<nlohmann::json>& metricJsons)
++{
++ std::vector<MetricArgs> metrics;
++ metrics.reserve(metricJsons.size());
++
++ for (auto& m : metricJsons)
++ {
++ MetricArgs metricArgs;
++ std::optional<std::string> collectionDurationStr;
++ if (!json_util::readJson(
++ m, aResp->res, "MetricId", metricArgs.id, "MetricProperties",
++ metricArgs.uris, "CollectionFunction",
++ metricArgs.collectionFunction, "CollectionTimeScope",
++ metricArgs.collectionTimeScope, "CollectionDuration",
++ collectionDurationStr))
++ {
++ return;
++ }
++
++ if (collectionDurationStr)
++ {
++ std::optional<std::chrono::milliseconds> duration =
++ time_utils::fromDurationString(*collectionDurationStr);
++
++ if (!duration || duration->count() < 0)
++ {
++ messages::propertyValueIncorrect(
++ aResp->res, "CollectionDuration", *collectionDurationStr);
++ return;
++ }
++
++ metricArgs.collectionDuration =
++ static_cast<uint64_t>(duration->count());
++ }
++
++ metrics.emplace_back(std::move(metricArgs));
++ }
++
++ boost::container::flat_set<std::pair<std::string, std::string>>
++ chassisSensors;
++ if (!getChassisSensorNode(aResp, metrics, chassisSensors))
++ {
++ return;
++ }
++
++ auto updateMetricsReq =
++ std::make_shared<UpdateMetrics>(id, std::move(metrics), aResp);
++
++ for (const auto& [chassis, sensorType] : chassisSensors)
++ {
++ retrieveUriToDbusMap(
++ chassis, sensorType,
++ [aResp, updateMetricsReq](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<std::string, std::string>&
++ uriToDbus) {
++ if (status != boost::beast::http::status::ok)
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to retrieve URI to dbus sensors map with err "
++ << static_cast<unsigned>(status);
++ return;
++ }
++ updateMetricsReq->insert(uriToDbus);
++ });
++ }
++}
++
++inline void handleReportPatch(const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id)
++{
++ std::optional<bool> enabled;
++ std::optional<std::string> type;
++ std::optional<std::string> updates;
++ std::optional<std::vector<std::string>> actions;
++ std::optional<nlohmann::json> schedule;
++ std::optional<std::vector<nlohmann::json>> metrics;
++
++ if (!json_util::readJsonPatch(
++ req, aResp->res, "MetricReportDefinitionEnabled", enabled,
++ "Schedule", schedule, "ReportActions", actions, "Metrics", metrics,
++ "MetricReportDefinitionType", type, "ReportUpdates", updates))
++ {
++ return;
++ }
++
++ if (enabled)
++ {
++ setReportEnabled(aResp, id, *enabled);
++ }
++ if (type)
++ {
++ setReportType(aResp, id, *type);
++ }
++ if (updates)
++ {
++ setReportUpdates(aResp, id, *updates);
++ }
++ if (actions)
++ {
++ setReportActions(aResp, id, *actions);
++ }
++ if (schedule)
++ {
++ setReportInterval(aResp, id, *schedule);
++ }
++ if (metrics)
++ {
++ setReportMetrics(aResp, id, *metrics);
++ }
++}
++
++inline void handleReportPut(const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id)
++{
++ AddReportArgs args;
++ if (!getUserParameters(aResp->res, req, args))
++ {
++ return;
++ }
++
++ boost::container::flat_set<std::pair<std::string, std::string>>
++ chassisSensors;
++ if (!getChassisSensorNode(aResp, args.metrics, chassisSensors))
++ {
++ return;
++ }
++
++ const std::string reportPath = getDbusReportPath(id);
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id, args = std::move(args),
++ chassisSensors =
++ std::move(chassisSensors)](const boost::system::error_code ec) {
++ addReportType addReportMode = addReportType::replace;
++ if (ec)
++ {
++ if (ec.value() != EBADR)
++ {
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ messages::internalError(aResp->res);
++ return;
++ }
++ BMCWEB_LOG_INFO << "Report not found, creating new report: "
++ << id;
++ addReportMode = addReportType::create;
++ }
++
++ auto addReportReq =
++ std::make_shared<AddReport>(args, aResp, addReportMode);
++ for (const auto& [chassis, sensorType] : chassisSensors)
++ {
++ retrieveUriToDbusMap(
++ chassis, sensorType,
++ [aResp,
++ addReportReq](const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ if (status != boost::beast::http::status::ok)
++ {
++ BMCWEB_LOG_ERROR
++ << "Failed to retrieve URI to dbus "
++ "sensors map with err "
++ << static_cast<unsigned>(status);
++ return;
++ }
++ addReportReq->insert(uriToDbus);
++ });
++ }
++ },
++ service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
++}
++
++inline void handleReportDelete(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const std::string& id)
++{
++ const std::string reportPath = getDbusReportPath(id);
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, id](const boost::system::error_code ec) {
++ if (!verifyCommonErrors(aResp->res, id, ec))
++ {
++ return;
++ }
++ aResp->res.result(boost::beast::http::status::no_content);
++ },
++ service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
++}
+ } // namespace telemetry
+
+ inline void requestRoutesMetricReportDefinitionCollection(App& app)
+@@ -517,7 +1143,7 @@ inline void requestRoutesMetricReportDefinitionCollection(App& app)
+ }
+
+ auto addReportReq = std::make_shared<telemetry::AddReport>(
+- std::move(args), asyncResp);
++ std::move(args), asyncResp, telemetry::addReportType::create);
+ for (const auto& [chassis, sensorType] : chassisSensors)
+ {
+ retrieveUriToDbusMap(
+@@ -554,17 +1180,9 @@ inline void requestRoutesMetricReportDefinition(App& app)
+ const std::vector<std::pair<
+ std::string, dbus::utility::DbusVariantType>>&
+ properties) {
+- if (ec.value() == EBADR ||
+- ec == boost::system::errc::host_unreachable)
++ if (!redfish::telemetry::verifyCommonErrors(
++ asyncResp->res, id, ec))
+ {
+- messages::resourceNotFound(
+- asyncResp->res, "MetricReportDefinition", id);
+- return;
+- }
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
+- messages::internalError(asyncResp->res);
+ return;
+ }
+
+@@ -578,40 +1196,32 @@ inline void requestRoutesMetricReportDefinition(App& app)
+
+ BMCWEB_ROUTE(app,
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
+- .privileges(redfish::privileges::deleteMetricReportDefinitionCollection)
++ .privileges(redfish::privileges::deleteMetricReportDefinition)
+ .methods(boost::beast::http::verb::delete_)(
+ [](const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::string& id)
+-
+- {
+- const std::string reportPath = telemetry::getDbusReportPath(id);
+-
+- crow::connections::systemBus->async_method_call(
+- [asyncResp, id](const boost::system::error_code ec) {
+- /*
+- * boost::system::errc and std::errc are missing value
+- * for EBADR error that is defined in Linux.
+- */
+- if (ec.value() == EBADR)
+- {
+- messages::resourceNotFound(
+- asyncResp->res, "MetricReportDefinition", id);
+- return;
+- }
++ const std::string& id) {
++ telemetry::handleReportDelete(asyncResp, id);
++ });
+
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
+- messages::internalError(asyncResp->res);
+- return;
+- }
++ BMCWEB_ROUTE(app,
++ "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
++ .privileges(redfish::privileges::putMetricReportDefinition)
++ .methods(boost::beast::http::verb::put)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& id) {
++ telemetry::handleReportPut(req, asyncResp, id);
++ });
+
+- asyncResp->res.result(
+- boost::beast::http::status::no_content);
+- },
+- telemetry::service, reportPath,
+- "xyz.openbmc_project.Object.Delete", "Delete");
++ BMCWEB_ROUTE(app,
++ "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
++ .privileges(redfish::privileges::patchMetricReportDefinition)
++ .methods(boost::beast::http::verb::patch)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& id) {
++ telemetry::handleReportPatch(req, asyncResp, id);
+ });
+ }
+ } // namespace redfish
+--
+2.25.1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-Add-Links-Triggers-to-MetricReportDefinition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-Add-Links-Triggers-to-MetricReportDefinition.patch
new file mode 100644
index 000000000..e60da9421
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0007-Add-Links-Triggers-to-MetricReportDefinition.patch
@@ -0,0 +1,107 @@
+From 0343399b0a609384da302272576a9c409f3b2794 Mon Sep 17 00:00:00 2001
+From: Szymon Dompke <szymon.dompke@intel.com>
+Date: Mon, 21 Mar 2022 17:40:36 +0100
+Subject: [PATCH] Add Links/Triggers to MetricReportDefinition
+
+This change is adding Triggers property to Links when GET is called on
+MetricReportDefinition. It contains array of @odata.id pointing to
+Trigger resource if it is also linking to given MRD.
+
+Testing done:
+- Links/Trigger property is returned by GET request on
+ /redfish/v1/TelemetryService/MetricReportDefinitions/<str>/
+
+Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
+Change-Id: I5accf4b50324437b0b185003200078ad2c7020b0
+---
+ redfish-core/lib/metric_report_definition.hpp | 46 +++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index e6b08a1..9ee6013 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -88,6 +88,31 @@ inline bool verifyCommonErrors(crow::Response& res, const std::string& id,
+ return true;
+ }
+
++inline std::optional<nlohmann::json>
++ getLinkedTriggers(const std::vector<std::string>& triggerIds)
++{
++ nlohmann::json triggers = nlohmann::json::array();
++
++ for (const std::string& id : triggerIds)
++ {
++ sdbusplus::message::object_path path(id);
++ if (path.parent_path() != "TelemetryService")
++ {
++ BMCWEB_LOG_ERROR << "Property TriggerIds contains invalid value: "
++ << id;
++ return std::nullopt;
++ }
++ triggers.push_back({
++ {"@odata.id",
++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
++ "Triggers", path.filename())
++ .string()},
++ });
++ }
++
++ return std::make_optional(triggers);
++}
++
+ inline void fillReportDefinition(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
+ const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
+@@ -101,6 +126,7 @@ inline void fillReportDefinition(
+ const uint64_t* appendLimit = nullptr;
+ const uint64_t* interval = nullptr;
+ const bool* enabled = nullptr;
++ const std::vector<std::string>* triggerIds = nullptr;
+
+ for (const auto& [key, var] : properties)
+ {
+@@ -136,6 +162,10 @@ inline void fillReportDefinition(
+ {
+ enabled = std::get_if<bool>(&var);
+ }
++ else if (key == "TriggerIds")
++ {
++ triggerIds = std::get_if<std::vector<std::string>>(&var);
++ }
+ }
+
+ std::vector<std::string> redfishReportActions;
+@@ -156,6 +186,17 @@ inline void fillReportDefinition(
+ }
+ }
+
++ std::optional<nlohmann::json> linkedTriggers;
++ if (triggerIds != nullptr)
++ {
++ linkedTriggers = getLinkedTriggers(*triggerIds);
++ if (!linkedTriggers)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ }
++
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
+ asyncResp->res.jsonValue["@odata.id"] =
+@@ -231,6 +272,11 @@ inline void fillReportDefinition(
+ {"CollectionTimeScope", collectionTimeScope}});
+ }
+ }
++
++ if (triggerIds != nullptr)
++ {
++ asyncResp->res.jsonValue["Links"]["Triggers"] = *linkedTriggers;
++ }
+ }
+
+ struct MetricArgs
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
new file mode 100644
index 000000000..224790df8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
@@ -0,0 +1,24 @@
+These patches are mirror of upstream TelemetryService implementation.
+Until change is integrated they will be manually merged here to enable feature in Intel builds.
+
+Current revisions:
+- LogService field, actual implementation will be upstreamed with triggers feature
+ file://telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch
+
+- ref: use url_view for telemetry uris
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/51650/7
+
+- Fix Trigger GET functionality
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/51521/9
+
+- Add support for POST on TriggersCollection
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/44935/27
+
+- Switched bmcweb to use new telemetry service API
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/44270/39
+
+- Add PUT and PATCH for MetricReportDefinition
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/49796/13
+
+- Add Links/Triggers to MetricReportDefinition
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/51723/1
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch
new file mode 100644
index 000000000..9225d20f1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch
@@ -0,0 +1,61 @@
+From e614dec3e007d3ceaa697fd7bb264dbc1ef496e5 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Wed, 1 Dec 2021 12:25:07 +0100
+Subject: [PATCH] Revert "Disable nbd proxy from the build"
+
+NBD Proxy has been disabled upstream. Reenable as we use it for Virtual
+Media
+
+This reverts commit efb8062c306474942bc94f15d748b2eb0b58fbb6.
+
+Change-Id: I19a88b30c1074dd376f2df8f5668245b638b881f
+---
+ meson.build | 3 ++-
+ meson_options.txt | 10 ++--------
+ 2 files changed, 4 insertions(+), 9 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index c9066d4..51c7f9d 100644
+--- a/meson.build
++++ b/meson.build
+@@ -86,7 +86,8 @@ feature_map = {
+ 'static-hosting' : '-DBMCWEB_ENABLE_STATIC_HOSTING',
+ 'vm-websocket' : '-DBMCWEB_ENABLE_VM_WEBSOCKET',
+ 'xtoken-auth' : '-DBMCWEB_ENABLE_XTOKEN_AUTHENTICATION',
+- #'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY',
++ 'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY',
++ 'validate-unsecure-feature' : '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE',
+ }
+
+ # Get the options status and build a project summary to show which flags are
+diff --git a/meson_options.txt b/meson_options.txt
+index 4661658..435f382 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -2,14 +2,7 @@ option('yocto-deps', type: 'feature', value: 'disabled', description : 'Use YOCT
+ option('kvm', type : 'feature',value : 'enabled', description : 'Enable the KVM host video WebSocket. Path is \'/kvm/0\'. Video is from the BMC\'s \'/dev/video\' device.')
+ option ('tests', type : 'feature', value : 'enabled', description : 'Enable Unit tests for bmcweb')
+ option('vm-websocket', type : 'feature', value : 'enabled', description : '''Enable the Virtual Media WebSocket. Path is \'/vm/0/0\'to open the websocket. See https://github.com/openbmc/jsnbd/blob/master/README.''')
+-
+-# if you use this option and are seeing this comment, please comment here:
+-# https://github.com/openbmc/bmcweb/issues/188 and put forward your intentions
+-# for this code. At this point, no daemon has been upstreamed that implements
+-# this interface, so for the moment this appears to be dead code; In leiu of
+-# removing it, it has been disabled to try to give those that use it the
+-# opportunity to upstream their backend implementation
+-#option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.')
++option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.')
+ option('rest', type : 'feature', value : 'disabled', description : '''Enable Phosphor REST (D-Bus) APIs. Paths directly map Phosphor D-Bus object paths, for example, \'/xyz/openbmc_project/logging/entry/enumerate\'. See https://github.com/openbmc/docs/blob/master/rest-api.md.''')
+ option('redfish', type : 'feature',value : 'enabled', description: 'Enable Redfish APIs. Paths are under \'/redfish/v1/\'. See https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#redfish.')
+ option('host-serial-socket', type : 'feature', value : 'enabled', description : 'Enable host serial console WebSocket. Path is \'/console0\'. See https://github.com/openbmc/docs/blob/master/console.md.')
+@@ -38,6 +31,7 @@ option ('https_port', type : 'integer', min : 1, max : 65535, value : 443, descr
+ # the implications of doing so.In general, enabling these options will cause security
+ # problems of varying degrees
+
++option ('validate-unsecure-feature', type : 'feature', value : 'disabled', description : '''Enables unsecure features required by validation. Note: mustbe turned off for production images.''')
+ option ('insecure-disable-csrf', type : 'feature', value : 'disabled', description : 'Disable CSRF prevention checks.Should be set to false for production systems.')
+ option ('insecure-disable-ssl', type : 'feature', value : 'disabled', description : 'Disable SSL ports. Should be set to false for production systems.')
+ option ('insecure-disable-auth', type : 'feature', value : 'disabled', description : 'Disable authentication on all ports. Should be set to false for production systems')
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch
new file mode 100644
index 000000000..e364fd887
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch
@@ -0,0 +1,215 @@
+From d951041df370dd4c7068b3883d5cc8ef39d044da Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Fri, 23 Jul 2021 12:07:02 +0000
+Subject: [PATCH] bmcweb handle device or resource busy exception
+
+Use async_method_call_timed() for mount/unmount dbus oprations.
+Long mount/unmount times are supported by VirtualMedia service,
+this works because of settable timeout property, available for each block
+device.
+Default dbus calls will timeout when mount/unmount timeout is long enough.
+
+Get mount/unmount timeout property and use it for mount/unmount calls.
+Add handling of device or resource busy exception (EBUSY) that
+can be thrown by VirtualMedia service during Mount/Unmount dbus operations.
+
+Tested: Verified that after mounting non-existing HTTPS resource
+ in proxy mode, VirtualMedia recovers restoring ready state
+ and returns EBUSY during that transition.
+ Verfied that resources can be mounted/unmounted in both legacy
+ and proxy mode.
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+Change-Id: Ica62c34db0cce24c4c6169fc661edfde49e948d0
+---
+ redfish-core/lib/virtual_media.hpp | 143 +++++++++++++++++++++--------
+ 1 file changed, 106 insertions(+), 37 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index b0e3b38..779e948 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -26,6 +26,8 @@
+ #include <boost/url/url_view.hpp>
+ #include <registries/privilege_registry.hpp>
+
++#include <chrono>
++
+ namespace redfish
+ {
+ /**
+@@ -143,6 +145,26 @@ inline void
+ }
+ }
+
++/**
++ * @brief parses Timeout property and converts to microseconds
++ */
++static std::optional<uint64_t>
++ vmParseTimeoutProperty(const std::variant<int>& timeoutProperty)
++{
++ const int* timeoutValue = std::get_if<int>(&timeoutProperty);
++ if (timeoutValue)
++ {
++ constexpr int timeoutMarginSeconds = 10;
++ return std::chrono::duration_cast<std::chrono::microseconds>(
++ std::chrono::seconds(*timeoutValue + timeoutMarginSeconds))
++ .count();
++ }
++ else
++ {
++ return std::nullopt;
++ }
++}
++
+ /**
+ * @brief Fill template for Virtual Media Item.
+ */
+@@ -702,22 +724,58 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ }
+
+ crow::connections::systemBus->async_method_call(
+- [asyncResp, secretPipe](const boost::system::error_code ec,
+- bool success) {
++ [asyncResp, service, name, imageUrl, rw, unixFd,
++ secretPipe](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+ messages::internalError(asyncResp->res);
++ return;
+ }
+- else if (!success)
++
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
+ {
+- BMCWEB_LOG_ERROR << "Service responded with error";
+- messages::generalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
+ }
++
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp, secretPipe](const boost::system::error_code ec,
++ bool success) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ if (ec == boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else if (ec == boost::system::errc::permission_denied)
++ {
++ messages::accessDenied(asyncResp->res,
++ crow::utility::urlFromPieces(
++ "VirtualMedia.Insert"));
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ }
++ else if (!success)
++ {
++ BMCWEB_LOG_ERROR << "Service responded with error ";
++ messages::generalError(asyncResp->res);
++ }
++ },
++ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
++ "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", *timeout,
++ imageUrl, rw, unixFd);
+ },
+ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
+- unixFd);
++ "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+
+ /**
+@@ -729,38 +787,49 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& service, const std::string& name,
+ bool legacy)
+ {
++ const std::string vmMode = legacy ? "Legacy" : "Proxy";
++ const std::string objectPath =
++ "/xyz/openbmc_project/VirtualMedia/" + vmMode + "/" + name;
++ const std::string ifaceName = "xyz.openbmc_project.VirtualMedia." + vmMode;
+
+- // Legacy mount requires parameter with image
+- if (legacy)
+- {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+-
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
+- }
+- else // proxy
+- {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, service, name, objectPath,
++ ifaceName](const boost::system::error_code ec,
++ const std::variant<int> timeoutProperty) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
+
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
+- }
++ auto timeout = vmParseTimeoutProperty(timeoutProperty);
++ if (timeout == std::nullopt)
++ {
++ BMCWEB_LOG_ERROR << "Timeout property is empty.";
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ crow::connections::systemBus->async_method_call_timed(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
++ if (ec == boost::system::errc::device_or_resource_busy)
++ {
++ messages::resourceInUse(asyncResp->res);
++ }
++ else
++ {
++ messages::internalError(asyncResp->res);
++ }
++ return;
++ }
++ },
++ service, objectPath, ifaceName, "Unmount", *timeout);
++ },
++ service, objectPath, "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.VirtualMedia.MountPoint", "Timeout");
+ }
+
+ struct InsertMediaActionParams
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
new file mode 100644
index 000000000..c8af3a659
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
@@ -0,0 +1,28 @@
+From 1abf9a1d336eed835472fe933210d3be7ad5ba7a Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Thu, 11 Feb 2021 08:35:41 +0000
+Subject: [PATCH] Add ConnectedVia property to virtual media item template
+
+Tested: Verified that ConnectedVia property is returned and set to
+ "NotConnected" for disconnected media.
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 57c2bd2..de1cc94 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -200,6 +200,7 @@ inline nlohmann::json vmItemTemplate(const std::string& name,
+ item["@odata.id"] = std::move(id);
+
+ item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
++ item["ConnectedVia"] = "NotConnected";
+ item["Name"] = "Virtual Removable Media";
+ item["Id"] = resName;
+ item["WriteProtected"] = true;
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch
new file mode 100644
index 000000000..e3684f406
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch
@@ -0,0 +1,175 @@
+From b75728a55016289d7a0eabaaee453051e4d29742 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Wed, 1 Dec 2021 12:27:22 +0100
+Subject: [PATCH] Invalid status code from InsertMedia REST methods GET, PUT,
+ DELETE, PATCH in proxy mode
+
+Add handlers for GET, PUT, DELETE, PATCH method and function that
+checks which mode is used and set suitable status code:
+Not allowed for Legacy and Not found for Proxy.
+
+Change-Id: Ib4c0a3e9a2a8853caa74c59239d9fcfed99c5e8b
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 137 +++++++++++++++++++++++++++++
+ 1 file changed, 137 insertions(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index dc829b3..5df7b5e 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -30,6 +30,117 @@
+
+ namespace redfish
+ {
++
++/**
++ * @brief Function checks if insert media request is Legacy or Proxy type
++ * and sets suitable response code for unsupported REST method.
++ *
++ */
++void checkProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const crow::Request& req, const std::string& name,
++ const std::string& resName)
++{
++ if (name != "bmc")
++ {
++ messages::resourceNotFound(aResp->res, "VirtualMedia.Insert", resName);
++
++ return;
++ }
++
++ crow::connections::systemBus->async_method_call(
++ [aResp, req, resName](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
++ << ec;
++ aResp->res.result(boost::beast::http::status::not_found);
++
++ return;
++ }
++
++ if (getObjectType.size() == 0)
++ {
++ BMCWEB_LOG_ERROR << "ObjectMapper : No Service found";
++ aResp->res.result(boost::beast::http::status::not_found);
++ return;
++ }
++
++ std::string service = getObjectType.begin()->first;
++ BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
++
++ crow::connections::systemBus->async_method_call(
++ [service, resName, req,
++ aResp](const boost::system::error_code ec,
++ dbus::utility::ManagedObjectType& subtree) {
++ if (ec)
++ {
++ BMCWEB_LOG_DEBUG << "DBUS response error";
++
++ return;
++ }
++
++ for (auto& item : subtree)
++ {
++ std::string thispath = item.first.filename();
++ if (thispath.empty())
++ {
++ continue;
++ }
++
++ if (thispath != resName)
++ {
++ continue;
++ }
++
++ auto mode = item.first.parent_path();
++ auto type = mode.parent_path();
++ if (mode.filename().empty() || type.filename().empty())
++ {
++ continue;
++ }
++
++ if (type.filename() != "VirtualMedia")
++ {
++ continue;
++ }
++
++ // Check if dbus path is Legacy type
++ if (mode.filename() == "Legacy")
++ {
++ BMCWEB_LOG_DEBUG << "InsertMedia only allowed "
++ "with POST method "
++ "in legacy mode";
++ aResp->res.result(
++ boost::beast::http::status::method_not_allowed);
++
++ return;
++ }
++ // Check if dbus path is Proxy type
++ if (mode.filename() == "Proxy")
++ {
++ // Not possible in proxy mode
++ BMCWEB_LOG_DEBUG << "InsertMedia not "
++ "allowed in proxy mode";
++ aResp->res.result(
++ boost::beast::http::status::not_found);
++
++ return;
++ }
++ }
++
++ BMCWEB_LOG_DEBUG << "Parent item not found";
++ aResp->res.result(boost::beast::http::status::not_found);
++ },
++ service, "/xyz/openbmc_project/VirtualMedia",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetObject",
++ "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
++}
++
+ /**
+ * @brief Function extracts transfer protocol name from URI.
+ */
+@@ -840,6 +951,32 @@ struct InsertMediaActionParams
+
+ inline void requestNBDVirtualMediaRoutes(App& app)
+ {
++ BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
++ "VirtualMedia.InsertMedia")
++ .privileges({{"Login"}})
++ .methods(boost::beast::http::verb::get)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& name, const std::string& resName) {
++ checkProxyMode(asyncResp, req, name, resName);
++ });
++
++ for (auto method :
++ {boost::beast::http::verb::patch, boost::beast::http::verb::put,
++ boost::beast::http::verb::delete_})
++ {
++ BMCWEB_ROUTE(app,
++ "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
++ "VirtualMedia.InsertMedia")
++ .privileges({{"ConfigureManager"}})
++ .methods(method)(
++ [](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& name, const std::string& resName) {
++ checkProxyMode(asyncResp, req, name, resName);
++ });
++ }
++
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch
new file mode 100644
index 000000000..a74b6e9fb
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0006-Bmcweb-handle-permission-denied-exception.patch
@@ -0,0 +1,38 @@
+From 232c7dbf21570aa0581d7c8cff71b793555f10cf Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Thu, 1 Jul 2021 10:41:47 +0000
+Subject: [PATCH] Bmcweb handle permission denied exception
+
+Add handling of permission denied exception (EPERM) that
+can be thrown by VirtualMedia service during Mount/Unmount dbus operations.
+
+Tested:
+Verified that after mounting/unmounting HTTPS resource twice in a row in legacy mode,
+VirtualMedia returns EPERM, which bmcweb handles as 403 status code.
+
+Change-Id: Ibc18d5ec822c5072605b1fc4651389982002798b
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 3eb585f..1168939 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -931,6 +931,12 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ {
+ messages::resourceInUse(asyncResp->res);
+ }
++ else if (ec == boost::system::errc::permission_denied)
++ {
++ messages::accessDenied(asyncResp->res,
++ crow::utility::urlFromPieces(
++ "VirtualMedia.Insert"));
++ }
+ else
+ {
+ messages::internalError(asyncResp->res);
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch
new file mode 100644
index 000000000..88fa89465
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0007-Fix-unmounting-image-in-proxy-mode.patch
@@ -0,0 +1,35 @@
+From 6f4b5fc1879f39b0f5fee0838f0ecbc481275d5e Mon Sep 17 00:00:00 2001
+From: Alicja Rybak <alicja.rybak@intel.com>
+Date: Fri, 23 Apr 2021 17:35:52 +0200
+Subject: [PATCH] Fix unmounting image in proxy mode.
+
+Sometimes Slot0 got higher key than Slot1 and erase function for Slot1
+invalidates elements with keys not less than the erased element.
+In that case invalid slot0 will be unmounted.
+Change order of calling close() and erase() functions to
+unmount correct device.
+
+Change-Id: I7a40a4518982f697d3eed635cde6d06978149cf0
+Signed-off-by: Alicja Rybak <alicja.rybak@intel.com>
+---
+ include/nbd_proxy.hpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp
+index 3b28823..897bcf2 100644
+--- a/include/nbd_proxy.hpp
++++ b/include/nbd_proxy.hpp
+@@ -439,9 +439,9 @@ inline void requestRoutes(App& app)
+ BMCWEB_LOG_DEBUG << "No session to close";
+ return;
+ }
++ session->second->close();
+ // Remove reference to session in global map
+ sessions.erase(session);
+- session->second->close();
+ })
+ .onmessage([](crow::websocket::Connection& conn,
+ const std::string& data, bool) {
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch
new file mode 100644
index 000000000..f05629776
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch
@@ -0,0 +1,369 @@
+From 5512b7bcdb691133d0321779f7d20f5cc1eb3188 Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Mon, 7 Mar 2022 19:22:18 +0100
+Subject: [PATCH] Apply async dbus API
+
+In order to support new asynchronous dbus API for Virtual Media generic
+listener for signals has been introduced.
+Using AsynsResp object lifetime expanded by listener waiting for signal
+response is passed to user once signal arrives.
+This patch covers mounting legacy mode and unmounting both legacy and
+proxy mode.
+
+Tested:
+Manually in companion with updated service signal is catched and redfish
+releases connection with appropriate reply
+
+Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 203 ++++++++++++++++++++++++-----
+ 1 file changed, 174 insertions(+), 29 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 8da40eea..e6ee2b6a 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -15,10 +15,18 @@
+ */
+ #pragma once
+
++#include "logging.hpp"
++
+ #include <app.hpp>
++#include <async_resp.hpp>
++#include <boost/asio/error.hpp>
++#include <boost/asio/io_context.hpp>
++#include <boost/asio/steady_timer.hpp>
+ #include <boost/container/flat_map.hpp>
+ #include <boost/process/async_pipe.hpp>
+ #include <boost/type_traits/has_dereference.hpp>
++#include <dbus_singleton.hpp>
++#include <sdbusplus/bus/match.hpp>
+ #include <sdbusplus/message/native_types.hpp>
+ #include <utils/json_utils.hpp>
+ // for GetObjectType and ManagedObjectType
+@@ -28,10 +36,24 @@
+ #include <registries/privilege_registry.hpp>
+
+ #include <chrono>
++#include <memory>
++#include <optional>
+
+ namespace redfish
+ {
+
++const char* legacyMode = "Legacy";
++const char* proxyMode = "Proxy";
++
++std::string getModeName(bool isLegacy)
++{
++ if (isLegacy)
++ {
++ return legacyMode;
++ }
++ return proxyMode;
++}
++
+ /**
+ * @brief Function parses getManagedObject response, finds item, makes generic
+ * validation and invokes callback handler on this item.
+@@ -109,6 +131,7 @@ void findItemAndRunHandler(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+
+ if (handler(service, aResp, item) == true)
+ {
++
+ return;
+ }
+ }
+@@ -382,7 +405,7 @@ inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ actionsId += "/Actions";
+
+ // Check if dbus path is Legacy type
+- if (mode.filename() == "Legacy")
++ if (mode.filename() == legacyMode)
+ {
+ aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
+ ["target"] =
+@@ -760,6 +783,111 @@ class Pipe
+ Buffer buffer;
+ };
+
++/**
++ * @brief holder for dbus signal matchers
++ */
++struct MatchWrapper
++{
++ void stop()
++ {
++ timer->cancel();
++ matcher = std::nullopt;
++ }
++
++ ~MatchWrapper()
++ {
++ BMCWEB_LOG_DEBUG << "~MatchWrapper()";
++ };
++ std::optional<sdbusplus::bus::match::match> matcher{};
++ std::optional<boost::asio::steady_timer> timer;
++};
++
++/**
++ * @brief Function starts waiting for signal completion
++ */
++static inline std::shared_ptr<MatchWrapper>
++ doListenForCompletion(const std::string& name,
++ const std::string& objectPath,
++ const std::string& action, bool legacy,
++ std::shared_ptr<bmcweb::AsyncResp> asyncResp)
++{
++ BMCWEB_LOG_DEBUG << "Start Listening for completion : " << action;
++ std::string matcherString = sdbusplus::bus::match::rules::type::signal();
++
++ std::string interface =
++ std::string("xyz.openbmc_project.VirtualMedia.") + getModeName(legacy);
++
++ matcherString += sdbusplus::bus::match::rules::interface(interface);
++ matcherString += sdbusplus::bus::match::rules::member("Completion");
++ matcherString += sdbusplus::bus::match::rules::sender(
++ "xyz.openbmc_project.VirtualMedia");
++ matcherString += sdbusplus::bus::match::rules::path(objectPath);
++
++ auto matchWrapper = std::make_shared<MatchWrapper>();
++ auto matchHandler = [asyncResp = std::move(asyncResp), name, action,
++ objectPath,
++ matchWrapper](sdbusplus::message::message& m) {
++ int errorCode;
++ try
++ {
++ BMCWEB_LOG_INFO << "Completion signal from " << m.get_path()
++ << " has been received";
++
++ m.read(errorCode);
++ switch (errorCode)
++ {
++ case 0: // success
++ BMCWEB_LOG_INFO << "Signal received: Success";
++ messages::success(asyncResp->res);
++ break;
++ case EPERM:
++ BMCWEB_LOG_ERROR << "Signal received: EPERM";
++ messages::accessDenied(
++ asyncResp->res, crow::utility::urlFromPieces(action));
++ break;
++ case EBUSY:
++ BMCWEB_LOG_ERROR << "Signal received: EAGAIN";
++ messages::resourceInUse(asyncResp->res);
++ break;
++ default:
++ BMCWEB_LOG_ERROR << "Signal received: Other: " << errorCode;
++ messages::operationFailed(asyncResp->res);
++ break;
++ };
++ }
++ catch (sdbusplus::exception::SdBusError& e)
++ {
++ BMCWEB_LOG_ERROR << e.what();
++ };
++ // postpone matcher deletion after callback finishes
++ boost::asio::post(crow::connections::systemBus->get_io_context(),
++ [name, matchWrapper = matchWrapper]()
++
++ {
++ BMCWEB_LOG_DEBUG << "Removing matcher for "
++ << name << " node.";
++ matchWrapper->stop();
++ });
++ };
++ matchWrapper->timer.emplace(crow::connections::systemBus->get_io_context());
++
++ // Safety valve. Clean itself after 3 minutes without signal
++ matchWrapper->timer->expires_after(std::chrono::minutes(3));
++ matchWrapper->timer->async_wait(
++ [matchWrapper](const boost::system::error_code& ec) {
++ if (ec != boost::asio::error::operation_aborted)
++ {
++ BMCWEB_LOG_DEBUG << "Timer expired! Signal did not come";
++ matchWrapper->matcher = std::nullopt;
++ return;
++ }
++ });
++
++ matchWrapper->matcher.emplace(*crow::connections::systemBus, matcherString,
++ matchHandler);
++ return matchWrapper;
++}
++
+ /**
+ * @brief Function transceives data with dbus directly.
+ *
+@@ -835,9 +963,15 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ return;
+ }
+
++ const std::string objectPath =
++ "/xyz/openbmc_project/VirtualMedia/Legacy/" + name;
++ const std::string action = "VirtualMedia.Insert";
++ auto wrapper = doListenForCompletion(name, objectPath, action, true,
++ asyncResp);
++
+ crow::connections::systemBus->async_method_call_timed(
+- [asyncResp, secretPipe](const boost::system::error_code ec,
+- bool success) {
++ [asyncResp, secretPipe, name, action, wrapper,
++ objectPath](const boost::system::error_code ec, bool success) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+@@ -847,24 +981,25 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ }
+ else if (ec == boost::system::errc::permission_denied)
+ {
+- messages::accessDenied(asyncResp->res,
+- crow::utility::urlFromPieces(
+- "VirtualMedia.Insert"));
++ messages::accessDenied(
++ asyncResp->res,
++ crow::utility::urlFromPieces(action));
+ }
+ else
+ {
+ messages::internalError(asyncResp->res);
+ }
++ wrapper->stop();
+ }
+ else if (!success)
+ {
+ BMCWEB_LOG_ERROR << "Service responded with error ";
+- messages::generalError(asyncResp->res);
++ messages::operationFailed(asyncResp->res);
++ wrapper->stop();
+ }
+ },
+- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", *timeout,
+- imageUrl, rw, unixFd);
++ service, objectPath, "xyz.openbmc_project.VirtualMedia.Legacy",
++ "Mount", *timeout, imageUrl, rw, unixFd);
+ },
+ service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
+ "org.freedesktop.DBus.Properties", "Get",
+@@ -876,17 +1011,17 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ *
+ * All BMC state properties will be retrieved before sending reset request.
+ */
+-inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::string& service, const std::string& name,
+- bool legacy)
++inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& service, const std::string& name,
++ bool legacy)
+ {
+- const std::string vmMode = legacy ? "Legacy" : "Proxy";
++ const std::string vmMode = getModeName(legacy);
+ const std::string objectPath =
+ "/xyz/openbmc_project/VirtualMedia/" + vmMode + "/" + name;
+ const std::string ifaceName = "xyz.openbmc_project.VirtualMedia." + vmMode;
+
+ crow::connections::systemBus->async_method_call(
+- [asyncResp, service, name, objectPath,
++ [asyncResp, service, name, objectPath, legacy,
+ ifaceName](const boost::system::error_code ec,
+ const std::variant<int> timeoutProperty) {
+ if (ec)
+@@ -903,8 +1038,12 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ messages::internalError(asyncResp->res);
+ return;
+ }
++ std::string action = "VirtualMedia.Eject";
++ auto wrapper = doListenForCompletion(name, objectPath, action,
++ legacy, asyncResp);
+ crow::connections::systemBus->async_method_call_timed(
+- [asyncResp](const boost::system::error_code ec) {
++ [asyncResp, name, action, objectPath,
++ wrapper](const boost::system::error_code ec, bool success) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
+@@ -914,15 +1053,20 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ }
+ else if (ec == boost::system::errc::permission_denied)
+ {
+- messages::accessDenied(asyncResp->res,
+- crow::utility::urlFromPieces(
+- "VirtualMedia.Insert"));
++ messages::accessDenied(
++ asyncResp->res,
++ crow::utility::urlFromPieces(action));
+ }
+ else
+ {
+ messages::internalError(asyncResp->res);
+ }
+- return;
++ wrapper->stop();
++ }
++ else if (!success)
++ {
++ messages::operationFailed(asyncResp->res);
++ wrapper->stop();
+ }
+ },
+ service, objectPath, ifaceName, "Unmount", *timeout);
+@@ -951,7 +1095,7 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+ auto mode = item.first.parent_path();
+ auto type = mode.parent_path();
+ // Check if dbus path is Legacy type
+- if (mode.filename() == "Legacy")
++ if (mode.filename() == legacyMode)
+ {
+ BMCWEB_LOG_DEBUG << "InsertMedia only allowed "
+ "with POST method "
+@@ -961,7 +1105,7 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+ return true;
+ }
+ // Check if dbus path is Proxy type
+- if (mode.filename() == "Proxy")
++ if (mode.filename() == proxyMode)
+ {
+ // Not possible in proxy mode
+ BMCWEB_LOG_DEBUG << "InsertMedia not "
+@@ -1019,7 +1163,7 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+ dbus::utility::DBusInteracesMap>& item) {
+ auto mode = item.first.parent_path();
+ auto type = mode.parent_path();
+- if (mode.filename() == "Proxy")
++ if (mode.filename() == proxyMode)
+ {
+ // Not possible in proxy mode
+ BMCWEB_LOG_DEBUG << "InsertMedia not "
+@@ -1131,20 +1275,21 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+
+ if (path.substr(lastIndex) == resName)
+ {
+- lastIndex = path.rfind("Proxy");
++ lastIndex = path.rfind(proxyMode);
+ if (lastIndex != std::string::npos)
+ {
+ // Proxy mode
+- doVmAction(asyncResp, service,
+- resName, false);
++ doEjectAction(asyncResp, service,
++ resName, false);
+ }
+
+- lastIndex = path.rfind("Legacy");
++ lastIndex = path.rfind(legacyMode);
++
+ if (lastIndex != std::string::npos)
+ {
+ // Legacy mode
+- doVmAction(asyncResp, service,
+- resName, true);
++ doEjectAction(asyncResp, service,
++ resName, true);
+ }
+
+ return;
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Return-404-for-POST-on-Proxy-InsertMedia.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Return-404-for-POST-on-Proxy-InsertMedia.patch
new file mode 100644
index 000000000..a6fef8b3f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Return-404-for-POST-on-Proxy-InsertMedia.patch
@@ -0,0 +1,380 @@
+From d80cd6b54604f688832674b4fc2f3cb715350266 Mon Sep 17 00:00:00 2001
+From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+Date: Fri, 4 Feb 2022 15:41:54 +0100
+Subject: [PATCH] Return 404 for POST on Proxy InsertMedia
+
+.../Actions/VirtualMedia.InsertMedia does not exist for proxy mode. But
+it does for legacy, so handler needs to return 404 for proxy and run
+mounting in legacy. Similar action has to be done for get, delete and
+patch.
+
+As the check shares the same codebase besides the action itself, this
+patch creates generic function for parsing GetManagedObjects, finding
+valid VirtualMedia endpoint and invoking specific action handler.
+
+Tested:
+Manually, with the following set of commands:
+POST /Proxy /InsertAction : returned 404
+POST /Legacy/InsertAction : returned 200 (mounted)
+GET /Proxy /InsertAction : returned 404
+GET /Legacy/InsertAction : returned 405
+PATCH /Proxy /InsertAction : returned 404
+PATCH /Legacy/InsertAction : returned 405
+DELETE/Proxy /InsertAction : returned 404
+DELETE/Legacy/InsertAction : returned 405
+
+Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 266 ++++++++++++-----------------
+ 1 file changed, 108 insertions(+), 158 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 045ca10..9b1898c 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -19,6 +19,7 @@
+ #include <boost/container/flat_map.hpp>
+ #include <boost/process/async_pipe.hpp>
+ #include <boost/type_traits/has_dereference.hpp>
++#include <sdbusplus/message/native_types.hpp>
+ #include <utils/json_utils.hpp>
+ // for GetObjectType and ManagedObjectType
+
+@@ -32,13 +33,14 @@ namespace redfish
+ {
+
+ /**
+- * @brief Function checks if insert media request is Legacy or Proxy type
+- * and sets suitable response code for unsupported REST method.
++ * @brief Function parses getManagedObject response, finds item, makes generic
++ * validation and invokes callback handler on this item.
+ *
+ */
+-void checkProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+- const crow::Request& req, const std::string& name,
+- const std::string& resName)
++template <typename T>
++void findItemAndRunHandler(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ const crow::Request& req, const std::string& name,
++ const std::string& resName, T&& handler)
+ {
+ if (name != "bmc")
+ {
+@@ -48,8 +50,8 @@ void checkProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ }
+
+ crow::connections::systemBus->async_method_call(
+- [aResp, req, resName](const boost::system::error_code ec,
+- const GetObjectType& getObjectType) {
++ [aResp, req, resName, handler](const boost::system::error_code ec,
++ const GetObjectType& getObjectType) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
+@@ -70,9 +72,9 @@ void checkProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
+
+ crow::connections::systemBus->async_method_call(
+- [service, resName, req,
+- aResp](const boost::system::error_code ec,
+- dbus::utility::ManagedObjectType& subtree) {
++ [service, resName, req, aResp,
++ handler](const boost::system::error_code ec,
++ dbus::utility::ManagedObjectType& subtree) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG << "DBUS response error";
+@@ -105,26 +107,8 @@ void checkProxyMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ continue;
+ }
+
+- // Check if dbus path is Legacy type
+- if (mode.filename() == "Legacy")
+- {
+- BMCWEB_LOG_DEBUG << "InsertMedia only allowed "
+- "with POST method "
+- "in legacy mode";
+- aResp->res.result(
+- boost::beast::http::status::method_not_allowed);
+-
+- return;
+- }
+- // Check if dbus path is Proxy type
+- if (mode.filename() == "Proxy")
++ if (handler(service, aResp, item) == true)
+ {
+- // Not possible in proxy mode
+- BMCWEB_LOG_DEBUG << "InsertMedia not "
+- "allowed in proxy mode";
+- aResp->res.result(
+- boost::beast::http::status::not_found);
+-
+ return;
+ }
+ }
+@@ -270,10 +254,7 @@ static std::optional<uint64_t>
+ std::chrono::seconds(*timeoutValue + timeoutMarginSeconds))
+ .count();
+ }
+- else
+- {
+- return std::nullopt;
+- }
++ return std::nullopt;
+ }
+
+ /**
+@@ -961,14 +942,43 @@ struct InsertMediaActionParams
+
+ inline void requestNBDVirtualMediaRoutes(App& app)
+ {
++ auto handler = []([[maybe_unused]] const std::string& service,
++ const std::shared_ptr<bmcweb::AsyncResp>& aResp,
++ std::pair<sdbusplus::message::object_path,
++ dbus::utility::DBusInteracesMap>& item) {
++ auto mode = item.first.parent_path();
++ auto type = mode.parent_path();
++ // Check if dbus path is Legacy type
++ if (mode.filename() == "Legacy")
++ {
++ BMCWEB_LOG_DEBUG << "InsertMedia only allowed "
++ "with POST method "
++ "in legacy mode";
++ aResp->res.result(boost::beast::http::status::method_not_allowed);
++
++ return true;
++ }
++ // Check if dbus path is Proxy type
++ if (mode.filename() == "Proxy")
++ {
++ // Not possible in proxy mode
++ BMCWEB_LOG_DEBUG << "InsertMedia not "
++ "allowed in proxy mode";
++ aResp->res.result(boost::beast::http::status::not_found);
++
++ return true;
++ }
++ return false;
++ };
+ BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
+ "VirtualMedia.InsertMedia")
+ .privileges({{"Login"}})
+ .methods(boost::beast::http::verb::get)(
+- [](const crow::Request& req,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::string& name, const std::string& resName) {
+- checkProxyMode(asyncResp, req, name, resName);
++ [handler](const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& name, const std::string& resName) {
++ findItemAndRunHandler(asyncResp, req, name, resName,
++ std::move(handler));
+ });
+
+ for (auto method :
+@@ -980,10 +990,12 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+ "VirtualMedia.InsertMedia")
+ .privileges({{"ConfigureManager"}})
+ .methods(method)(
+- [](const crow::Request& req,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::string& name, const std::string& resName) {
+- checkProxyMode(asyncResp, req, name, resName);
++ [handler = std::move(handler)](
++ const crow::Request& req,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& name, const std::string& resName) {
++ findItemAndRunHandler(asyncResp, req, name, resName,
++ std::move(handler));
+ });
+ }
+
+@@ -995,130 +1007,68 @@ inline void requestNBDVirtualMediaRoutes(App& app)
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& name, const std::string& resName) {
+- if (name != "bmc")
+- {
+- messages::resourceNotFound(asyncResp->res,
+- "VirtualMedia.Insert", resName);
+-
+- return;
+- }
+- InsertMediaActionParams actionParams;
+-
+- // Read obligatory parameters (url of
+- // image)
+- if (!json_util::readJsonAction(
+- req, asyncResp->res, "Image", actionParams.imageUrl,
+- "WriteProtected", actionParams.writeProtected,
+- "UserName", actionParams.userName, "Password",
+- actionParams.password, "Inserted",
+- actionParams.inserted, "TransferMethod",
+- actionParams.transferMethod, "TransferProtocolType",
+- actionParams.transferProtocolType))
+- {
+- BMCWEB_LOG_DEBUG << "Image is not provided";
+- return;
+- }
+-
+- bool paramsValid = validateParams(
+- asyncResp->res, actionParams.imageUrl,
+- actionParams.inserted, actionParams.transferMethod,
+- actionParams.transferProtocolType);
+-
+- if (!paramsValid)
+- {
+- return;
+- }
+-
+- crow::connections::systemBus->async_method_call(
+- [asyncResp, actionParams,
+- resName](const boost::system::error_code ec,
+- const GetObjectType& getObjectType) mutable {
+- if (ec)
++ // handle legacy mode (parse parameters and start action
++ // for proxy mode return 404.
++ auto handler =
++ [req, resName](
++ const std::string& service,
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ std::pair<sdbusplus::message::object_path,
++ dbus::utility::DBusInteracesMap>& item) {
++ auto mode = item.first.parent_path();
++ auto type = mode.parent_path();
++ if (mode.filename() == "Proxy")
+ {
+- BMCWEB_LOG_ERROR
+- << "ObjectMapper::GetObject call failed: "
+- << ec;
+- messages::internalError(asyncResp->res);
++ // Not possible in proxy mode
++ BMCWEB_LOG_DEBUG << "InsertMedia not "
++ "allowed in proxy mode";
++ messages::resourceNotFound(
++ asyncResp->res, "VirtualMedia.InsertMedia",
++ resName);
+
+- return;
++ return true;
+ }
+- std::string service = getObjectType.begin()->first;
+- BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
+-
+- crow::connections::systemBus->async_method_call(
+- [service, resName, actionParams,
+- asyncResp](const boost::system::error_code ec,
+- dbus::utility::ManagedObjectType&
+- subtree) mutable {
+- if (ec)
+- {
+- BMCWEB_LOG_DEBUG << "DBUS response error";
+
+- return;
+- }
+-
+- for (const auto& object : subtree)
+- {
+- const std::string& path =
+- static_cast<const std::string&>(
+- object.first);
+-
+- std::size_t lastIndex = path.rfind('/');
+- if (lastIndex == std::string::npos)
+- {
+- continue;
+- }
+-
+- lastIndex += 1;
+-
+- if (path.substr(lastIndex) == resName)
+- {
+- lastIndex = path.rfind("Proxy");
+- if (lastIndex != std::string::npos)
+- {
+- // Not possible in proxy mode
+- BMCWEB_LOG_DEBUG
+- << "InsertMedia not "
+- "allowed in proxy mode";
+- messages::resourceNotFound(
+- asyncResp->res,
+- "VirtualMedia.InsertMedia",
+- resName);
+-
+- return;
+- }
++ InsertMediaActionParams actionParams;
++
++ // Read obligatory parameters (url of
++ // image)
++ if (!json_util::readJsonAction(
++ req, asyncResp->res, "Image",
++ actionParams.imageUrl, "WriteProtected",
++ actionParams.writeProtected, "UserName",
++ actionParams.userName, "Password",
++ actionParams.password, "Inserted",
++ actionParams.inserted, "TransferMethod",
++ actionParams.transferMethod,
++ "TransferProtocolType",
++ actionParams.transferProtocolType))
++ {
++ BMCWEB_LOG_DEBUG << "Image is not provided";
++ return true;
++ }
+
+- lastIndex = path.rfind("Legacy");
+- if (lastIndex == std::string::npos)
+- {
+- continue;
+- }
++ bool paramsValid = validateParams(
++ asyncResp->res, actionParams.imageUrl,
++ actionParams.inserted, actionParams.transferMethod,
++ actionParams.transferProtocolType);
+
+- // manager is irrelevant for
+- // VirtualMedia dbus calls
+- doMountVmLegacy(
+- asyncResp, service, resName,
+- actionParams.imageUrl,
+- !(*actionParams.writeProtected),
+- std::move(*actionParams.userName),
+- std::move(*actionParams.password));
++ if (paramsValid == false)
++ {
++ return true;
++ }
+
+- return;
+- }
+- }
+- BMCWEB_LOG_DEBUG << "Parent item not found";
+- messages::resourceNotFound(
+- asyncResp->res, "VirtualMedia", resName);
+- },
+- service, "/xyz/openbmc_project/VirtualMedia",
+- "org.freedesktop.DBus.ObjectManager",
+- "GetManagedObjects");
+- },
+- "xyz.openbmc_project.ObjectMapper",
+- "/xyz/openbmc_project/object_mapper",
+- "xyz.openbmc_project.ObjectMapper", "GetObject",
+- "/xyz/openbmc_project/VirtualMedia",
+- std::array<const char*, 0>());
++ // manager is irrelevant for
++ // VirtualMedia dbus calls
++ doMountVmLegacy(asyncResp, service, resName,
++ actionParams.imageUrl,
++ !(*actionParams.writeProtected),
++ std::move(*actionParams.userName),
++ std::move(*actionParams.password));
++
++ return true;
++ };
++ findItemAndRunHandler(asyncResp, req, name, resName, handler);
+ });
+
+ BMCWEB_ROUTE(
+--
+2.25.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
new file mode 100644
index 000000000..ff5c514af
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
@@ -0,0 +1,96 @@
+# The URI is required for the autobump script but keep it commented
+# to not override the upstream value
+# SRC_URI = "git://github.com/openbmc/bmcweb.git;branch=master;protocol=https"
+SRCREV = "f7725d79fa9b5fcffcc368008fa029ea1a16c4b9"
+
+DEPENDS += "boost-url"
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
+
+SRC_URI += "file://0001-Firmware-update-configuration-changes.patch \
+ file://0002-Use-chip-id-based-UUID-for-Service-Root.patch \
+ file://0010-managers-add-attributes-for-Manager.CommandShell.patch \
+ file://0011-bmcweb-Add-PhysicalContext-to-Thermal-resources.patch \
+ file://0012-Log-RedFish-event-for-Invalid-login-attempt.patch \
+ file://0013-Add-UART-routing-logic-into-host-console-connection-.patch \
+ file://0014-recommended-fixes-by-crypto-review-team.patch \
+ file://0015-Add-state-sensor-messages-to-the-registry.patch \
+ file://0016-Fix-bmcweb-crashes-if-socket-directory-not-present.patch \
+ file://0017-Add-msg-registry-for-subscription-related-actions.patch \
+ file://0018-bmcweb-Add-BMC-Time-update-log-to-the-registry.patch \
+ file://0019-Add-generic-message-PropertySizeExceeded.patch \
+ file://0020-Redfish-Deny-set-AccountLockDuration-to-zero.patch \
+ file://0023-Add-get-IPMI-session-id-s-to-Redfish.patch \
+ file://0024-Add-count-sensor-type.patch \
+ file://0025-Add-Model-to-ProcessorSummary.patch \
+ file://0026-Revert-Delete-the-copy-constructor-on-the-Request.patch \
+ file://0028-Add-Redfish-schema-files-for-OEM-PECI.patch \
+"
+
+
+# OOB Bios Config:
+SRC_URI += "file://biosconfig/0001-Define-Redfish-interface-Registries-Bios.patch \
+ file://biosconfig/0002-BaseBiosTable-Add-support-for-PATCH-operation.patch \
+ file://biosconfig/0003-Add-support-to-ResetBios-action.patch \
+ file://biosconfig/0004-Add-support-to-ChangePassword-action.patch \
+ file://biosconfig/0005-Fix-remove-bios-user-pwd-change-option-via-Redfish.patch \
+ file://biosconfig/0006-Add-fix-for-broken-feature-Pending-Attributes.patch \
+ file://biosconfig/0007-Add-BiosAttributeRegistry-node-under-Registries.patch \
+"
+
+# Virtual Media: Backend code is not upstreamed so downstream only patches.
+SRC_URI += " \
+ file://vm/0001-Revert-Disable-nbd-proxy-from-the-build.patch \
+ file://vm/0002-bmcweb-handle-device-or-resource-busy-exception.patch \
+ file://vm/0003-Add-ConnectedVia-property-to-virtual-media-item-temp.patch \
+ file://vm/0004-Invalid-status-code-from-InsertMedia-REST-methods.patch \
+ file://vm/0006-Bmcweb-handle-permission-denied-exception.patch \
+ file://vm/0007-Fix-unmounting-image-in-proxy-mode.patch \
+ file://vm/0008-Return-404-for-POST-on-Proxy-InsertMedia.patch \
+ file://vm/0008-Apply-async-dbus-API.patch \
+"
+
+# EventService: Temporary pulled to downstream. See eventservice\README for details
+SRC_URI += "file://eventservice/0001-Add-unmerged-changes-for-http-retry-support.patch \
+ file://eventservice/0002-EventService-https-client-support.patch \
+ file://eventservice/0004-Add-Server-Sent-Events-support.patch \
+ file://eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch \
+ file://eventservice/0006-Add-EventService-SSE-filter-support.patch \
+ file://eventservice/0007-EventService-Log-events-for-subscription-actions.patch \
+ file://eventservice/0008-Add-checks-on-Event-Subscription-input-parameters.patch \
+ file://eventservice/0010-Remove-Terminated-Event-Subscriptions.patch \
+ file://eventservice/0011-Fix-bmcweb-crash-while-deleting-terminated-subscriptions.patch \
+ file://eventservice/0012-Add-support-for-deleting-terminated-subscriptions.patch \
+ file://eventservice/0013-event-service-fix-added-Context-field-to-response.patch \
+"
+
+# Temporary downstream mirror of upstream patches, see telemetry\README for details
+SRC_URI += " file://telemetry/0001-Revert-Remove-LogService-from-TelemetryService.patch \
+ file://telemetry/0002-ref-use-url_view-for-telemetry-uris.patch \
+ file://telemetry/0003-Fix-Trigger-GET-functionality.patch \
+ file://telemetry/0004-Add-support-for-POST-on-TriggersCollection.patch \
+ file://telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch \
+ file://telemetry/0006-Add-PUT-and-PATCH-for-MetricReportDefinition.patch \
+ file://telemetry/0007-Add-Links-Triggers-to-MetricReportDefinition.patch \
+"
+
+# Temporary downstream patch for routing and privilege changes
+SRC_URI += "file://http_routing/0001-Add-asyncResp-support-during-handleUpgrade.patch \
+ file://http_routing/0002-Move-privileges-to-separate-entity.patch \
+ file://http_routing/0003-Add-Support-for-privilege-check-in-handleUpgrade.patch \
+ file://http_routing/0004-Add-Privileges-to-Websockets.patch \
+ file://http_routing/0005-Add-Privileges-to-SseSockets.patch \
+ "
+
+# Enable PFR support
+EXTRA_OEMESON += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '-Dredfish-provisioning-feature=enabled', '', d)}"
+
+# Enable NBD proxy embedded in bmcweb
+EXTRA_OEMESON += " -Dvm-nbdproxy=enabled"
+
+# Disable dependency on external nbd-proxy application
+EXTRA_OEMESON += " -Dvm-websocket=disabled"
+EXTRA_OEMESON += " -Dredfish-host-logger=disabled"
+RDEPENDS:${PN}:remove = " jsnbd"
+
+