fw update: common code
new daemons to implement the flow as described in
https://github.com/openbmc/docs/blob/master/designs/code-update.md
- common/
common code folder
- common update flow
- base class for the device specific update daemons
The new daemons are all following the generic template of Code Updater
daemon as outlined in the design.
The idea is that they are separate daemons (per device, as outlined in
the design) but share all the code that's not device specific.
Tested: next patch in series
Change-Id: If2438b8506aceb8c5313ec13a0bf7cb68f3cc279
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/common/src/software_manager.cpp b/common/src/software_manager.cpp
new file mode 100644
index 0000000..487f63e
--- /dev/null
+++ b/common/src/software_manager.cpp
@@ -0,0 +1,194 @@
+#include "software_manager.hpp"
+
+#include "sdbusplus/async/timer.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+SoftwareManager::SoftwareManager(sdbusplus::async::context& ctx,
+ const std::string& busNameSuffix,
+ bool isDryRun) :
+ dryRun(isDryRun), ctx(ctx), busNameSuffix(busNameSuffix), manager(ctx, "/")
+{
+ lg2::debug("initialized SoftwareManager");
+}
+
+std::string SoftwareManager::setupBusName()
+{
+ const std::string serviceNameFull =
+ "xyz.openbmc_project.Software." + this->busNameSuffix;
+
+ lg2::debug("requesting dbus name {BUSNAME}", "BUSNAME", serviceNameFull);
+
+ ctx.get_bus().request_name(serviceNameFull.c_str());
+
+ return serviceNameFull;
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<bool> SoftwareManager::setHostPowerstate(bool state)
+// NOLINTEND
+{
+ auto proxy = sdbusplus::async::proxy()
+ .service("xyz.openbmc_project.State.Host")
+ .path("/xyz/openbmc_project/state/host0")
+ .interface("xyz.openbmc_project.State.Host");
+
+ lg2::info("[PWR] changing host power state to {STATE}", "STATE",
+ (state) ? "ON" : "OFF");
+
+ std::string voff = "xyz.openbmc_project.State.Host.Transition.Off";
+ std::string von = "xyz.openbmc_project.State.Host.Transition.On";
+ std::string targetState;
+ if (state)
+ {
+ co_await proxy.set_property(ctx, "RequestedHostTransition", von);
+ targetState = "xyz.openbmc_project.State.Host.HostState.Running";
+ }
+ else
+ {
+ co_await proxy.set_property(ctx, "RequestedHostTransition", voff);
+ targetState = "xyz.openbmc_project.State.Host.HostState.Off";
+ }
+
+ lg2::debug("[PWR] requested host transition to {STATE}", "STATE",
+ targetState);
+
+ lg2::debug("[PWR] async sleep to wait for state transition");
+ co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(10));
+
+ auto actualOpt = co_await getHostPowerstate();
+
+ if (actualOpt == std::nullopt)
+ {
+ co_return false;
+ }
+
+ const bool actual = actualOpt.value();
+
+ if (actual == state)
+ {
+ lg2::debug("[PWR] successfully achieved state {STATE}", "STATE",
+ targetState);
+ co_return true;
+ }
+ else
+ {
+ lg2::debug("[PWR] failed to achieve state {STATE}", "STATE",
+ targetState);
+ co_return false;
+ }
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<std::optional<bool>> SoftwareManager::getHostPowerstate()
+// NOLINTEND
+{
+ auto proxy = sdbusplus::async::proxy()
+ .service("xyz.openbmc_project.State.Host")
+ .path("/xyz/openbmc_project/state/host0")
+ .interface("xyz.openbmc_project.State.Host");
+
+ std::string stateOn = "xyz.openbmc_project.State.Host.HostState.Running";
+ std::string stateOff = "xyz.openbmc_project.State.Host.HostState.Off";
+
+ std::string res =
+ co_await proxy.get_property<std::string>(ctx, "CurrentHostState");
+
+ if (res == stateOn)
+ {
+ co_return true;
+ }
+ else if (res == stateOff)
+ {
+ co_return false;
+ }
+
+ lg2::error("[PWR] unexpected power state: {STATE}", "STATE", res);
+
+ co_return true;
+}
+
+// NOLINTBEGIN
+sdbusplus::async::task<> SoftwareManager::getInitialConfiguration(
+ const std::vector<std::string>& configurationInterfaces)
+// NOLINTEND
+{
+ auto client = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>(ctx)
+ .service("xyz.openbmc_project.ObjectMapper")
+ .path("/xyz/openbmc_project/object_mapper");
+
+ auto res =
+ co_await client.get_sub_tree("/xyz/openbmc_project/inventory", 0, {});
+
+ for (auto& iface : configurationInterfaces)
+ {
+ lg2::debug("[config] looking for dbus interface {INTF}", "INTF", iface);
+ }
+
+ for (auto& [path, v] : res)
+ {
+ for (auto& [service, interfaceNames] : v)
+ {
+ std::string interfaceFound;
+
+ for (std::string& interfaceName : interfaceNames)
+ {
+ for (auto& iface : configurationInterfaces)
+ {
+ if (interfaceName == iface)
+ {
+ interfaceFound = interfaceName;
+ }
+ }
+ }
+
+ if (interfaceFound.empty())
+ {
+ continue;
+ }
+
+ lg2::debug(
+ "[config] found configuration interface at {SERVICE}, {OBJPATH}",
+ "SERVICE", service, "OBJPATH", path);
+
+ std::optional<uint64_t> optVendorIANA =
+ co_await SoftwareManager::dbusGetRequiredProperty<uint64_t>(
+ service, path, interfaceFound, "VendorIANA");
+
+ std::optional<std::string> optCompatible =
+ co_await SoftwareManager::dbusGetRequiredProperty<std::string>(
+ service, path, interfaceFound, "Compatible");
+
+ std::optional<std::string> optEMConfigType =
+ co_await SoftwareManager::dbusGetRequiredProperty<std::string>(
+ service, path, interfaceFound, "Type");
+
+ std::optional<std::string> optEMConfigName =
+ co_await SoftwareManager::dbusGetRequiredProperty<std::string>(
+ service, path, interfaceFound, "Name");
+
+ if (!optVendorIANA.has_value() || !optCompatible.has_value() ||
+ !optEMConfigType.has_value() || !optEMConfigName.has_value())
+ {
+ continue;
+ }
+
+ DeviceConfig config(optVendorIANA.value(), optCompatible.value(),
+ optEMConfigType.value(),
+ optEMConfigName.value());
+
+ co_await getInitialConfigurationSingleDevice(service, path, config);
+ }
+ }
+
+ lg2::debug("[config] done with initial configuration");
+
+ setupBusName();
+}