diff options
author | George Liu <liuxiwei@inspur.com> | 2020-05-12 11:06:27 +0300 |
---|---|---|
committer | George Liu <liuxiwei@inspur.com> | 2023-03-24 09:41:34 +0300 |
commit | 0ed80c8ce93a38eca6951ffad5c3143a3a720053 (patch) | |
tree | 4cb6dbaf356058c80979319780dc861d81b2363e | |
parent | 6c3e94511027133db407f9e1c3c628927ec1332f (diff) | |
download | bmcweb-0ed80c8ce93a38eca6951ffad5c3143a3a720053.tar.xz |
UpdateService: Support for MultipartHttpPushUri
Since a service support the MultipartHttpPushUri property within the
UpdateService resource, so add a new MultipartHttpPushUri property,
This property shall contain a URI used to perform a Redfish
Specification-defined Multipart HTTP or HTTPS POST of a software image
for the purpose of installing software contained within the image, and
use the requirements of a multipart/form-data to request body for HTTP
push software update.
Tested:
curl -k -H "X-Auth-Token: $token" -H "Content-Type:
multipart/form-data"
-F
'UpdateParameters={"Targets":["/redfish/v1/Managers/bmc"],
"@Redfish.OperationApplyTime":"Immediate"};type=application/json'
-F
'UpdateFile=@obmc-phosphor-image.static.mtd.tar;type=application/
octet-stream'
https://${bmc}/redfish/v1/UpdateService/update
{
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"Id": "0",
"TaskState": "Running",
"TaskStatus": "OK"
}
Passed the validator:
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Ie1ea0594337efdb073270aba6918389c4381a2b3
-rw-r--r-- | redfish-core/lib/update_service.hpp | 214 |
1 files changed, 167 insertions, 47 deletions
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp index 0d4c1b70b5..650ba95622 100644 --- a/redfish-core/lib/update_service.hpp +++ b/redfish-core/lib/update_service.hpp @@ -19,6 +19,7 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "multipart_parser.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "task.hpp" @@ -32,6 +33,7 @@ #include <sdbusplus/unpack_properties.hpp> #include <array> +#include <filesystem> #include <string_view> namespace redfish @@ -527,6 +529,150 @@ inline void requestRoutesUpdateServiceActionsSimpleUpdate(App& app) }); } +inline void uploadImageFile(crow::Response& res, std::string_view body) +{ + std::filesystem::path 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); + // set the permission of the file to 640 + std::filesystem::perms permission = + std::filesystem::perms::owner_read | std::filesystem::perms::group_read; + std::filesystem::permissions(filepath, permission); + out << body; + + if (out.bad()) + { + messages::internalError(res); + cleanUp(); + } +} + +inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& applyTime) +{ + std::string applyTimeNewVal; + if (applyTime == "Immediate") + { + applyTimeNewVal = + "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; + } + else if (applyTime == "OnReset") + { + applyTimeNewVal = + "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; + } + else + { + BMCWEB_LOG_INFO + << "ApplyTime value is not in the list of acceptable values"; + messages::propertyValueNotInList(asyncResp->res, applyTime, + "ApplyTime"); + return; + } + + // 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", + dbus::utility::DbusVariantType{applyTimeNewVal}); +} + +inline void + updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const MultipartParser& parser) +{ + const std::string* uploadData = nullptr; + std::optional<std::string> applyTime = "OnReset"; + bool targetFound = false; + for (const FormPart& formpart : parser.mime_fields) + { + boost::beast::http::fields::const_iterator it = + formpart.fields.find("Content-Disposition"); + if (it == formpart.fields.end()) + { + BMCWEB_LOG_ERROR << "Couldn't find Content-Disposition"; + return; + } + BMCWEB_LOG_INFO << "Parsing value " << it->value(); + + // The construction parameters of param_list must start with `;` + size_t index = it->value().find(';'); + if (index == std::string::npos) + { + continue; + } + + for (auto const& param : + boost::beast::http::param_list{it->value().substr(index)}) + { + if (param.first != "name" || param.second.empty()) + { + continue; + } + + if (param.second == "UpdateParameters") + { + std::vector<std::string> targets; + nlohmann::json content = + nlohmann::json::parse(formpart.content); + if (!json_util::readJson(content, asyncResp->res, "Targets", + targets, "@Redfish.OperationApplyTime", + applyTime)) + { + return; + } + if (targets.size() != 1) + { + messages::propertyValueFormatError(asyncResp->res, + "Targets", ""); + return; + } + if (targets[0] != "/redfish/v1/Managers/bmc") + { + messages::propertyValueNotInList(asyncResp->res, + "Targets/0", targets[0]); + return; + } + targetFound = true; + } + else if (param.second == "UpdateFile") + { + uploadData = &(formpart.content); + } + } + } + + if (uploadData == nullptr) + { + BMCWEB_LOG_ERROR << "Upload data is NULL"; + messages::propertyMissing(asyncResp->res, "UpdateFile"); + return; + } + if (!targetFound) + { + messages::propertyMissing(asyncResp->res, "targets"); + return; + } + + setApplyTime(asyncResp, *applyTime); + + uploadImageFile(asyncResp->res, *uploadData); +} + inline void handleUpdateServicePost(App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -540,15 +686,23 @@ inline void // 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!!"; + MultipartParser parser; + ParserError ec = parser.parse(req); + if (ec == ParserError::ERROR_BOUNDARY_FORMAT) + { + // If the request didnt' contain boundary information, assume it was a + // POST binary payload. + uploadImageFile(asyncResp->res, req.body()); + return; + } + if (ec != ParserError::PARSER_SUCCESS) + { + // handle error + BMCWEB_LOG_ERROR << "MIME parse failed, ec : " << static_cast<int>(ec); + messages::internalError(asyncResp->res); + return; + } + updateMultipartContext(asyncResp, parser); } inline void requestRoutesUpdateService(App& app) @@ -563,7 +717,7 @@ inline void requestRoutesUpdateService(App& app) return; } asyncResp->res.jsonValue["@odata.type"] = - "#UpdateService.v1_5_0.UpdateService"; + "#UpdateService.v1_11_1.UpdateService"; asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService"; asyncResp->res.jsonValue["Id"] = "UpdateService"; asyncResp->res.jsonValue["Description"] = "Service for Software Update"; @@ -580,6 +734,8 @@ inline void requestRoutesUpdateService(App& app) asyncResp->res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService/update"; + asyncResp->res.jsonValue["MultipartHttpPushUri"] = + "/redfish/v1/UpdateService/update"; // UpdateService cannot be disabled asyncResp->res.jsonValue["ServiceEnabled"] = true; @@ -667,43 +823,7 @@ inline void requestRoutesUpdateService(App& app) if (applyTime) { - std::string applyTimeNewVal; - if (applyTime == "Immediate") - { - applyTimeNewVal = - "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate"; - } - else if (applyTime == "OnReset") - { - applyTimeNewVal = - "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; - } - else - { - BMCWEB_LOG_INFO - << "ApplyTime value is not in the list of acceptable values"; - messages::propertyValueNotInList( - asyncResp->res, *applyTime, "ApplyTime"); - return; - } - - // 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", - dbus::utility::DbusVariantType{applyTimeNewVal}); + setApplyTime(asyncResp, *applyTime); } } } |