fw-update: Support host image update
Per the Redfish schema for the UpdateService, it supports all
SoftwareInventory targets. On OpenBMC this is currently the BMC and the
Host (BIOS) firmware.
Introduced a new fwUpdateInProgress static variable so that the signal
subscription could be disabled once the required interface was added.
This prevented multiple call backs and multiple activations of the same
image.
Tested: Verified that a host and BMC image update worked as expected on
a witherspoon system.
$ curl -k -H "X-Auth-Token: $TOKEN" -H "Content-Type: application/octet-stream" -X POST -T ./obmc-phosphor-image-witherspoon.ubi.mtd.tar https://${BMC_IP}/redfish/v1/UpdateService
{
"@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"
}
]
}
Change-Id: I7bb9381f1b79bf65604464b628ef98646951d840
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index aaaf85b..9f16e1e 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -24,7 +24,10 @@
namespace redfish
{
+// Match signals added on software path
static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
+// Only allow one update at a time
+static bool fwUpdateInProgress = false;
class UpdateService : public Node
{
@@ -58,36 +61,113 @@
{"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
res.end();
}
- static void activateImage(const std::string &objPath)
+ static void cleanUp()
{
+ fwUpdateInProgress = false;
+ fwUpdateMatcher = nullptr;
+ }
+ 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(
- [objPath](const boost::system::error_code error_code) {
+ [](const boost::system::error_code error_code) {
if (error_code)
{
BMCWEB_LOG_DEBUG << "error_code = " << error_code;
BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
}
},
- "xyz.openbmc_project.Software.BMC.Updater", objPath,
- "org.freedesktop.DBus.Properties", "Set",
+ service, objPath, "org.freedesktop.DBus.Properties", "Set",
"xyz.openbmc_project.Software.Activation", "RequestedActivation",
std::variant<std::string>(
"xyz.openbmc_project.Software.Activation.RequestedActivations."
"Active"));
}
+ static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
+ boost::asio::deadline_timer &timeout,
+ sdbusplus::message::message &m)
+ {
+ std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::variant<std::string>>>>>
+ interfacesProperties;
+
+ sdbusplus::message::object_path objPath;
+
+ m.read(objPath, interfacesProperties);
+
+ BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
+ for (auto &interface : interfacesProperties)
+ {
+ BMCWEB_LOG_DEBUG << "interface = " << interface.first;
+
+ if (interface.first == "xyz.openbmc_project.Software.Activation")
+ {
+ // Found our interface, disable callbacks
+ fwUpdateMatcher = nullptr;
+
+ // Retrieve service and activate
+ crow::connections::systemBus->async_method_call(
+ [objPath, asyncResp, &timeout](
+ const boost::system::error_code error_code,
+ const std::vector<std::pair<
+ std::string, std::vector<std::string>>> &objInfo) {
+ if (error_code)
+ {
+ BMCWEB_LOG_DEBUG << "error_code = " << error_code;
+ BMCWEB_LOG_DEBUG << "error msg = "
+ << error_code.message();
+ messages::internalError(asyncResp->res);
+ cleanUp();
+ return;
+ }
+ // Ensure we only got one service back
+ if (objInfo.size() != 1)
+ {
+ BMCWEB_LOG_ERROR << "Invalid Object Size "
+ << objInfo.size();
+ messages::internalError(asyncResp->res);
+ cleanUp();
+ return;
+ }
+ // cancel timer only when
+ // xyz.openbmc_project.Software.Activation interface
+ // is added
+ boost::system::error_code ec;
+ timeout.cancel(ec);
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "error canceling timer " << ec;
+ }
+ UpdateService::activateImage(objPath.str,
+ objInfo[0].first);
+ redfish::messages::success(asyncResp->res);
+ fwUpdateInProgress = false;
+ },
+ "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"});
+ }
+ }
+ }
void doPost(crow::Response &res, const crow::Request &req,
const std::vector<std::string> ¶ms) override
{
BMCWEB_LOG_DEBUG << "doPost...";
// Only allow one FW update at a time
- if (fwUpdateMatcher != nullptr)
+ if (fwUpdateInProgress != false)
{
res.addHeader("Retry-After", "30");
- messages::serviceTemporarilyUnavailable(res, "3");
+ messages::serviceTemporarilyUnavailable(res, "30");
res.end();
return;
}
+
// Make this const static so it survives outside this method
static boost::asio::deadline_timer timeout(
*req.ioService, boost::posix_time::seconds(5));
@@ -95,7 +175,7 @@
timeout.expires_from_now(boost::posix_time::seconds(5));
timeout.async_wait([&res](const boost::system::error_code &ec) {
- fwUpdateMatcher = nullptr;
+ cleanUp();
if (ec == boost::asio::error::operation_aborted)
{
// expected, we were canceled before the timer completed.
@@ -115,52 +195,14 @@
res.end();
});
- auto callback = [&res](sdbusplus::message::message &m) {
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+ auto callback = [asyncResp](sdbusplus::message::message &m) {
BMCWEB_LOG_DEBUG << "Match fired";
-
- if (m.is_method_error())
- {
- BMCWEB_LOG_DEBUG << "Dbus method error!!!";
- res.end();
- return;
- }
- std::vector<std::pair<
- std::string,
- std::vector<std::pair<std::string, std::variant<std::string>>>>>
- interfacesProperties;
-
- sdbusplus::message::object_path objPath;
-
- m.read(objPath, interfacesProperties); // Read in the object path
- // that was just created
- // std::string str_objpath = objPath.str; // keep a copy for
- // constructing response message
- BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
- for (auto &interface : interfacesProperties)
- {
- BMCWEB_LOG_DEBUG << "interface = " << interface.first;
-
- if (interface.first ==
- "xyz.openbmc_project.Software.Activation")
- {
- // cancel timer only when
- // xyz.openbmc_project.Software.Activation interface is
- // added
- boost::system::error_code ec;
- timeout.cancel(ec);
- if (ec)
- {
- BMCWEB_LOG_ERROR << "error canceling timer " << ec;
- }
- UpdateService::activateImage(objPath.str); // str_objpath);
- redfish::messages::success(res);
- BMCWEB_LOG_DEBUG << "ending response";
- res.end();
- fwUpdateMatcher = nullptr;
- }
- }
+ softwareInterfaceAdded(asyncResp, timeout, m);
};
+ fwUpdateInProgress = true;
+
fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
*crow::connections::systemBus,
"interface='org.freedesktop.DBus.ObjectManager',type='signal',"