Fix association creation
This change contains two fixes:
1) phosphor-health-monitor will now not exit with an
xyz.openbmc_project.Common.Error.ResourceNotFound error even when the
inventory it's looking for (/xyz/openbmc_project/inventory/system) is
not found. The sensor objects will still be created; they will just not
have associations with the system.
2) phosphor-health-monitor now listens for the "BMC inventory activated"
InventoryAdded singal. Upon detecting this signal, it will recreate the
health sensors with associations.
Tested by stopping and restarting
xyz.openbmc_project.Inventory.Manager.service, saw the health sensors
disappear and re-appear from the curl command against the
https://${bmc}/redfish/v1/Chassis/bmc/Sensors RedFish resource.
Signed-off-by: Sui Chen <suichen@google.com>
Change-Id: I37abe0b680038f8989181ac816ac67bfa3ee60bf
diff --git a/healthMonitor.cpp b/healthMonitor.cpp
index c3fccd4..089b939 100644
--- a/healthMonitor.cpp
+++ b/healthMonitor.cpp
@@ -2,11 +2,19 @@
#include "healthMonitor.hpp"
+#include <unistd.h>
+
+#include <boost/asio/deadline_timer.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/sd_event.hpp>
+#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/server/manager.hpp>
#include <sdeventplus/event.hpp>
#include <fstream>
#include <iostream>
+#include <memory>
#include <numeric>
#include <sstream>
@@ -21,6 +29,30 @@
static constexpr bool DEBUG = false;
static constexpr uint8_t defaultHighThreshold = 100;
+// Limit sensor recreation interval to 10s
+bool needUpdate;
+static constexpr int TIMER_INTERVAL = 10;
+std::shared_ptr<boost::asio::deadline_timer> sensorRecreateTimer;
+std::shared_ptr<phosphor::health::HealthMon> healthMon;
+
+void sensorRecreateTimerCallback(
+ std::shared_ptr<boost::asio::deadline_timer> timer)
+{
+ timer->expires_from_now(boost::posix_time::seconds(TIMER_INTERVAL));
+ timer->async_wait([timer](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return;
+ }
+ if (needUpdate)
+ {
+ healthMon->recreateSensors();
+ needUpdate = false;
+ }
+ sensorRecreateTimerCallback(timer);
+ });
+}
+
namespace phosphor
{
namespace health
@@ -366,6 +398,56 @@
checkSensorThreshold(avgValue);
}
+void HealthMon::recreateSensors()
+{
+ PHOSPHOR_LOG2_USING;
+ healthSensors.clear();
+ std::vector<std::string> bmcIds = {};
+ if (FindSystemInventoryInObjectMapper(bus))
+ {
+ try
+ {
+ // Find all BMCs (DBus objects implementing the
+ // Inventory.Item.Bmc interface that may be created by
+ // configuring the Inventory Manager)
+ sdbusplus::message::message msg = bus.new_method_call(
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
+
+ // Search term
+ msg.append(InventoryPath);
+
+ // Limit the depth to 2. Example of "depth":
+ // /xyz/openbmc_project/inventory/system/chassis has a depth of
+ // 1 since it has 1 '/' after
+ // "/xyz/openbmc_project/inventory/system".
+ msg.append(2);
+
+ // Must have the Inventory.Item.Bmc interface
+ msg.append(std::vector<std::string>{
+ "xyz.openbmc_project.Inventory.Item.Bmc"});
+
+ sdbusplus::message::message reply = bus.call(msg, 0);
+ reply.read(bmcIds);
+ info("BMC inventory found");
+ }
+ catch (std::exception& e)
+ {
+ error("Exception occurred while calling {PATH}: {ERROR}", "PATH",
+ InventoryPath, "ERROR", e);
+ }
+ }
+ else
+ {
+ error("Path {PATH} does not exist in ObjectMapper, cannot "
+ "create association",
+ "PATH", InventoryPath);
+ }
+ // Create health sensors
+ createHealthSensors(bmcIds);
+}
+
void printConfig(HealthConfig& cfg)
{
std::cout << "Name: " << cfg.name << "\n";
@@ -509,24 +591,47 @@
*/
int main()
{
+ // The io_context is needed for the timer
+ boost::asio::io_context io;
+
+ // DBus connection
+ auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+
+ conn->request_name(HEALTH_BUS_NAME);
+
// Get a default event loop
auto event = sdeventplus::Event::get_default();
- // Get a handle to system dbus
- auto bus = sdbusplus::bus::new_default();
-
// Create an health monitor object
- phosphor::health::HealthMon healthMon(bus);
-
- // Request service bus name
- bus.request_name(HEALTH_BUS_NAME);
+ healthMon = std::make_shared<phosphor::health::HealthMon>(*conn);
// Add object manager to sensor node
- sdbusplus::server::manager::manager objManager(bus, SENSOR_OBJPATH);
+ sdbusplus::server::manager::manager objManager(*conn, SENSOR_OBJPATH);
- // Attach the bus to sd_event to service user requests
- bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
- event.loop();
+ sdbusplus::asio::sd_event_wrapper sdEvents(io);
+
+ sensorRecreateTimer = std::make_shared<boost::asio::deadline_timer>(io);
+
+ // If the SystemInventory does not exist: wait for the InterfaceAdded signal
+ auto interfacesAddedSignalHandler =
+ std::make_unique<sdbusplus::bus::match::match>(
+ static_cast<sdbusplus::bus::bus&>(*conn),
+ sdbusplus::bus::match::rules::interfacesAdded(
+ phosphor::health::BMCActivationPath),
+ [conn](sdbusplus::message::message& msg) {
+ sdbusplus::message::object_path o;
+ msg.read(o);
+ if (o.str == phosphor::health::BMCActivationPath)
+ {
+ info("should recreate sensors now");
+ needUpdate = true;
+ }
+ });
+
+ // Start the timer
+ io.post([]() { sensorRecreateTimerCallback(sensorRecreateTimer); });
+
+ io.run();
return 0;
}
diff --git a/healthMonitor.hpp b/healthMonitor.hpp
index 0a52174..062c840 100644
--- a/healthMonitor.hpp
+++ b/healthMonitor.hpp
@@ -19,6 +19,30 @@
namespace health
{
+const char* InventoryPath = "/xyz/openbmc_project/inventory";
+
+// Used for identifying the BMC inventory creation signal
+const char* BMCActivationPath = "/xyz/openbmc_project/inventory/bmc/activation";
+
+bool FindSystemInventoryInObjectMapper(sdbusplus::bus::bus& bus)
+{
+ sdbusplus::message::message msg =
+ bus.new_method_call("xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject");
+ msg.append(InventoryPath);
+ msg.append(std::vector<std::string>{});
+
+ try
+ {
+ sdbusplus::message::message reply = bus.call(msg, 0);
+ return true;
+ }
+ catch (const std::exception& e)
+ {}
+ return false;
+}
+
using Json = nlohmann::json;
using ValueIface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
@@ -112,51 +136,19 @@
HealthMon& operator=(HealthMon&&) = delete;
virtual ~HealthMon() = default;
+ /** @brief Recreates sensor objects and their association if possible
+ */
+ void recreateSensors();
+
/** @brief Constructs HealthMon
*
* @param[in] bus - Handle to system dbus
*/
HealthMon(sdbusplus::bus::bus& bus) : bus(bus)
{
- PHOSPHOR_LOG2_USING;
- std::vector<std::string> bmcIds = {};
-
- // Find all BMCs (DBus objects implementing the
- // Inventory.Item.Bmc interface that may be created by
- // configuring the Inventory Manager)
- sdbusplus::message::message msg = bus.new_method_call(
- "xyz.openbmc_project.ObjectMapper",
- "/xyz/openbmc_project/object_mapper",
- "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
-
- // Search term
- msg.append("/xyz/openbmc_project/inventory/system");
-
- // Limit the depth to 2. Example of "depth":
- // /xyz/openbmc_project/inventory/system/chassis has a depth of 1
- // since it has 1 '/' after "/xyz/openbmc_project/inventory/system".
- msg.append(2);
-
- // Must have the Inventory.Item.Bmc interface
- msg.append(
- std::vector<std::string>{"xyz.openbmc_project.Inventory.Item.Bmc"});
-
- sdbusplus::message::message reply = bus.call(msg, 0);
- if (reply.get_signature() == std::string("as"))
- {
- reply.read(bmcIds);
- info("BMC inventory found");
- }
- else
- {
- warning("Did not find BMC inventory, cannot create association");
- }
-
// Read JSON file
sensorConfigs = getHealthConfig();
-
- // Create health sensors
- createHealthSensors(bmcIds);
+ recreateSensors();
}
/** @brief Parse Health config JSON file */