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/include/dbus_utility.hpp b/include/dbus_utility.hpp
new file mode 100644
index 0000000..e527e90
--- /dev/null
+++ b/include/dbus_utility.hpp
@@ -0,0 +1,87 @@
+/*
+ // Copyright (c) 2018 Intel Corporation
+ //
+ // Licensed under the Apache License, Version 2.0 (the "License");
+ // you may not use this file except in compliance with the License.
+ // You may obtain a copy of the License at
+ //
+ // http://www.apache.org/licenses/LICENSE-2.0
+ //
+ // Unless required by applicable law or agreed to in writing, software
+ // distributed under the License is distributed on an "AS IS" BASIS,
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ // See the License for the specific language governing permissions and
+ // limitations under the License.
+ */
+#pragma once
+
+#include <regex>
+#include <sdbusplus/message.hpp>
+
+namespace dbus
+{
+
+namespace utility
+{
+
+using DbusVariantType = sdbusplus::message::variant<
+ std::vector<std::tuple<std::string, std::string, std::string>>,
+ std::vector<std::string>, std::string, int64_t, uint64_t, double, int32_t,
+ uint32_t, int16_t, uint16_t, uint8_t, bool>;
+
+using ManagedObjectType = std::vector<
+ std::pair<sdbusplus::message::object_path,
+ boost::container::flat_map<
+ std::string,
+ boost::container::flat_map<std::string, DbusVariantType>>>>;
+
+inline void escapePathForDbus(std::string& path)
+{
+ const std::regex reg("[^A-Za-z0-9_/]");
+ std::regex_replace(path.begin(), path.begin(), path.end(), reg, "_");
+}
+
+// gets the string N strings deep into a path
+// i.e. /0th/1st/2nd/3rd
+inline bool getNthStringFromPath(const std::string& path, int index,
+ std::string& result)
+{
+ int count = 0;
+ auto first = path.begin();
+ auto last = path.end();
+ for (auto it = path.begin(); it < path.end(); it++)
+ {
+ // skip first character as it's either a leading slash or the first
+ // character in the word
+ if (it == path.begin())
+ {
+ continue;
+ }
+ if (*it == '/')
+ {
+ count++;
+ if (count == index)
+ {
+ first = it;
+ }
+ if (count == index + 1)
+ {
+ last = it;
+ break;
+ }
+ }
+ }
+ if (count < index)
+ {
+ return false;
+ }
+ if (first != path.begin())
+ {
+ first++;
+ }
+ result = path.substr(first - path.begin(), last - first);
+ return true;
+}
+
+} // namespace utility
+} // namespace dbus
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index 110e6ac..aa6c95f 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -1,5 +1,18 @@
-#pragma once
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
#include <crow/app.h>
#include <tinyxml2.h>
@@ -7,6 +20,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/container/flat_set.hpp>
#include <dbus_singleton.hpp>
+#include <dbus_utility.hpp>
#include <experimental/filesystem>
#include <fstream>
@@ -77,19 +91,6 @@
"Introspect");
}
-// A smattering of common types to unpack. TODO(ed) this should really iterate
-// the sdbusplus object directly and build the json response
-using DbusRestVariantType = sdbusplus::message::variant<
- std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
- int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
- bool>;
-
-using ManagedObjectType = std::vector<std::pair<
- sdbusplus::message::object_path,
- boost::container::flat_map<
- std::string,
- boost::container::flat_map<std::string, DbusRestVariantType>>>>;
-
void getManagedObjectsForEnumerate(const std::string &object_name,
const std::string &object_manager_path,
const std::string &connection_name,
@@ -97,9 +98,8 @@
std::shared_ptr<nlohmann::json> transaction)
{
crow::connections::systemBus->async_method_call(
- [&res, transaction, object_name{std::string(object_name)}](
- const boost::system::error_code ec,
- const ManagedObjectType &objects) {
+ [&res, transaction](const boost::system::error_code ec,
+ const dbus::utility::ManagedObjectType &objects) {
if (ec)
{
BMCWEB_LOG_ERROR << ec;
@@ -840,8 +840,8 @@
crow::connections::systemBus->async_method_call(
[&res, response, propertyName](
const boost::system::error_code ec,
- const std::vector<
- std::pair<std::string, DbusRestVariantType>>
+ const std::vector<std::pair<
+ std::string, dbus::utility::DbusVariantType>>
&properties) {
if (ec)
{
@@ -850,8 +850,9 @@
}
else
{
- for (const std::pair<std::string,
- DbusRestVariantType>
+ for (const std::pair<
+ std::string,
+ dbus::utility::DbusVariantType>
&property : properties)
{
// if property name is empty, or matches our
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