summaryrefslogtreecommitdiff
path: root/redfish-core/lib/update_service.hpp
diff options
context:
space:
mode:
authorJohn Edward Broadbent <jebr@google.com>2021-04-09 01:57:16 +0300
committerEd Tanous <ed@tanous.net>2021-06-03 21:18:02 +0300
commit7e860f1550c8686eec42f7a75bc5f2ef51e756ad (patch)
tree989da47a8427bc1a60119f480e2523151b1433aa /redfish-core/lib/update_service.hpp
parenteb75770c6c4369984cb150ded4f5ace410ed24a9 (diff)
downloadbmcweb-7e860f1550c8686eec42f7a75bc5f2ef51e756ad.tar.xz
Remove Redfish Node class
Reduces the total number of lines and will allow for easier testing of the redfish responses. A main purpose of the node class was to set app.routeDynamic(). However now app.routeDynamic can handle the complexity that was once in critical to node. The macro app.routeDynamic() provides a shorter cleaner interface to the unerlying app.routeDyanic call. The old pattern set permissions for 6 interfaces (get, head, patch, put, delete_, and post) even if only one interface is created. That pattern creates unneeded code that can be safely removed with no effect. Unit test for the responses would have to mock the node the class in order to fully test responses. see https://github.com/openbmc/bmcweb/issues/181 The following files still need node to be extracted. virtual_media.hpp account_service.hpp redfish_sessions.hpp ethernet.hpp The files above use a pattern that is not trivial to address. Often their responses call an async lambda capturing the inherited class. ie (https://github.com/openbmc/bmcweb/blob/ffed87b5ad1797ca966d030e7f979770 28d258fa/redfish-core/lib/account_service.hpp#L1393) At a later point I plan to remove node from the files above. Tested: I ran the docker unit test with the following command. WORKSPACE=$(pwd) UNIT_TEST_PKG=bmcweb ./openbmc-build-scripts/run-unit-test-docker.sh I ran the validator and this change did not create any issues. python3 RedfishServiceValidator.py -c config.ini Signed-off-by: John Edward Broadbent <jebr@google.com> Signed-off-by: Ed Tanous <edtanous@google.com> Change-Id: I147a0289c52cb4198345b1ad9bfe6fdddf57f3df
Diffstat (limited to 'redfish-core/lib/update_service.hpp')
-rw-r--r--redfish-core/lib/update_service.hpp1035
1 files changed, 500 insertions, 535 deletions
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 9f6c6f9abd..bfab6fbe1f 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -15,8 +15,7 @@
*/
#pragma once
-#include "node.hpp"
-
+#include <app.hpp>
#include <boost/container/flat_map.hpp>
#include <utils/fw_utils.hpp>
@@ -33,14 +32,14 @@ static bool fwUpdateInProgress = false;
// Timer for software available
static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
-static void cleanUp()
+inline static void cleanUp()
{
fwUpdateInProgress = false;
fwUpdateMatcher = nullptr;
fwUpdateErrorMatcher = nullptr;
}
-static void activateImage(const std::string& objPath,
- const std::string& service)
+inline static void activateImage(const std::string& objPath,
+ const std::string& service)
{
BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
crow::connections::systemBus->async_method_call(
@@ -383,589 +382,555 @@ static void monitorForSoftwareAvailable(
* UpdateServiceActionsSimpleUpdate class supports handle POST method for
* SimpleUpdate action.
*/
-class UpdateServiceActionsSimpleUpdate : public Node
+inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app)
{
- public:
- UpdateServiceActionsSimpleUpdate(App& app) :
- Node(app,
- "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
- {
- entityPrivileges = {
- {boost::beast::http::verb::get, {{"Login"}}},
- {boost::beast::http::verb::head, {{"Login"}}},
- {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
- }
-
- private:
- void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request& req,
- const std::vector<std::string>&) override
- {
- std::optional<std::string> transferProtocol;
- std::string imageURI;
-
- BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
+ BMCWEB_ROUTE(
+ app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
+ .privileges({"ConfigureComponents"})
+ .methods(
+ boost::beast::http::verb::
+ post)([](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ std::optional<std::string> transferProtocol;
+ std::string imageURI;
+
+ BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
+
+ // User can pass in both TransferProtocol and ImageURI parameters or
+ // they can pass in just the ImageURI with the transfer protocol
+ // embedded within it.
+ // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
+ // 2) ImageURI:tftp://1.1.1.1/myfile.bin
+
+ if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
+ transferProtocol, "ImageURI", imageURI))
+ {
+ BMCWEB_LOG_DEBUG
+ << "Missing TransferProtocol or ImageURI parameter";
+ return;
+ }
+ if (!transferProtocol)
+ {
+ // Must be option 2
+ // Verify ImageURI has transfer protocol in it
+ size_t separator = imageURI.find(':');
+ if ((separator == std::string::npos) ||
+ ((separator + 1) > imageURI.size()))
+ {
+ messages::actionParameterValueTypeError(
+ asyncResp->res, imageURI, "ImageURI",
+ "UpdateService.SimpleUpdate");
+ BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
+ << imageURI;
+ return;
+ }
+ transferProtocol = imageURI.substr(0, separator);
+ // Ensure protocol is upper case for a common comparison path
+ // below
+ boost::to_upper(*transferProtocol);
+ BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
+ << *transferProtocol;
+
+ // Adjust imageURI to not have the protocol on it for parsing
+ // below
+ // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
+ imageURI = imageURI.substr(separator + 3);
+ BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
+ }
- // User can pass in both TransferProtocol and ImageURI parameters or
- // they can pass in just the ImageURI with the transfer protocol
- // embedded within it.
- // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
- // 2) ImageURI:tftp://1.1.1.1/myfile.bin
+ // OpenBMC currently only supports TFTP
+ if (*transferProtocol != "TFTP")
+ {
+ messages::actionParameterNotSupported(
+ asyncResp->res, "TransferProtocol",
+ "UpdateService.SimpleUpdate");
+ BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
+ << *transferProtocol;
+ return;
+ }
- if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
- transferProtocol, "ImageURI", imageURI))
- {
- BMCWEB_LOG_DEBUG
- << "Missing TransferProtocol or ImageURI parameter";
- return;
- }
- if (!transferProtocol)
- {
- // Must be option 2
- // Verify ImageURI has transfer protocol in it
- size_t separator = imageURI.find(':');
+ // Format should be <IP or Hostname>/<file> for imageURI
+ size_t separator = imageURI.find('/');
if ((separator == std::string::npos) ||
((separator + 1) > imageURI.size()))
{
messages::actionParameterValueTypeError(
asyncResp->res, imageURI, "ImageURI",
"UpdateService.SimpleUpdate");
- BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
- << imageURI;
+ BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
return;
}
- transferProtocol = imageURI.substr(0, separator);
- // Ensure protocol is upper case for a common comparison path below
- boost::to_upper(*transferProtocol);
- BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
- << *transferProtocol;
-
- // Adjust imageURI to not have the protocol on it for parsing
- // below
- // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
- imageURI = imageURI.substr(separator + 3);
- BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
- }
-
- // OpenBMC currently only supports TFTP
- if (*transferProtocol != "TFTP")
- {
- messages::actionParameterNotSupported(asyncResp->res,
- "TransferProtocol",
- "UpdateService.SimpleUpdate");
- BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
- << *transferProtocol;
- return;
- }
-
- // Format should be <IP or Hostname>/<file> for imageURI
- size_t separator = imageURI.find('/');
- if ((separator == std::string::npos) ||
- ((separator + 1) > imageURI.size()))
- {
- messages::actionParameterValueTypeError(
- asyncResp->res, imageURI, "ImageURI",
- "UpdateService.SimpleUpdate");
- BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
- return;
- }
- std::string tftpServer = imageURI.substr(0, separator);
- std::string fwFile = imageURI.substr(separator + 1);
- BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
-
- // Setup callback for when new software detected
- // Give TFTP 10 minutes to complete
- monitorForSoftwareAvailable(
- asyncResp, req,
- "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
- 600);
-
- // TFTP can take up to 10 minutes depending on image size and
- // connection speed. Return to caller as soon as the TFTP operation
- // has been started. The callback above will ensure the activate
- // is started once the download has completed
- redfish::messages::success(asyncResp->res);
-
- // Call TFTP service
- crow::connections::systemBus->async_method_call(
- [](const boost::system::error_code ec) {
- if (ec)
- {
- // messages::internalError(asyncResp->res);
- cleanUp();
- BMCWEB_LOG_DEBUG << "error_code = " << ec;
- BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
- }
- else
- {
- BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
- }
- },
- "xyz.openbmc_project.Software.Download",
- "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
- "DownloadViaTFTP", fwFile, tftpServer);
+ std::string tftpServer = imageURI.substr(0, separator);
+ std::string fwFile = imageURI.substr(separator + 1);
+ BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
- BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
- }
-};
-
-class UpdateService : public Node
-{
- public:
- UpdateService(App& app) : Node(app, "/redfish/v1/UpdateService/")
- {
- entityPrivileges = {
- {boost::beast::http::verb::get, {{"Login"}}},
- {boost::beast::http::verb::head, {{"Login"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
- }
-
- private:
- void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request&, const std::vector<std::string>&) override
- {
- asyncResp->res.jsonValue["@odata.type"] =
- "#UpdateService.v1_4_0.UpdateService";
- asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
- asyncResp->res.jsonValue["Id"] = "UpdateService";
- asyncResp->res.jsonValue["Description"] = "Service for Software Update";
- asyncResp->res.jsonValue["Name"] = "Update Service";
- asyncResp->res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
- // UpdateService cannot be disabled
- asyncResp->res.jsonValue["ServiceEnabled"] = true;
- asyncResp->res.jsonValue["FirmwareInventory"] = {
- {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
-#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
- // Update Actions object.
- nlohmann::json& updateSvcSimpleUpdate =
- asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
- updateSvcSimpleUpdate["target"] =
- "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
- updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
- "TFTP"};
-#endif
- // Get the current ApplyTime value
- crow::connections::systemBus->async_method_call(
- [asyncResp](const boost::system::error_code ec,
- const std::variant<std::string>& applyTime) {
- if (ec)
- {
- BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
- messages::internalError(asyncResp->res);
- return;
- }
+ // Setup callback for when new software detected
+ // Give TFTP 10 minutes to complete
+ monitorForSoftwareAvailable(
+ asyncResp, req,
+ "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
+ 600);
- const std::string* s = std::get_if<std::string>(&applyTime);
- if (s == nullptr)
- {
- return;
- }
- // Store the ApplyTime Value
- if (*s == "xyz.openbmc_project.Software.ApplyTime."
- "RequestedApplyTimes.Immediate")
- {
- asyncResp->res
- .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]
- ["ApplyTime"] = "Immediate";
- }
- else if (*s == "xyz.openbmc_project.Software.ApplyTime."
- "RequestedApplyTimes.OnReset")
- {
- asyncResp->res
- .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]
- ["ApplyTime"] = "OnReset";
- }
- },
- "xyz.openbmc_project.Settings",
- "/xyz/openbmc_project/software/apply_time",
- "org.freedesktop.DBus.Properties", "Get",
- "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
- }
+ // TFTP can take up to 10 minutes depending on image size and
+ // connection speed. Return to caller as soon as the TFTP operation
+ // has been started. The callback above will ensure the activate
+ // is started once the download has completed
+ redfish::messages::success(asyncResp->res);
- void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request& req,
- const std::vector<std::string>&) override
- {
- BMCWEB_LOG_DEBUG << "doPatch...";
-
- std::optional<nlohmann::json> pushUriOptions;
- if (!json_util::readJson(req, asyncResp->res, "HttpPushUriOptions",
- pushUriOptions))
- {
- return;
- }
-
- if (pushUriOptions)
- {
- std::optional<nlohmann::json> pushUriApplyTime;
- if (!json_util::readJson(*pushUriOptions, asyncResp->res,
- "HttpPushUriApplyTime", pushUriApplyTime))
- {
- return;
- }
-
- if (pushUriApplyTime)
- {
- std::optional<std::string> applyTime;
- if (!json_util::readJson(*pushUriApplyTime, asyncResp->res,
- "ApplyTime", applyTime))
- {
- return;
- }
-
- if (applyTime)
- {
- std::string applyTimeNewVal;
- if (applyTime == "Immediate")
- {
- applyTimeNewVal =
- "xyz.openbmc_project.Software.ApplyTime."
- "RequestedApplyTimes.Immediate";
- }
- else if (applyTime == "OnReset")
+ // Call TFTP service
+ crow::connections::systemBus->async_method_call(
+ [](const boost::system::error_code ec) {
+ if (ec)
{
- applyTimeNewVal =
- "xyz.openbmc_project.Software.ApplyTime."
- "RequestedApplyTimes.OnReset";
+ // messages::internalError(asyncResp->res);
+ cleanUp();
+ BMCWEB_LOG_DEBUG << "error_code = " << ec;
+ BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
}
else
{
- BMCWEB_LOG_INFO
- << "ApplyTime value is not in the list of "
- "acceptable values";
- messages::propertyValueNotInList(
- asyncResp->res, *applyTime, "ApplyTime");
- return;
+ BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
}
+ },
+ "xyz.openbmc_project.Software.Download",
+ "/xyz/openbmc_project/software",
+ "xyz.openbmc_project.Common.TFTP", "DownloadViaTFTP", fwFile,
+ tftpServer);
- // 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.Settings",
- "/xyz/openbmc_project/software/apply_time",
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Software.ApplyTime",
- "RequestedApplyTime",
- std::variant<std::string>{applyTimeNewVal});
- }
- }
- }
- }
-
- void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request& req,
- const std::vector<std::string>&) override
- {
- BMCWEB_LOG_DEBUG << "doPost...";
-
- // Setup callback for when new software detected
- monitorForSoftwareAvailable(asyncResp, req,
- "/redfish/v1/UpdateService");
-
- std::string filepath(
- "/tmp/images/" +
- boost::uuids::to_string(boost::uuids::random_generator()()));
- BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
- std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
- std::ofstream::trunc);
- out << req.body;
- out.close();
- BMCWEB_LOG_DEBUG << "file upload complete!!";
- }
-};
+ BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
+ });
+}
-class SoftwareInventoryCollection : public Node
+inline void requestRoutesUpdateService(App& app)
{
- public:
- SoftwareInventoryCollection(App& app) :
- Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
- {
- entityPrivileges = {
- {boost::beast::http::verb::get, {{"Login"}}},
- {boost::beast::http::verb::head, {{"Login"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
- }
-
- private:
- void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request&, const std::vector<std::string>&) override
- {
- asyncResp->res.jsonValue["@odata.type"] =
- "#SoftwareInventoryCollection.SoftwareInventoryCollection";
- asyncResp->res.jsonValue["@odata.id"] =
- "/redfish/v1/UpdateService/FirmwareInventory";
- asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
-
- crow::connections::systemBus->async_method_call(
- [asyncResp](
- const boost::system::error_code ec,
- const std::vector<std::pair<
- std::string, std::vector<std::pair<
- std::string, std::vector<std::string>>>>>&
- subtree) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
- asyncResp->res.jsonValue["Members@odata.count"] = 0;
-
- for (auto& obj : subtree)
- {
- sdbusplus::message::object_path path(obj.first);
- std::string swId = path.filename();
- if (swId.empty())
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
+ .privileges({"Login"})
+ .methods(
+ boost::beast::http::verb::
+ get)([](const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#UpdateService.v1_4_0.UpdateService";
+ asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
+ asyncResp->res.jsonValue["Id"] = "UpdateService";
+ asyncResp->res.jsonValue["Description"] =
+ "Service for Software Update";
+ asyncResp->res.jsonValue["Name"] = "Update Service";
+ asyncResp->res.jsonValue["HttpPushUri"] =
+ "/redfish/v1/UpdateService";
+ // UpdateService cannot be disabled
+ asyncResp->res.jsonValue["ServiceEnabled"] = true;
+ asyncResp->res.jsonValue["FirmwareInventory"] = {
+ {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
+#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
+ // Update Actions object.
+ nlohmann::json& updateSvcSimpleUpdate =
+ asyncResp->res
+ .jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
+ updateSvcSimpleUpdate["target"] =
+ "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
+ updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
+ {"TFTP"};
+#endif
+ // Get the current ApplyTime value
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+ const std::variant<std::string>& applyTime) {
+ if (ec)
{
+ BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
messages::internalError(asyncResp->res);
- BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
return;
}
- nlohmann::json& members =
- asyncResp->res.jsonValue["Members"];
- members.push_back(
- {{"@odata.id", "/redfish/v1/UpdateService/"
- "FirmwareInventory/" +
- swId}});
- asyncResp->res.jsonValue["Members@odata.count"] =
- members.size();
- }
- },
- // Note that only firmware levels associated with a device are
- // stored under /xyz/openbmc_project/software therefore to ensure
- // only real FirmwareInventory items are returned, this full object
- // path must be used here as input to mapper
- "xyz.openbmc_project.ObjectMapper",
- "/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"});
- }
-};
-
-class SoftwareInventory : public Node
-{
- public:
- SoftwareInventory(App& app) :
- Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
- std::string())
- {
- entityPrivileges = {
- {boost::beast::http::verb::get, {{"Login"}}},
- {boost::beast::http::verb::head, {{"Login"}}},
- {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
- {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
- }
-
- private:
- /* Fill related item links (i.e. bmc, bios) in for inventory */
- static void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
- const std::string& purpose)
- {
- if (purpose == fw_util::bmcPurpose)
- {
- nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
- relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
- aResp->res.jsonValue["RelatedItem@odata.count"] =
- relatedItem.size();
- }
- else if (purpose == fw_util::biosPurpose)
- {
- nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
- relatedItem.push_back(
- {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
- aResp->res.jsonValue["RelatedItem@odata.count"] =
- relatedItem.size();
- }
- else
- {
- BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
- }
- }
-
- void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const crow::Request&,
- const std::vector<std::string>& params) override
- {
- if (params.size() != 1)
- {
- messages::internalError(asyncResp->res);
-
- return;
- }
-
- std::shared_ptr<std::string> swId =
- std::make_shared<std::string>(params[0]);
-
- asyncResp->res.jsonValue["@odata.id"] =
- "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
-
- crow::connections::systemBus->async_method_call(
- [asyncResp, swId](
- const boost::system::error_code ec,
- const std::vector<std::pair<
- std::string, std::vector<std::pair<
- std::string, std::vector<std::string>>>>>&
- subtree) {
- BMCWEB_LOG_DEBUG << "doGet callback...";
- if (ec)
+ const std::string* s = std::get_if<std::string>(&applyTime);
+ if (s == nullptr)
+ {
+ return;
+ }
+ // Store the ApplyTime Value
+ if (*s == "xyz.openbmc_project.Software.ApplyTime."
+ "RequestedApplyTimes.Immediate")
+ {
+ asyncResp->res
+ .jsonValue["HttpPushUriOptions"]
+ ["HttpPushUriApplyTime"]["ApplyTime"] =
+ "Immediate";
+ }
+ else if (*s == "xyz.openbmc_project.Software.ApplyTime."
+ "RequestedApplyTimes.OnReset")
+ {
+ asyncResp->res
+ .jsonValue["HttpPushUriOptions"]
+ ["HttpPushUriApplyTime"]["ApplyTime"] =
+ "OnReset";
+ }
+ },
+ "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/software/apply_time",
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
+ });
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
+ .privileges({"ConfigureComponents"})
+ .methods(boost::beast::http::verb::patch)(
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ BMCWEB_LOG_DEBUG << "doPatch...";
+
+ std::optional<nlohmann::json> pushUriOptions;
+ if (!json_util::readJson(req, asyncResp->res,
+ "HttpPushUriOptions", pushUriOptions))
{
- messages::internalError(asyncResp->res);
return;
}
- // Ensure we find our input swId, otherwise return an error
- bool found = false;
- for (const std::pair<
- std::string,
- std::vector<
- std::pair<std::string, std::vector<std::string>>>>&
- obj : subtree)
+ if (pushUriOptions)
{
- if (boost::ends_with(obj.first, *swId) != true)
+ std::optional<nlohmann::json> pushUriApplyTime;
+ if (!json_util::readJson(*pushUriOptions, asyncResp->res,
+ "HttpPushUriApplyTime",
+ pushUriApplyTime))
{
- continue;
+ return;
}
- if (obj.second.size() < 1)
+ if (pushUriApplyTime)
{
- continue;
- }
-
- found = true;
- fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
+ std::optional<std::string> applyTime;
+ if (!json_util::readJson(*pushUriApplyTime,
+ asyncResp->res, "ApplyTime",
+ applyTime))
+ {
+ return;
+ }
- crow::connections::systemBus->async_method_call(
- [asyncResp,
- swId](const boost::system::error_code errorCode,
- const boost::container::flat_map<
- std::string, VariantType>& propertiesList) {
- if (errorCode)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- boost::container::flat_map<
- std::string, VariantType>::const_iterator it =
- propertiesList.find("Purpose");
- if (it == propertiesList.end())
+ if (applyTime)
+ {
+ std::string applyTimeNewVal;
+ if (applyTime == "Immediate")
{
- BMCWEB_LOG_DEBUG
- << "Can't find property \"Purpose\"!";
- messages::propertyMissing(asyncResp->res,
- "Purpose");
- return;
+ applyTimeNewVal =
+ "xyz.openbmc_project.Software.ApplyTime."
+ "RequestedApplyTimes.Immediate";
}
- const std::string* swInvPurpose =
- std::get_if<std::string>(&it->second);
- if (swInvPurpose == nullptr)
+ else if (applyTime == "OnReset")
{
- BMCWEB_LOG_DEBUG
- << "wrong types for property\"Purpose\"!";
- messages::propertyValueTypeError(asyncResp->res,
- "", "Purpose");
- return;
+ applyTimeNewVal =
+ "xyz.openbmc_project.Software.ApplyTime."
+ "RequestedApplyTimes.OnReset";
}
-
- BMCWEB_LOG_DEBUG << "swInvPurpose = "
- << *swInvPurpose;
- it = propertiesList.find("Version");
- if (it == propertiesList.end())
+ else
{
- BMCWEB_LOG_DEBUG
- << "Can't find property \"Version\"!";
- messages::propertyMissing(asyncResp->res,
- "Version");
+ BMCWEB_LOG_INFO
+ << "ApplyTime value is not in the list of "
+ "acceptable values";
+ messages::propertyValueNotInList(
+ asyncResp->res, *applyTime, "ApplyTime");
return;
}
- BMCWEB_LOG_DEBUG << "Version found!";
-
- const std::string* version =
- std::get_if<std::string>(&it->second);
+ // 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.Settings",
+ "/xyz/openbmc_project/software/apply_time",
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Software.ApplyTime",
+ "RequestedApplyTime",
+ std::variant<std::string>{applyTimeNewVal});
+ }
+ }
+ }
+ });
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
+ .privileges({"ConfigureComponents"})
+ .methods(boost::beast::http::verb::post)(
+ [](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ BMCWEB_LOG_DEBUG << "doPost...";
+
+ // Setup callback for when new software detected
+ monitorForSoftwareAvailable(asyncResp, req,
+ "/redfish/v1/UpdateService");
+
+ std::string filepath("/tmp/images/" +
+ boost::uuids::to_string(
+ boost::uuids::random_generator()()));
+ BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
+ std::ofstream out(filepath, std::ofstream::out |
+ std::ofstream::binary |
+ std::ofstream::trunc);
+ out << req.body;
+ out.close();
+ BMCWEB_LOG_DEBUG << "file upload complete!!";
+ });
+}
- if (version == nullptr)
- {
- BMCWEB_LOG_DEBUG
- << "Can't find property \"Version\"!";
+inline void requestRoutesSoftwareInventoryCollection(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
+ .privileges({"Login"})
+ .methods(boost::beast::http::verb::get)(
+ [](const crow::Request&,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#SoftwareInventoryCollection.SoftwareInventoryCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/UpdateService/FirmwareInventory";
+ asyncResp->res.jsonValue["Name"] =
+ "Software Inventory Collection";
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](
+ const boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string,
+ std::vector<std::string>>>>>&
+ subtree) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ asyncResp->res.jsonValue["Members"] =
+ nlohmann::json::array();
+ asyncResp->res.jsonValue["Members@odata.count"] = 0;
- messages::propertyValueTypeError(asyncResp->res,
- "", "Version");
- return;
- }
- asyncResp->res.jsonValue["Version"] = *version;
- asyncResp->res.jsonValue["Id"] = *swId;
-
- // swInvPurpose is of format:
- // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
- // Translate this to "ABC image"
- size_t endDesc = swInvPurpose->rfind('.');
- if (endDesc == std::string::npos)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- endDesc++;
- if (endDesc >= swInvPurpose->size())
+ for (auto& obj : subtree)
+ {
+ sdbusplus::message::object_path path(obj.first);
+ std::string swId = path.filename();
+ if (swId.empty())
{
messages::internalError(asyncResp->res);
+ BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
return;
}
- std::string formatDesc =
- swInvPurpose->substr(endDesc);
- asyncResp->res.jsonValue["Description"] =
- formatDesc + " image";
- getRelatedItems(asyncResp, *swInvPurpose);
- },
- obj.second[0].first, obj.first,
- "org.freedesktop.DBus.Properties", "GetAll",
- "xyz.openbmc_project.Software.Version");
- }
- if (!found)
- {
- BMCWEB_LOG_ERROR << "Input swID " + *swId + " not found!";
- messages::resourceMissingAtURI(
- asyncResp->res,
- "/redfish/v1/UpdateService/FirmwareInventory/" + *swId);
- return;
- }
- asyncResp->res.jsonValue["@odata.type"] =
- "#SoftwareInventory.v1_1_0.SoftwareInventory";
- asyncResp->res.jsonValue["Name"] = "Software Inventory";
- asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
-
- asyncResp->res.jsonValue["Updateable"] = false;
- fw_util::getFwUpdateableStatus(asyncResp, swId);
- },
- "xyz.openbmc_project.ObjectMapper",
- "/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"});
+ nlohmann::json& members =
+ asyncResp->res.jsonValue["Members"];
+ members.push_back(
+ {{"@odata.id", "/redfish/v1/UpdateService/"
+ "FirmwareInventory/" +
+ swId}});
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ members.size();
+ }
+ },
+ // Note that only firmware levels associated with a device
+ // are stored under /xyz/openbmc_project/software therefore
+ // to ensure only real FirmwareInventory items are returned,
+ // this full object path must be used here as input to
+ // mapper
+ "xyz.openbmc_project.ObjectMapper",
+ "/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"});
+ });
+}
+/* Fill related item links (i.e. bmc, bios) in for inventory */
+inline static void
+ getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ const std::string& purpose)
+{
+ if (purpose == fw_util::bmcPurpose)
+ {
+ nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
+ relatedItem.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
+ aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItem.size();
}
-};
+ else if (purpose == fw_util::biosPurpose)
+ {
+ nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"];
+ relatedItem.push_back(
+ {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
+ aResp->res.jsonValue["Members@odata.count"] = relatedItem.size();
+ }
+ else
+ {
+ BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
+ }
+}
+
+inline void requestRoutesSoftwareInventory(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
+ .privileges({"Login"})
+ .methods(
+ boost::beast::http::verb::get)([](const crow::Request&,
+ const std::shared_ptr<
+ bmcweb::AsyncResp>& asyncResp,
+ const std::string& param) {
+ std::shared_ptr<std::string> swId =
+ std::make_shared<std::string>(param);
+
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, swId](
+ const boost::system::error_code ec,
+ const std::vector<
+ std::pair<std::string,
+ std::vector<std::pair<
+ std::string, std::vector<std::string>>>>>&
+ subtree) {
+ BMCWEB_LOG_DEBUG << "doGet callback...";
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ // Ensure we find our input swId, otherwise return an error
+ bool found = false;
+ for (const std::pair<
+ std::string,
+ std::vector<std::pair<
+ std::string, std::vector<std::string>>>>& obj :
+ subtree)
+ {
+ if (boost::ends_with(obj.first, *swId) != true)
+ {
+ continue;
+ }
+
+ if (obj.second.size() < 1)
+ {
+ continue;
+ }
+
+ found = true;
+ fw_util::getFwStatus(asyncResp, swId,
+ obj.second[0].first);
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, swId](
+ const boost::system::error_code errorCode,
+ const boost::container::flat_map<
+ std::string, VariantType>& propertiesList) {
+ if (errorCode)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ boost::container::flat_map<
+ std::string, VariantType>::const_iterator
+ it = propertiesList.find("Purpose");
+ if (it == propertiesList.end())
+ {
+ BMCWEB_LOG_DEBUG
+ << "Can't find property \"Purpose\"!";
+ messages::propertyMissing(asyncResp->res,
+ "Purpose");
+ return;
+ }
+ const std::string* swInvPurpose =
+ std::get_if<std::string>(&it->second);
+ if (swInvPurpose == nullptr)
+ {
+ BMCWEB_LOG_DEBUG << "wrong types for "
+ "property\"Purpose\"!";
+ messages::propertyValueTypeError(
+ asyncResp->res, "", "Purpose");
+ return;
+ }
+
+ BMCWEB_LOG_DEBUG << "swInvPurpose = "
+ << *swInvPurpose;
+ it = propertiesList.find("Version");
+ if (it == propertiesList.end())
+ {
+ BMCWEB_LOG_DEBUG
+ << "Can't find property \"Version\"!";
+ messages::propertyMissing(asyncResp->res,
+ "Version");
+ return;
+ }
+
+ BMCWEB_LOG_DEBUG << "Version found!";
+
+ const std::string* version =
+ std::get_if<std::string>(&it->second);
+
+ if (version == nullptr)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Can't find property \"Version\"!";
+
+ messages::propertyValueTypeError(
+ asyncResp->res, "", "Version");
+ return;
+ }
+ asyncResp->res.jsonValue["Version"] = *version;
+ asyncResp->res.jsonValue["Id"] = *swId;
+
+ // swInvPurpose is of format:
+ // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
+ // Translate this to "ABC image"
+ size_t endDesc = swInvPurpose->rfind('.');
+ if (endDesc == std::string::npos)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ endDesc++;
+ if (endDesc >= swInvPurpose->size())
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ std::string formatDesc =
+ swInvPurpose->substr(endDesc);
+ asyncResp->res.jsonValue["Description"] =
+ formatDesc + " image";
+ getRelatedItems(asyncResp, *swInvPurpose);
+ },
+ obj.second[0].first, obj.first,
+ "org.freedesktop.DBus.Properties", "GetAll",
+ "xyz.openbmc_project.Software.Version");
+ }
+ if (!found)
+ {
+ BMCWEB_LOG_ERROR
+ << "Input swID " + *swId + " not found!";
+ messages::resourceMissingAtURI(
+ asyncResp->res,
+ "/redfish/v1/UpdateService/FirmwareInventory/" +
+ *swId);
+ return;
+ }
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#SoftwareInventory.v1_1_0.SoftwareInventory";
+ asyncResp->res.jsonValue["Name"] = "Software Inventory";
+ asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
+
+ asyncResp->res.jsonValue["Updateable"] = false;
+ fw_util::getFwUpdateableStatus(asyncResp, swId);
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/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"});
+ });
+}
} // namespace redfish