Add PID Get To Redfish
Add doGet to managers for PID configuration data.
Make sure passes schema validation.
Change-Id: Ieeb97bf76a3d8a3c06f59f79cc0887aec746675e
Signed-off-by: James Feist <james.feist@linux.intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index ce21406..1fb2f69 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -17,31 +17,280 @@
#include "node.hpp"
+#include <boost/algorithm/string/replace.hpp>
+#include <dbus_utility.hpp>
+
namespace redfish
{
+static constexpr const char* objectManagerIface =
+ "org.freedesktop.DBus.ObjectManager";
+static constexpr const char* pidConfigurationIface =
+ "xyz.openbmc_project.Configuration.Pid";
+static constexpr const char* pidZoneConfigurationIface =
+ "xyz.openbmc_project.Configuration.Pid.Zone";
-/**
- * DBus types primitives for several generic DBus interfaces
- * TODO consider move this to separate file into boost::dbus
- */
-using GetManagedObjectsType = boost::container::flat_map<
- sdbusplus::message::object_path,
- boost::container::flat_map<
- std::string,
- boost::container::flat_map<
- std::string, sdbusplus::message::variant<
- std::string, bool, uint8_t, int16_t, uint16_t,
- int32_t, uint32_t, int64_t, uint64_t, double>>>>;
+static void asyncPopulatePid(const std::string& connection,
+ const std::string& path,
+ std::shared_ptr<AsyncResp> asyncResp)
+{
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+ const dbus::utility::ManagedObjectType& managedObj) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << ec;
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ asyncResp->res.jsonValue.clear();
+ return;
+ }
+ nlohmann::json& configRoot =
+ asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
+ nlohmann::json& fans = configRoot["FanControllers"];
+ fans["@odata.type"] = "#OemManager.FanControllers";
+ fans["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.FanControllers";
+ fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
+ "Fan/FanControllers";
+
+ nlohmann::json& pids = configRoot["PidControllers"];
+ pids["@odata.type"] = "#OemManager.PidControllers";
+ pids["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.PidControllers";
+ pids["@odata.id"] =
+ "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
+
+ nlohmann::json& zones = configRoot["FanZones"];
+ zones["@odata.id"] =
+ "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
+ zones["@odata.type"] = "#OemManager.FanZones";
+ zones["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.FanZones";
+ configRoot["@odata.id"] =
+ "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
+ configRoot["@odata.type"] = "#OemManager.Fan";
+ configRoot["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.Fan";
+
+ bool propertyError = false;
+ for (const auto& pathPair : managedObj)
+ {
+ for (const auto& intfPair : pathPair.second)
+ {
+ if (intfPair.first != pidConfigurationIface &&
+ intfPair.first != pidZoneConfigurationIface)
+ {
+ continue;
+ }
+ auto findName = intfPair.second.find("Name");
+ if (findName == intfPair.second.end())
+ {
+ BMCWEB_LOG_ERROR << "Pid Field missing Name";
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+ const std::string* namePtr =
+ mapbox::getPtr<const std::string>(findName->second);
+ if (namePtr == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Pid Name Field illegal";
+ return;
+ }
+
+ std::string name = *namePtr;
+ dbus::utility::escapePathForDbus(name);
+ if (intfPair.first == pidZoneConfigurationIface)
+ {
+ std::string chassis;
+ if (!dbus::utility::getNthStringFromPath(
+ pathPair.first.str, 5, chassis))
+ {
+ chassis = "#IllegalValue";
+ }
+ nlohmann::json& zone = zones[name];
+ zone["Chassis"] = {
+ {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
+ zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
+ "OpenBmc/Fan/FanZones/" +
+ name;
+ zone["@odata.type"] = "#OemManager.FanZone";
+ zone["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.FanZone";
+ }
+
+ for (const auto& propertyPair : intfPair.second)
+ {
+ if (propertyPair.first == "Type" ||
+ propertyPair.first == "Class" ||
+ propertyPair.first == "Name")
+ {
+ continue;
+ }
+
+ // zones
+ if (intfPair.first == pidZoneConfigurationIface)
+ {
+ const double* ptr = mapbox::getPtr<const double>(
+ propertyPair.second);
+ if (ptr == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Field Illegal "
+ << propertyPair.first;
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ zones[name][propertyPair.first] = *ptr;
+ }
+
+ // pid and fans are off the same configuration
+ if (intfPair.first == pidConfigurationIface)
+ {
+ const std::string* classPtr = nullptr;
+ auto findClass = intfPair.second.find("Class");
+ if (findClass != intfPair.second.end())
+ {
+ classPtr = mapbox::getPtr<const std::string>(
+ findClass->second);
+ }
+ if (classPtr == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Pid Class Field illegal";
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ bool isFan = *classPtr == "fan";
+ nlohmann::json& element =
+ isFan ? fans[name] : pids[name];
+ if (isFan)
+ {
+ element["@odata.id"] =
+ "/redfish/v1/Managers/bmc#/Oem/"
+ "OpenBmc/Fan/FanControllers/" +
+ std::string(name);
+ element["@odata.type"] =
+ "#OemManager.FanController";
+
+ element["@odata.context"] =
+ "/redfish/v1/"
+ "$metadata#OemManager.FanController";
+ }
+ else
+ {
+ element["@odata.id"] =
+ "/redfish/v1/Managers/bmc#/Oem/"
+ "OpenBmc/Fan/PidControllers/" +
+ std::string(name);
+ element["@odata.type"] =
+ "#OemManager.PidController";
+ element["@odata.context"] =
+ "/redfish/v1/$metadata"
+ "#OemManager.PidController";
+ }
+
+ if (propertyPair.first == "Zones")
+ {
+ const std::vector<std::string>* inputs =
+ mapbox::getPtr<
+ const std::vector<std::string>>(
+ propertyPair.second);
+
+ if (inputs == nullptr)
+ {
+ BMCWEB_LOG_ERROR
+ << "Zones Pid Field Illegal";
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ auto& data = element[propertyPair.first];
+ data = nlohmann::json::array();
+ for (std::string itemCopy : *inputs)
+ {
+ dbus::utility::escapePathForDbus(itemCopy);
+ data.push_back(
+ {{"@odata.id",
+ "/redfish/v1/Managers/bmc#/Oem/"
+ "OpenBmc/Fan/FanZones/" +
+ itemCopy}});
+ }
+ }
+ // todo(james): may never happen, but this
+ // assumes configuration data referenced in the
+ // PID config is provided by the same daemon, we
+ // could add another loop to cover all cases,
+ // but I'm okay kicking this can down the road a
+ // bit
+
+ else if (propertyPair.first == "Inputs" ||
+ propertyPair.first == "Outputs")
+ {
+ auto& data = element[propertyPair.first];
+ const std::vector<std::string>* inputs =
+ mapbox::getPtr<
+ const std::vector<std::string>>(
+ propertyPair.second);
+
+ if (inputs == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Field Illegal "
+ << propertyPair.first;
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ data = *inputs;
+ } // doubles
+ else if (propertyPair.first ==
+ "FFGainCoefficient" ||
+ propertyPair.first == "FFOffCoefficient" ||
+ propertyPair.first == "ICoefficient" ||
+ propertyPair.first == "ILimitMax" ||
+ propertyPair.first == "ILimitMin" ||
+ propertyPair.first == "OutLimitMax" ||
+ propertyPair.first == "OutLimitMin" ||
+ propertyPair.first == "PCoefficient" ||
+ propertyPair.first == "SlewNeg" ||
+ propertyPair.first == "SlewPos")
+ {
+ const double* ptr =
+ mapbox::getPtr<const double>(
+ propertyPair.second);
+ if (ptr == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Field Illegal "
+ << propertyPair.first;
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ element[propertyPair.first] = *ptr;
+ }
+ }
+ }
+ }
+ }
+ },
+ connection, path, objectManagerIface, "GetManagedObjects");
+}
class Manager : public Node
{
public:
- Manager(CrowApp &app) : Node(app, "/redfish/v1/Managers/openbmc/")
+ Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
{
- Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc";
+ Node::json["@odata.id"] = "/redfish/v1/Managers/bmc";
Node::json["@odata.type"] = "#Manager.v1_3_0.Manager";
Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager";
- Node::json["Id"] = "openbmc";
+ Node::json["Id"] = "bmc";
Node::json["Name"] = "OpenBmc Manager";
Node::json["Description"] = "Baseboard Management Controller";
Node::json["PowerState"] = "On";
@@ -51,7 +300,7 @@
.systemUuid;
Node::json["Model"] = "OpenBmc"; // TODO(ed), get model
Node::json["EthernetInterfaces"] = {
- {"@odata.id", "/redfish/v1/Managers/openbmc/EthernetInterfaces"}};
+ {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
@@ -60,20 +309,89 @@
{boost::beast::http::verb::put, {{"ConfigureManager"}}},
{boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
{boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+
+ // default oem data
+ nlohmann::json& oem = Node::json["Oem"];
+ nlohmann::json& oemOpenbmc = oem["OpenBmc"];
+ oem["@odata.type"] = "#OemManager.Oem";
+ oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
+ oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
+ oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
+ oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
+ oemOpenbmc["@odata.context"] =
+ "/redfish/v1/$metadata#OemManager.OpenBmc";
}
private:
- void doGet(crow::Response &res, const crow::Request &req,
- const std::vector<std::string> ¶ms) override
+ void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
+ {
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+ const crow::openbmc_mapper::GetSubTreeType& subtree) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << ec;
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+
+ // create map of <connection, path to objMgr>>
+ boost::container::flat_map<std::string, std::string>
+ objectMgrPaths;
+ for (const auto& pathGroup : subtree)
+ {
+ for (const auto& connectionGroup : pathGroup.second)
+ {
+ for (const std::string& interface :
+ connectionGroup.second)
+ {
+ if (interface == objectManagerIface)
+ {
+ objectMgrPaths[connectionGroup.first] =
+ pathGroup.first;
+ }
+ // this list is alphabetical, so we
+ // should have found the objMgr by now
+ if (interface == pidConfigurationIface ||
+ interface == pidZoneConfigurationIface)
+ {
+ auto findObjMgr =
+ objectMgrPaths.find(connectionGroup.first);
+ if (findObjMgr == objectMgrPaths.end())
+ {
+ BMCWEB_LOG_DEBUG << connectionGroup.first
+ << "Has no Object Manager";
+ continue;
+ }
+ asyncPopulatePid(findObjMgr->first,
+ findObjMgr->second, asyncResp);
+ break;
+ }
+ }
+ }
+ }
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
+ std::array<const char*, 3>{pidConfigurationIface,
+ pidZoneConfigurationIface,
+ objectManagerIface});
+ }
+
+ void doGet(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
{
std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
asyncResp->res.jsonValue = Node::json;
Node::json["DateTime"] = getDateTime();
res.jsonValue = Node::json;
+
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec,
- const GetManagedObjectsType &resp) {
+ const dbus::utility::ManagedObjectType& resp) {
if (ec)
{
BMCWEB_LOG_ERROR << "Error while getting Software Version";
@@ -82,9 +400,9 @@
return;
}
- for (auto &objpath : resp)
+ for (auto& objpath : resp)
{
- for (auto &interface : objpath.second)
+ for (auto& interface : objpath.second)
{
// If interface is xyz.openbmc_project.Software.Version,
// this is what we're looking for.
@@ -92,12 +410,12 @@
"xyz.openbmc_project.Software.Version")
{
// Cut out everyting until last "/", ...
- const std::string &iface_id = objpath.first;
- for (auto &property : interface.second)
+ const std::string& iface_id = objpath.first;
+ for (auto& property : interface.second)
{
if (property.first == "Version")
{
- const std::string *value =
+ const std::string* value =
mapbox::getPtr<const std::string>(
property.second);
if (value == nullptr)
@@ -115,6 +433,12 @@
"xyz.openbmc_project.Software.BMC.Updater",
"/xyz/openbmc_project/software",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+ getPidValues(asyncResp);
+ }
+
+ void doPatch(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
+ {
}
std::string getDateTime() const
@@ -138,7 +462,7 @@
class ManagerCollection : public Node
{
public:
- ManagerCollection(CrowApp &app) : Node(app, "/redfish/v1/Managers/")
+ ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
{
Node::json["@odata.id"] = "/redfish/v1/Managers";
Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection";
@@ -146,8 +470,7 @@
"/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
Node::json["Name"] = "Manager Collection";
Node::json["Members@odata.count"] = 1;
- Node::json["Members"] = {
- {{"@odata.id", "/redfish/v1/Managers/openbmc"}}};
+ Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}};
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
@@ -159,8 +482,8 @@
}
private:
- void doGet(crow::Response &res, const crow::Request &req,
- const std::vector<std::string> ¶ms) override
+ void doGet(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
{
// Collections don't include the static data added by SubRoute because
// it has a duplicate entry for members
@@ -171,9 +494,8 @@
res.jsonValue["Name"] = "Manager Collection";
res.jsonValue["Members@odata.count"] = 1;
res.jsonValue["Members"] = {
- {{"@odata.id", "/redfish/v1/Managers/openbmc"}}};
+ {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
res.end();
}
};
-
} // namespace redfish