PEL: Use class to watch properties in DataIface
Instead of having the DataInterface class explicitly watch for all of
the PropertiesChanged and InterfacesAdded signals for the D-Bus
properties it needs the values of, create some classes to wrap that
functionality.
The PropertyWatcher class will call a user defined function, passing it
the property value for the path/interface/property specified in the
following cases:
1) On construction, by making a property read method call, if the
property is on D-Bus then.
2) On a properties changed signal for that property.
3) On an interfaces added signal for that property's interface.
The InterfaceWatcher class will call a user defined function, passing it
the property name/value map for all properties in that interface, in the
following cases:
1) On construction, by making a GetAll property read method call, if the
interface is on D-Bus then.
2) On a properties changed signal for that interface.
3) On an interfaces added signal for that interface.
Both of these are derived from the DBusWatcher class, and the
DataInterface will store a vector of DBusWatcher pointers after it
creates the instances of the PropertyWatcher or InterfaceWatcher classes
in its constructor.
This commit changes the current properties being watched - the system
model, the system serial number, and the operating system status to this
method.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Iade4ac89a9ce1d46bdebf353350bf161722ced9f
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index 3342569..deb4ba4 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -51,70 +51,50 @@
DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
{
- readMTMS();
- readHostState();
readBMCFWVersion();
readServerFWVersion();
readBMCFWVersionID();
+
+ // Watch both the Model and SN properties on the system's Asset iface
+ _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
+ bus, object_path::systemInv, interface::invAsset, *this,
+ [this](const auto& properties) {
+ auto model = properties.find("Model");
+ if (model != properties.end())
+ {
+ this->_machineTypeModel = std::get<std::string>(model->second);
+ }
+
+ auto sn = properties.find("SerialNumber");
+ if (sn != properties.end())
+ {
+ this->_machineSerialNumber = std::get<std::string>(sn->second);
+ }
+ }));
+
+ // Watch the OperatingSystemState property
+ _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
+ bus, object_path::hostState, interface::osStatus,
+ "OperatingSystemState", *this, [this](const auto& value) {
+ auto status =
+ Status::convertOSStatusFromString(std::get<std::string>(value));
+
+ if ((status == Status::OSStatus::BootComplete) ||
+ (status == Status::OSStatus::Standby))
+ {
+ setHostState(true);
+ }
+ else
+ {
+ setHostState(false);
+ }
+ }));
}
-void DataInterface::readMTMS()
-{
- // If this runs when the inventory service isn't running, it will get the
- // value whenever it starts via the propertiesChanged callback.
- try
- {
- auto inventoryService =
- getService(object_path::systemInv, interface::invAsset);
-
- if (!inventoryService.empty())
- {
- auto properties = getAllProperties(
- inventoryService, object_path::systemInv, interface::invAsset);
-
- _machineTypeModel = std::get<std::string>(properties["Model"]);
-
- _machineSerialNumber =
- std::get<std::string>(properties["SerialNumber"]);
- }
- }
- catch (std::exception& e)
- {
- // Inventory must not be running at this moment.
- }
-
- // Keep up to date by watching for the propertiesChanged signal.
- _sysInventoryPropMatch = std::make_unique<sdbusplus::bus::match_t>(
- _bus,
- sdbusplus::bus::match::rules::propertiesChanged(object_path::systemInv,
- interface::invAsset),
- std::bind(std::mem_fn(&DataInterface::sysAssetPropChanged), this,
- std::placeholders::_1));
-}
-
-void DataInterface::sysAssetPropChanged(sdbusplus::message::message& msg)
-{
- DBusInterface interface;
- DBusPropertyMap properties;
-
- msg.read(interface, properties);
-
- auto model = properties.find("Model");
- if (model != properties.end())
- {
- _machineTypeModel = std::get<std::string>(model->second);
- }
-
- auto sn = properties.find("SerialNumber");
- if (sn != properties.end())
- {
- _machineSerialNumber = std::get<std::string>(sn->second);
- }
-}
-
-DBusPropertyMap DataInterface::getAllProperties(const std::string& service,
- const std::string& objectPath,
- const std::string& interface)
+DBusPropertyMap
+ DataInterface::getAllProperties(const std::string& service,
+ const std::string& objectPath,
+ const std::string& interface) const
{
DBusPropertyMap properties;
@@ -131,7 +111,8 @@
void DataInterface::getProperty(const std::string& service,
const std::string& objectPath,
const std::string& interface,
- const std::string& property, DBusValue& value)
+ const std::string& property,
+ DBusValue& value) const
{
auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
@@ -164,67 +145,6 @@
return std::string{};
}
-void DataInterface::readHostState()
-{
- _hostUp = false;
-
- try
- {
- auto service = getService(object_path::hostState, interface::osStatus);
- if (!service.empty())
- {
- DBusValue value;
- getProperty(service, object_path::hostState, interface::osStatus,
- "OperatingSystemState", value);
-
- auto status =
- Status::convertOSStatusFromString(std::get<std::string>(value));
-
- if ((status == Status::OSStatus::BootComplete) ||
- (status == Status::OSStatus::Standby))
- {
- _hostUp = true;
- }
- }
- }
- catch (std::exception& e)
- {
- // Not available yet.
- }
-
- // Keep up to date by watching for the propertiesChanged signal.
- _osStateMatch = std::make_unique<sdbusplus::bus::match_t>(
- _bus,
- sdbusplus::bus::match::rules::propertiesChanged(object_path::hostState,
- interface::osStatus),
- std::bind(std::mem_fn(&DataInterface::osStatePropChanged), this,
- std::placeholders::_1));
-}
-
-void DataInterface::osStatePropChanged(sdbusplus::message::message& msg)
-{
- DBusInterface interface;
- DBusPropertyMap properties;
-
- msg.read(interface, properties);
-
- auto state = properties.find("OperatingSystemState");
- if (state != properties.end())
- {
- auto status = Status::convertOSStatusFromString(
- std::get<std::string>(state->second));
-
- bool newHostState = false;
- if ((status == Status::OSStatus::BootComplete) ||
- (status == Status::OSStatus::Standby))
- {
- newHostState = true;
- }
-
- setHostState(newHostState);
- }
-}
-
uint8_t DataInterface::getPLDMInstanceID(uint8_t eid) const
{
return 0;
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index e139c07..a275e4d 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include "dbus_types.hpp"
+#include "dbus_watcher.hpp"
+
#include <filesystem>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
@@ -10,14 +13,6 @@
namespace pels
{
-using DBusValue = sdbusplus::message::variant<std::string>;
-using DBusProperty = std::string;
-using DBusInterface = std::string;
-using DBusService = std::string;
-using DBusPath = std::string;
-using DBusInterfaceList = std::vector<DBusInterface>;
-using DBusPropertyMap = std::map<DBusProperty, DBusValue>;
-
/**
* @class DataInterface
*
@@ -269,48 +264,6 @@
*/
uint8_t getPLDMInstanceID(uint8_t eid) const override;
- private:
- /**
- * @brief Reads the machine type/model and SN from D-Bus.
- *
- * Looks for them on the 'system' inventory object, and also
- * places a properties changed watch on them to obtain any changes
- * (or read them for the first time if the inventory isn't ready
- * when this function runs.)
- */
- void readMTMS();
-
- /**
- * @brief Reads the host state from D-Bus.
- *
- * For host on, looks for the values of 'BootComplete' or 'Standby'
- * in the OperatingSystemState property on the
- * 'xyz.openbmc_project.State.OperatingSystem.Status' interface
- * on the '/xyz/openbmc_project/state/host0' path.
- *
- * Also adds a properties changed watch on it so the code can be
- * kept up to date on changes.
- */
- void readHostState();
-
- /**
- * @brief Reads the BMC firmware version string and puts it into
- * _bmcFWVersion.
- */
- void readBMCFWVersion();
-
- /**
- * @brief Reads the server firmware version string and puts it into
- * _serverFWVersion.
- */
- void readServerFWVersion();
-
- /**
- * @brief Reads the BMC firmware version ID and puts it into
- * _bmcFWVersionID.
- */
- void readBMCFWVersionID();
-
/**
* @brief Finds the D-Bus service name that hosts the
* passed in path and interface.
@@ -331,7 +284,7 @@
*/
DBusPropertyMap getAllProperties(const std::string& service,
const std::string& objectPath,
- const std::string& interface);
+ const std::string& interface) const;
/**
* @brief Wrapper for the 'Get' properties method call
@@ -344,33 +297,33 @@
*/
void getProperty(const std::string& service, const std::string& objectPath,
const std::string& interface, const std::string& property,
- DBusValue& value);
+ DBusValue& value) const;
+
+ private:
+ /**
+ * @brief Reads the BMC firmware version string and puts it into
+ * _bmcFWVersion.
+ */
+ void readBMCFWVersion();
/**
- * @brief The properties changed callback for the Asset iface
- * on the system inventory object.
- *
- * @param[in] msg - The sdbusplus message of the signal
+ * @brief Reads the server firmware version string and puts it into
+ * _serverFWVersion.
*/
- void sysAssetPropChanged(sdbusplus::message::message& msg);
+ void readServerFWVersion();
/**
- * @brief The properties changed callback for the OperatingSystemStatus
- * interface on the host state object.
- *
- * @param[in] msg - The sdbusplus message of the signal
+ * @brief Reads the BMC firmware version ID and puts it into
+ * _bmcFWVersionID.
*/
- void osStatePropChanged(sdbusplus::message::message& msg);
+ void readBMCFWVersionID();
/**
- * @brief The match object for the system path's properties
+ * @brief The D-Bus property or interface watchers that have callbacks
+ * registered that will set members in this class when
+ * they change.
*/
- std::unique_ptr<sdbusplus::bus::match_t> _sysInventoryPropMatch;
-
- /**
- * @brief The match object for the operating system status.
- */
- std::unique_ptr<sdbusplus::bus::match_t> _osStateMatch;
+ std::vector<std::unique_ptr<DBusWatcher>> _properties;
/**
* @brief The sdbusplus bus object for making D-Bus calls.
diff --git a/extensions/openpower-pels/dbus_types.hpp b/extensions/openpower-pels/dbus_types.hpp
new file mode 100644
index 0000000..d263958
--- /dev/null
+++ b/extensions/openpower-pels/dbus_types.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <map>
+#include <sdbusplus/bus.hpp>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace openpower::pels
+{
+
+using DBusValue =
+ sdbusplus::message::variant<std::string, bool, std::vector<uint8_t>>;
+using DBusProperty = std::string;
+using DBusInterface = std::string;
+using DBusService = std::string;
+using DBusPath = std::string;
+using DBusInterfaceList = std::vector<DBusInterface>;
+using DBusPathList = std::vector<DBusPath>;
+using DBusPropertyMap = std::map<DBusProperty, DBusValue>;
+using DBusInterfaceMap = std::map<DBusInterface, DBusPropertyMap>;
+
+} // namespace openpower::pels
diff --git a/extensions/openpower-pels/dbus_watcher.hpp b/extensions/openpower-pels/dbus_watcher.hpp
new file mode 100644
index 0000000..bdd3b2c
--- /dev/null
+++ b/extensions/openpower-pels/dbus_watcher.hpp
@@ -0,0 +1,339 @@
+#pragma once
+
+#include "dbus_types.hpp"
+
+#include <sdbusplus/bus/match.hpp>
+
+namespace openpower::pels
+{
+
+using sdbusplus::exception::SdBusError;
+namespace match_rules = sdbusplus::bus::match::rules;
+
+/**
+ * @class DBusWatcher
+ *
+ * The base class for the PropertyWatcher and InterfaceWatcher classes.
+ */
+class DBusWatcher
+{
+ public:
+ DBusWatcher() = delete;
+ virtual ~DBusWatcher() = default;
+ DBusWatcher(const DBusWatcher&) = default;
+ DBusWatcher& operator=(const DBusWatcher&) = default;
+ DBusWatcher(DBusWatcher&&) = default;
+ DBusWatcher& operator=(DBusWatcher&&) = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] path - The D-Bus path that will be watched
+ * @param[in] interface - The D-Bus interface that will be watched
+ */
+ DBusWatcher(const std::string& path, const std::string& interface) :
+ _path(path), _interface(interface)
+ {
+ }
+
+ protected:
+ /**
+ * @brief The D-Bus path
+ */
+ std::string _path;
+
+ /**
+ * @brief The D-Bus interface
+ */
+ std::string _interface;
+
+ /**
+ * @brief The match objects for the propertiesChanged and
+ * interfacesAdded signals.
+ */
+ std::vector<sdbusplus::bus::match_t> _matches;
+};
+
+/**
+ * @class PropertyWatcher
+ *
+ * This class allows the user to be kept up to data with a D-Bus
+ * property's value. It does this by calling a user specified function
+ * that is passed the variant that contains the property's value when:
+ *
+ * 1) The property is read when the class is constructed, if
+ * the property is on D-Bus at the time.
+ * 2) The property changes (via a property changed signal).
+ * 3) An interfacesAdded signal is received with that property.
+ *
+ * The DataInterface class is used to access D-Bus, and is a template
+ * to avoid any circular include issues as that class is one of the
+ * users of this one.
+ */
+template <typename DataIface>
+class PropertyWatcher : public DBusWatcher
+{
+ public:
+ PropertyWatcher() = delete;
+ ~PropertyWatcher() = default;
+ PropertyWatcher(const PropertyWatcher&) = delete;
+ PropertyWatcher& operator=(const PropertyWatcher&) = delete;
+ PropertyWatcher(PropertyWatcher&&) = delete;
+ PropertyWatcher& operator=(PropertyWatcher&&) = delete;
+
+ using PropertySetFunc = std::function<void(const DBusValue&)>;
+
+ /**
+ * @brief Constructor
+ *
+ * Reads the property if it is on D-Bus, and sets up the match
+ * objects for the propertiesChanged and interfacesAdded signals.
+ *
+ * @param[in] bus - The sdbusplus bus object
+ * @param[in] path - The D-Bus path of the property
+ * @param[in] interface - The D-Bus interface that contains the property
+ * @param[in] propertyName - The property name
+ * @param[in] dataIface - The DataInterface object
+ * @param[in] func - The callback used any time the property is read
+ */
+ PropertyWatcher(sdbusplus::bus::bus& bus, const std::string& path,
+ const std::string& interface,
+ const std::string& propertyName, const DataIface& dataIface,
+ PropertySetFunc func) :
+ DBusWatcher(path, interface),
+ _name(propertyName), _setFunc(func)
+ {
+ try
+ {
+ read(dataIface);
+ }
+ catch (const SdBusError& e)
+ {
+ // Path doesn't exist now
+ }
+
+ _matches.emplace_back(
+ bus, match_rules::propertiesChanged(_path, _interface),
+ std::bind(std::mem_fn(&PropertyWatcher::propChanged), this,
+ std::placeholders::_1));
+
+ _matches.emplace_back(
+ bus,
+ match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
+ std::bind(std::mem_fn(&PropertyWatcher::interfaceAdded), this,
+ std::placeholders::_1));
+ }
+
+ /**
+ * @brief Reads the property on D-Bus, and calls
+ * the user defined function with the value.
+ *
+ * @param[in] dataIface - The DataInterface object
+ */
+ void read(const DataIface& dataIface)
+ {
+ auto service = dataIface.getService(_path, _interface);
+ if (!service.empty())
+ {
+ DBusValue value;
+ dataIface.getProperty(service, _path, _interface, _name, value);
+
+ _setFunc(value);
+ }
+ }
+
+ /**
+ * @brief The propertiesChanged callback
+ *
+ * Calls the user defined function with the property value
+ *
+ * @param[in] msg - The sdbusplus message object
+ */
+ void propChanged(sdbusplus::message::message& msg)
+ {
+ DBusInterface interface;
+ DBusPropertyMap properties;
+
+ msg.read(interface, properties);
+
+ auto prop = properties.find(_name);
+ if (prop != properties.end())
+ {
+ _setFunc(prop->second);
+ }
+ }
+
+ /**
+ * @brief The interfacesAdded callback
+ *
+ * Calls the user defined function with the property value
+ *
+ * @param[in] msg - The sdbusplus message object
+ */
+ void interfaceAdded(sdbusplus::message::message& msg)
+ {
+ sdbusplus::message::object_path path;
+ DBusInterfaceMap interfaces;
+
+ msg.read(path, interfaces);
+
+ auto iface = interfaces.find(_interface);
+ if (iface != interfaces.end())
+ {
+ auto prop = iface->second.find(_name);
+ if (prop != iface->second.end())
+ {
+ _setFunc(prop->second);
+ }
+ }
+ }
+
+ private:
+ /**
+ * @brief The D-Bus property name
+ */
+ std::string _name;
+
+ /**
+ * @brief The function that will be called any time the
+ * property is read.
+ */
+ PropertySetFunc _setFunc;
+};
+
+/**
+ * @class InterfaceWatcher
+ *
+ * This class allows the user to be kept up to data with a D-Bus
+ * interface's properties.. It does this by calling a user specified
+ * function that is passed a map of the D-Bus property names and values
+ * on that interface when:
+ *
+ * 1) The interface is read when the class is constructed, if
+ * the interface is on D-Bus at the time.
+ * 2) The interface has a property that changes (via a property changed signal).
+ * 3) An interfacesAdded signal is received.
+ *
+ * The DataInterface class is used to access D-Bus, and is a template
+ * to avoid any circular include issues as that class is one of the
+ * users of this one.
+ */
+template <typename DataIface>
+class InterfaceWatcher : public DBusWatcher
+{
+ public:
+ InterfaceWatcher() = delete;
+ ~InterfaceWatcher() = default;
+ InterfaceWatcher(const InterfaceWatcher&) = delete;
+ InterfaceWatcher& operator=(const InterfaceWatcher&) = delete;
+ InterfaceWatcher(InterfaceWatcher&&) = delete;
+ InterfaceWatcher& operator=(InterfaceWatcher&&) = delete;
+
+ using InterfaceSetFunc = std::function<void(const DBusPropertyMap&)>;
+
+ /**
+ * @brief Constructor
+ *
+ * Reads all properties on the interface if it is on D-Bus,
+ * and sets up the match objects for the propertiesChanged
+ * and interfacesAdded signals.
+ *
+ * @param[in] bus - The sdbusplus bus object
+ * @param[in] path - The D-Bus path of the property
+ * @param[in] interface - The D-Bus interface that contains the property
+ * @param[in] dataIface - The DataInterface object
+ * @param[in] func - The callback used any time the property is read
+ */
+ InterfaceWatcher(sdbusplus::bus::bus& bus, const std::string& path,
+ const std::string& interface, const DataIface& dataIface,
+ InterfaceSetFunc func) :
+ DBusWatcher(path, interface),
+ _setFunc(func)
+ {
+ try
+ {
+ read(dataIface);
+ }
+ catch (const SdBusError& e)
+ {
+ // Path doesn't exist now
+ }
+
+ _matches.emplace_back(
+ bus, match_rules::propertiesChanged(_path, _interface),
+ std::bind(std::mem_fn(&InterfaceWatcher::propChanged), this,
+ std::placeholders::_1));
+
+ _matches.emplace_back(
+ bus,
+ match_rules::interfacesAdded() + match_rules::argNpath(0, _path),
+ std::bind(std::mem_fn(&InterfaceWatcher::interfaceAdded), this,
+ std::placeholders::_1));
+ }
+
+ /**
+ * @brief Reads the interface's properties on D-Bus, and
+ * calls the the user defined function with the property map.
+ *
+ * @param[in] dataIface - The DataInterface object
+ */
+ void read(const DataIface& dataIface)
+ {
+ auto service = dataIface.getService(_path, _interface);
+ if (!service.empty())
+ {
+ auto properties =
+ dataIface.getAllProperties(service, _path, _interface);
+
+ _setFunc(properties);
+ }
+ }
+
+ /**
+ * @brief The propertiesChanged callback
+ *
+ * Calls the user defined function with the property map. Only the
+ * properties that changed will be in the map.
+ *
+ * @param[in] msg - The sdbusplus message object
+ */
+ void propChanged(sdbusplus::message::message& msg)
+ {
+ DBusInterface interface;
+ DBusPropertyMap properties;
+
+ msg.read(interface, properties);
+
+ _setFunc(properties);
+ }
+
+ /**
+ * @brief The interfacesAdded callback
+ *
+ * Calls the user defined function with the property map
+ *
+ * @param[in] msg - The sdbusplus message object
+ */
+ void interfaceAdded(sdbusplus::message::message& msg)
+ {
+ sdbusplus::message::object_path path;
+ DBusInterfaceMap interfaces;
+
+ msg.read(path, interfaces);
+
+ auto iface = interfaces.find(_interface);
+ if (iface != interfaces.end())
+ {
+ _setFunc(iface->second);
+ }
+ }
+
+ private:
+ /**
+ * @brief The function that will be called any time the
+ * interface is read.
+ */
+ InterfaceSetFunc _setFunc;
+};
+
+} // namespace openpower::pels