diff options
author | Andrew Geissler <geissonator@yahoo.com> | 2019-04-23 22:40:12 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2019-06-26 21:20:31 +0300 |
commit | 0554c984827c1593663257a842d0cd943ce4f508 (patch) | |
tree | ea0369802a2b813710a9ea2a4c4c6d18d9614c95 /redfish-core/lib/update_service.hpp | |
parent | e4a4b9a95622b8e1c1bae93718699ad19f4882ac (diff) | |
download | bmcweb-0554c984827c1593663257a842d0cd943ce4f508.tar.xz |
simpleupdate: Basic support of SimpleUpdate
This is TFTP only since that is all the OpenBMC back end currently
supports
Design Doc Ref:
https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/20700
Tested:
1) Verified BMC image update via new SimpleUpdate interface worked
curl -k -H "X-Auth-Token: $TOKEN" -d '{"ImageURI":"XXX.XX.X.X/obmc-phosphor-image-witherspoon.ubi.mtd.tar","TransferProtocol":"TFTP"}' -X POST https://${BMC_IP}/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate
{
"@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "Successfully Completed Request",
"MessageArgs": [],
"MessageId": "Base.1.4.0.Success",
"Resolution": "None",
"Severity": "OK"
}
]
}
2) Verified encoding the protocol in ImageURI worked
curl -k -H "X-Auth-Token: $TOKEN" -X POST https://${BMC_IP}/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate -d '{"ImageURI":"tftp://${TFTP_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar"}'
{
"@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "Successfully Completed Request",
"MessageArgs": [],
"MessageId": "Base.1.4.0.Success",
"Resolution": "None",
"Severity": "OK"
}
]
}
Jun 12 20:52:20 witherspoon bmcweb[2470]: (2019-06-12 20:52:20) [DEBUG ] Enter UpdateService.SimpleUpdate doPost
Jun 12 20:52:20 witherspoon bmcweb[2470]: (2019-06-12 20:52:20) [DEBUG ] Encoded transfer protocol tftp
Jun 12 20:52:20 witherspoon bmcweb[2470]: (2019-06-12 20:52:20) [DEBUG ] Adjusted imageUri ${BMC_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar
Jun 12 20:52:20 witherspoon bmcweb[2470]: (2019-06-12 20:52:20) [DEBUG ] Server: ${TFTP_IP} File: obmc-phosphor-image-witherspoon.ubi.mtd.tar
Jun 12 20:52:20 witherspoon bmcweb[2470]: (2019-06-12 20:52:20) [DEBUG ] Exit UpdateService.SimpleUpdate doPost
3) Verified if no transfer protocol, error returned
curl -k -H "X-Auth-Token: $TOKEN" -X POST https://${BMC_IP}/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate -d '{"ImageURI":”${TFTP_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar"}'
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "The value ${TFTP_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar for the parameter ImageURI in the action UpdateService.SimpleUpdate is of a different type than the parameter can accept.",
"MessageArgs": [
“${TFTP_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar",
"ImageURI",
"UpdateService.SimpleUpdate"
],
"MessageId": "Base.1.4.0.ActionParameterValueTypeError",
"Resolution": "Correct the value for the parameter in the request body and resubmit the request if the operation failed.",
"Severity": "Warning"
}
],
"code": "Base.1.4.0.ActionParameterValueTypeError",
"message": "The value ${TFTP_IP}/obmc-phosphor-image-witherspoon.ubi.mtd.tar for the parameter ImageURI in the action UpdateService.SimpleUpdate is of a different type than the parameter can accept."
}
}
4) Verified no new error in Redfish Validator
5) Verified error return when parameter missing:
curl -k -H "X-Auth-Token: $TOKEN" -d '{"TransferProtocol":"TFTP"}' -X POST https://${BMC_IP}/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate
{
"ImageURI@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "The property ImageURI is a required property and must be included in the request.",
"MessageArgs": [
"ImageURI"
],
"MessageId": "Base.1.4.0.PropertyMissing",
"Resolution": "Ensure that the property is in the request body and has a valid value and resubmit the request if the operation failed.",
"Severity": "Warning"
}
]
}
6) Verified that by default, the new Action is not available
Change-Id: I67ea91e181380e6da7ff63a37f02408a318602b7
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Diffstat (limited to 'redfish-core/lib/update_service.hpp')
-rw-r--r-- | redfish-core/lib/update_service.hpp | 183 |
1 files changed, 172 insertions, 11 deletions
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp index 06c1965ce2..7e08ec7c46 100644 --- a/redfish-core/lib/update_service.hpp +++ b/redfish-core/lib/update_service.hpp @@ -54,6 +54,9 @@ static void activateImage(const std::string &objPath, "xyz.openbmc_project.Software.Activation.RequestedActivations." "Active")); } + +// Note that asyncResp can be either a valid pointer or nullptr. If nullptr +// then no asyncResp updates will occur static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, sdbusplus::message::message &m) { @@ -87,7 +90,10 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, BMCWEB_LOG_DEBUG << "error_code = " << error_code; BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); - messages::internalError(asyncResp->res); + if (asyncResp) + { + messages::internalError(asyncResp->res); + } cleanUp(); return; } @@ -96,7 +102,10 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, { BMCWEB_LOG_ERROR << "Invalid Object Size " << objInfo.size(); - messages::internalError(asyncResp->res); + if (asyncResp) + { + messages::internalError(asyncResp->res); + } cleanUp(); return; } @@ -106,7 +115,10 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, fwAvailableTimer = nullptr; activateImage(objPath.str, objInfo[0].first); - redfish::messages::success(asyncResp->res); + if (asyncResp) + { + redfish::messages::success(asyncResp->res); + } fwUpdateInProgress = false; }, "xyz.openbmc_project.ObjectMapper", @@ -118,21 +130,28 @@ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp, } } +// Note that asyncResp can be either a valid pointer or nullptr. If nullptr +// then no asyncResp updates will occur static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, - const crow::Request &req) + const crow::Request &req, + int timeoutTimeSeconds = 5) { // Only allow one FW update at a time if (fwUpdateInProgress != false) { - asyncResp->res.addHeader("Retry-After", "30"); - messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); + if (asyncResp) + { + asyncResp->res.addHeader("Retry-After", "30"); + messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); + } return; } - fwAvailableTimer = std::make_unique<boost::asio::deadline_timer>( - *req.ioService, boost::posix_time::seconds(5)); + fwAvailableTimer = + std::make_unique<boost::asio::deadline_timer>(*req.ioService); - fwAvailableTimer->expires_from_now(boost::posix_time::seconds(5)); + fwAvailableTimer->expires_from_now( + boost::posix_time::seconds(timeoutTimeSeconds)); fwAvailableTimer->async_wait( [asyncResp](const boost::system::error_code &ec) { @@ -151,8 +170,10 @@ static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, BMCWEB_LOG_ERROR << "Async_wait failed" << ec; return; } - - redfish::messages::internalError(asyncResp->res); + if (asyncResp) + { + redfish::messages::internalError(asyncResp->res); + } }); auto callback = [asyncResp](sdbusplus::message::message &m) { @@ -169,6 +190,137 @@ static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp, callback); } +/** + * UpdateServiceActionsSimpleUpdate class supports handle POST method for + * SimpleUpdate action. + */ +class UpdateServiceActionsSimpleUpdate : public Node +{ + public: + UpdateServiceActionsSimpleUpdate(CrowApp &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(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + std::optional<std::string> transferProtocol; + std::string imageURI; + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + 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 protocl 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; + } + + // 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 2 minutes to complete + monitorForSoftwareAvailable(nullptr, req, 120); + + // TFTP can take up to 2 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); + + BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost"; + } +}; + class UpdateService : public Node { public: @@ -199,6 +351,15 @@ class UpdateService : public Node res.jsonValue["ServiceEnabled"] = true; res.jsonValue["FirmwareInventory"] = { {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; +#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE + // Update Actions object. + nlohmann::json &updateSvcSimpleUpdate = + res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"]; + updateSvcSimpleUpdate["target"] = + "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate"; + updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = { + "TFTP"}; +#endif res.end(); } |