xref: /openbmc/bmcweb/features/redfish/lib/managers.hpp (revision 26f0389959179b37bae5410dfd5a84831fbdaee6)
19c310685SBorawski.Lukasz /*
29c310685SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation
39c310685SBorawski.Lukasz //
49c310685SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License");
59c310685SBorawski.Lukasz // you may not use this file except in compliance with the License.
69c310685SBorawski.Lukasz // You may obtain a copy of the License at
79c310685SBorawski.Lukasz //
89c310685SBorawski.Lukasz //      http://www.apache.org/licenses/LICENSE-2.0
99c310685SBorawski.Lukasz //
109c310685SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software
119c310685SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS,
129c310685SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c310685SBorawski.Lukasz // See the License for the specific language governing permissions and
149c310685SBorawski.Lukasz // limitations under the License.
159c310685SBorawski.Lukasz */
169c310685SBorawski.Lukasz #pragma once
179c310685SBorawski.Lukasz 
189c310685SBorawski.Lukasz #include "node.hpp"
199c310685SBorawski.Lukasz 
205b4aa86bSJames Feist #include <boost/algorithm/string/replace.hpp>
21af5d6058SSantosh Puranik #include <boost/date_time.hpp>
225b4aa86bSJames Feist #include <dbus_utility.hpp>
23af5d6058SSantosh Puranik #include <sstream>
247bffdb7eSBernard Wong #include <utils/systemd_utils.hpp>
25abf2add6SEd Tanous #include <variant>
265b4aa86bSJames Feist 
271abe55efSEd Tanous namespace redfish
281abe55efSEd Tanous {
29ed5befbdSJennifer Lee 
30ed5befbdSJennifer Lee /**
31ed5befbdSJennifer Lee  * ManagerActionsReset class supports handle POST method for Reset action.
32ed5befbdSJennifer Lee  * The class retrieves and sends data directly to dbus.
33ed5befbdSJennifer Lee  */
34ed5befbdSJennifer Lee class ManagerActionsReset : public Node
35ed5befbdSJennifer Lee {
36ed5befbdSJennifer Lee   public:
37ed5befbdSJennifer Lee     ManagerActionsReset(CrowApp& app) :
38ed5befbdSJennifer Lee         Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
39ed5befbdSJennifer Lee     {
40ed5befbdSJennifer Lee         entityPrivileges = {
41ed5befbdSJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
42ed5befbdSJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
43ed5befbdSJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
44ed5befbdSJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
45ed5befbdSJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
46ed5befbdSJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
47ed5befbdSJennifer Lee     }
48ed5befbdSJennifer Lee 
49ed5befbdSJennifer Lee   private:
50ed5befbdSJennifer Lee     /**
51ed5befbdSJennifer Lee      * Function handles POST method request.
52ed5befbdSJennifer Lee      * Analyzes POST body message before sends Reset request data to dbus.
53ed5befbdSJennifer Lee      * OpenBMC allows for ResetType is GracefulRestart only.
54ed5befbdSJennifer Lee      */
55ed5befbdSJennifer Lee     void doPost(crow::Response& res, const crow::Request& req,
56ed5befbdSJennifer Lee                 const std::vector<std::string>& params) override
57ed5befbdSJennifer Lee     {
58ed5befbdSJennifer Lee         std::string resetType;
59ed5befbdSJennifer Lee 
60ed5befbdSJennifer Lee         if (!json_util::readJson(req, res, "ResetType", resetType))
61ed5befbdSJennifer Lee         {
62ed5befbdSJennifer Lee             return;
63ed5befbdSJennifer Lee         }
64ed5befbdSJennifer Lee 
65ed5befbdSJennifer Lee         if (resetType != "GracefulRestart")
66ed5befbdSJennifer Lee         {
67ed5befbdSJennifer Lee             res.result(boost::beast::http::status::bad_request);
68ed5befbdSJennifer Lee             messages::actionParameterNotSupported(res, resetType, "ResetType");
69ed5befbdSJennifer Lee             BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
70ed5befbdSJennifer Lee                              << resetType;
71ed5befbdSJennifer Lee             res.end();
72ed5befbdSJennifer Lee             return;
73ed5befbdSJennifer Lee         }
74ed5befbdSJennifer Lee         doBMCGracefulRestart(res, req, params);
75ed5befbdSJennifer Lee     }
76ed5befbdSJennifer Lee 
77ed5befbdSJennifer Lee     /**
78ed5befbdSJennifer Lee      * Function transceives data with dbus directly.
79ed5befbdSJennifer Lee      * All BMC state properties will be retrieved before sending reset request.
80ed5befbdSJennifer Lee      */
81ed5befbdSJennifer Lee     void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
82ed5befbdSJennifer Lee                               const std::vector<std::string>& params)
83ed5befbdSJennifer Lee     {
84ed5befbdSJennifer Lee         const char* processName = "xyz.openbmc_project.State.BMC";
85ed5befbdSJennifer Lee         const char* objectPath = "/xyz/openbmc_project/state/bmc0";
86ed5befbdSJennifer Lee         const char* interfaceName = "xyz.openbmc_project.State.BMC";
87ed5befbdSJennifer Lee         const std::string& propertyValue =
88ed5befbdSJennifer Lee             "xyz.openbmc_project.State.BMC.Transition.Reboot";
89ed5befbdSJennifer Lee         const char* destProperty = "RequestedBMCTransition";
90ed5befbdSJennifer Lee 
91ed5befbdSJennifer Lee         // Create the D-Bus variant for D-Bus call.
92ed5befbdSJennifer Lee         VariantType dbusPropertyValue(propertyValue);
93ed5befbdSJennifer Lee 
94ed5befbdSJennifer Lee         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
95ed5befbdSJennifer Lee 
96ed5befbdSJennifer Lee         crow::connections::systemBus->async_method_call(
97ed5befbdSJennifer Lee             [asyncResp](const boost::system::error_code ec) {
98ed5befbdSJennifer Lee                 // Use "Set" method to set the property value.
99ed5befbdSJennifer Lee                 if (ec)
100ed5befbdSJennifer Lee                 {
101ed5befbdSJennifer Lee                     BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
102ed5befbdSJennifer Lee                     messages::internalError(asyncResp->res);
103ed5befbdSJennifer Lee                     return;
104ed5befbdSJennifer Lee                 }
105ed5befbdSJennifer Lee 
106ed5befbdSJennifer Lee                 messages::success(asyncResp->res);
107ed5befbdSJennifer Lee             },
108ed5befbdSJennifer Lee             processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
109ed5befbdSJennifer Lee             interfaceName, destProperty, dbusPropertyValue);
110ed5befbdSJennifer Lee     }
111ed5befbdSJennifer Lee };
112ed5befbdSJennifer Lee 
1135b4aa86bSJames Feist static constexpr const char* objectManagerIface =
1145b4aa86bSJames Feist     "org.freedesktop.DBus.ObjectManager";
1155b4aa86bSJames Feist static constexpr const char* pidConfigurationIface =
1165b4aa86bSJames Feist     "xyz.openbmc_project.Configuration.Pid";
1175b4aa86bSJames Feist static constexpr const char* pidZoneConfigurationIface =
1185b4aa86bSJames Feist     "xyz.openbmc_project.Configuration.Pid.Zone";
119b7a08d04SJames Feist static constexpr const char* stepwiseConfigurationIface =
120b7a08d04SJames Feist     "xyz.openbmc_project.Configuration.Stepwise";
1219c310685SBorawski.Lukasz 
1225b4aa86bSJames Feist static void asyncPopulatePid(const std::string& connection,
1235b4aa86bSJames Feist                              const std::string& path,
1245b4aa86bSJames Feist                              std::shared_ptr<AsyncResp> asyncResp)
1255b4aa86bSJames Feist {
1265b4aa86bSJames Feist 
1275b4aa86bSJames Feist     crow::connections::systemBus->async_method_call(
1285b4aa86bSJames Feist         [asyncResp](const boost::system::error_code ec,
1295b4aa86bSJames Feist                     const dbus::utility::ManagedObjectType& managedObj) {
1305b4aa86bSJames Feist             if (ec)
1315b4aa86bSJames Feist             {
1325b4aa86bSJames Feist                 BMCWEB_LOG_ERROR << ec;
1335b4aa86bSJames Feist                 asyncResp->res.jsonValue.clear();
134f12894f8SJason M. Bills                 messages::internalError(asyncResp->res);
1355b4aa86bSJames Feist                 return;
1365b4aa86bSJames Feist             }
1375b4aa86bSJames Feist             nlohmann::json& configRoot =
1385b4aa86bSJames Feist                 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
1395b4aa86bSJames Feist             nlohmann::json& fans = configRoot["FanControllers"];
1405b4aa86bSJames Feist             fans["@odata.type"] = "#OemManager.FanControllers";
1415b4aa86bSJames Feist             fans["@odata.context"] =
1425b4aa86bSJames Feist                 "/redfish/v1/$metadata#OemManager.FanControllers";
1435b4aa86bSJames Feist             fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
1445b4aa86bSJames Feist                                 "Fan/FanControllers";
1455b4aa86bSJames Feist 
1465b4aa86bSJames Feist             nlohmann::json& pids = configRoot["PidControllers"];
1475b4aa86bSJames Feist             pids["@odata.type"] = "#OemManager.PidControllers";
1485b4aa86bSJames Feist             pids["@odata.context"] =
1495b4aa86bSJames Feist                 "/redfish/v1/$metadata#OemManager.PidControllers";
1505b4aa86bSJames Feist             pids["@odata.id"] =
1515b4aa86bSJames Feist                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
1525b4aa86bSJames Feist 
153b7a08d04SJames Feist             nlohmann::json& stepwise = configRoot["StepwiseControllers"];
154b7a08d04SJames Feist             stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
155b7a08d04SJames Feist             stepwise["@odata.context"] =
156b7a08d04SJames Feist                 "/redfish/v1/$metadata#OemManager.StepwiseControllers";
157b7a08d04SJames Feist             stepwise["@odata.id"] =
158b7a08d04SJames Feist                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
159b7a08d04SJames Feist 
1605b4aa86bSJames Feist             nlohmann::json& zones = configRoot["FanZones"];
1615b4aa86bSJames Feist             zones["@odata.id"] =
1625b4aa86bSJames Feist                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
1635b4aa86bSJames Feist             zones["@odata.type"] = "#OemManager.FanZones";
1645b4aa86bSJames Feist             zones["@odata.context"] =
1655b4aa86bSJames Feist                 "/redfish/v1/$metadata#OemManager.FanZones";
1665b4aa86bSJames Feist             configRoot["@odata.id"] =
1675b4aa86bSJames Feist                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
1685b4aa86bSJames Feist             configRoot["@odata.type"] = "#OemManager.Fan";
1695b4aa86bSJames Feist             configRoot["@odata.context"] =
1705b4aa86bSJames Feist                 "/redfish/v1/$metadata#OemManager.Fan";
1715b4aa86bSJames Feist 
1725b4aa86bSJames Feist             for (const auto& pathPair : managedObj)
1735b4aa86bSJames Feist             {
1745b4aa86bSJames Feist                 for (const auto& intfPair : pathPair.second)
1755b4aa86bSJames Feist                 {
1765b4aa86bSJames Feist                     if (intfPair.first != pidConfigurationIface &&
177b7a08d04SJames Feist                         intfPair.first != pidZoneConfigurationIface &&
178b7a08d04SJames Feist                         intfPair.first != stepwiseConfigurationIface)
1795b4aa86bSJames Feist                     {
1805b4aa86bSJames Feist                         continue;
1815b4aa86bSJames Feist                     }
1825b4aa86bSJames Feist                     auto findName = intfPair.second.find("Name");
1835b4aa86bSJames Feist                     if (findName == intfPair.second.end())
1845b4aa86bSJames Feist                     {
1855b4aa86bSJames Feist                         BMCWEB_LOG_ERROR << "Pid Field missing Name";
186a08b46ccSJason M. Bills                         messages::internalError(asyncResp->res);
1875b4aa86bSJames Feist                         return;
1885b4aa86bSJames Feist                     }
1895b4aa86bSJames Feist                     const std::string* namePtr =
190abf2add6SEd Tanous                         std::get_if<std::string>(&findName->second);
1915b4aa86bSJames Feist                     if (namePtr == nullptr)
1925b4aa86bSJames Feist                     {
1935b4aa86bSJames Feist                         BMCWEB_LOG_ERROR << "Pid Name Field illegal";
194b7a08d04SJames Feist                         messages::internalError(asyncResp->res);
1955b4aa86bSJames Feist                         return;
1965b4aa86bSJames Feist                     }
1975b4aa86bSJames Feist 
1985b4aa86bSJames Feist                     std::string name = *namePtr;
1995b4aa86bSJames Feist                     dbus::utility::escapePathForDbus(name);
200b7a08d04SJames Feist                     nlohmann::json* config = nullptr;
201c33a90ecSJames Feist 
202c33a90ecSJames Feist                     const std::string* classPtr = nullptr;
203c33a90ecSJames Feist                     auto findClass = intfPair.second.find("Class");
204c33a90ecSJames Feist                     if (findClass != intfPair.second.end())
205c33a90ecSJames Feist                     {
206c33a90ecSJames Feist                         classPtr = std::get_if<std::string>(&findClass->second);
207c33a90ecSJames Feist                     }
208c33a90ecSJames Feist 
2095b4aa86bSJames Feist                     if (intfPair.first == pidZoneConfigurationIface)
2105b4aa86bSJames Feist                     {
2115b4aa86bSJames Feist                         std::string chassis;
2125b4aa86bSJames Feist                         if (!dbus::utility::getNthStringFromPath(
2135b4aa86bSJames Feist                                 pathPair.first.str, 5, chassis))
2145b4aa86bSJames Feist                         {
2155b4aa86bSJames Feist                             chassis = "#IllegalValue";
2165b4aa86bSJames Feist                         }
2175b4aa86bSJames Feist                         nlohmann::json& zone = zones[name];
2185b4aa86bSJames Feist                         zone["Chassis"] = {
2195b4aa86bSJames Feist                             {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
2205b4aa86bSJames Feist                         zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
2215b4aa86bSJames Feist                                             "OpenBmc/Fan/FanZones/" +
2225b4aa86bSJames Feist                                             name;
2235b4aa86bSJames Feist                         zone["@odata.type"] = "#OemManager.FanZone";
2245b4aa86bSJames Feist                         zone["@odata.context"] =
2255b4aa86bSJames Feist                             "/redfish/v1/$metadata#OemManager.FanZone";
226b7a08d04SJames Feist                         config = &zone;
2275b4aa86bSJames Feist                     }
2285b4aa86bSJames Feist 
229b7a08d04SJames Feist                     else if (intfPair.first == stepwiseConfigurationIface)
2305b4aa86bSJames Feist                     {
231c33a90ecSJames Feist                         if (classPtr == nullptr)
232c33a90ecSJames Feist                         {
233c33a90ecSJames Feist                             BMCWEB_LOG_ERROR << "Pid Class Field illegal";
234c33a90ecSJames Feist                             messages::internalError(asyncResp->res);
235c33a90ecSJames Feist                             return;
236c33a90ecSJames Feist                         }
237c33a90ecSJames Feist 
238b7a08d04SJames Feist                         nlohmann::json& controller = stepwise[name];
239b7a08d04SJames Feist                         config = &controller;
2405b4aa86bSJames Feist 
241b7a08d04SJames Feist                         controller["@odata.id"] =
242b7a08d04SJames Feist                             "/redfish/v1/Managers/bmc#/Oem/"
243b7a08d04SJames Feist                             "OpenBmc/Fan/StepwiseControllers/" +
244b7a08d04SJames Feist                             std::string(name);
245b7a08d04SJames Feist                         controller["@odata.type"] =
246b7a08d04SJames Feist                             "#OemManager.StepwiseController";
247b7a08d04SJames Feist 
248b7a08d04SJames Feist                         controller["@odata.context"] =
249b7a08d04SJames Feist                             "/redfish/v1/"
250b7a08d04SJames Feist                             "$metadata#OemManager.StepwiseController";
251c33a90ecSJames Feist                         controller["Direction"] = *classPtr;
2525b4aa86bSJames Feist                     }
2535b4aa86bSJames Feist 
2545b4aa86bSJames Feist                     // pid and fans are off the same configuration
255b7a08d04SJames Feist                     else if (intfPair.first == pidConfigurationIface)
2565b4aa86bSJames Feist                     {
257c33a90ecSJames Feist 
2585b4aa86bSJames Feist                         if (classPtr == nullptr)
2595b4aa86bSJames Feist                         {
2605b4aa86bSJames Feist                             BMCWEB_LOG_ERROR << "Pid Class Field illegal";
261a08b46ccSJason M. Bills                             messages::internalError(asyncResp->res);
2625b4aa86bSJames Feist                             return;
2635b4aa86bSJames Feist                         }
2645b4aa86bSJames Feist                         bool isFan = *classPtr == "fan";
2655b4aa86bSJames Feist                         nlohmann::json& element =
2665b4aa86bSJames Feist                             isFan ? fans[name] : pids[name];
267b7a08d04SJames Feist                         config = &element;
2685b4aa86bSJames Feist                         if (isFan)
2695b4aa86bSJames Feist                         {
2705b4aa86bSJames Feist                             element["@odata.id"] =
2715b4aa86bSJames Feist                                 "/redfish/v1/Managers/bmc#/Oem/"
2725b4aa86bSJames Feist                                 "OpenBmc/Fan/FanControllers/" +
2735b4aa86bSJames Feist                                 std::string(name);
2745b4aa86bSJames Feist                             element["@odata.type"] =
2755b4aa86bSJames Feist                                 "#OemManager.FanController";
2765b4aa86bSJames Feist 
2775b4aa86bSJames Feist                             element["@odata.context"] =
2785b4aa86bSJames Feist                                 "/redfish/v1/"
2795b4aa86bSJames Feist                                 "$metadata#OemManager.FanController";
2805b4aa86bSJames Feist                         }
2815b4aa86bSJames Feist                         else
2825b4aa86bSJames Feist                         {
2835b4aa86bSJames Feist                             element["@odata.id"] =
2845b4aa86bSJames Feist                                 "/redfish/v1/Managers/bmc#/Oem/"
2855b4aa86bSJames Feist                                 "OpenBmc/Fan/PidControllers/" +
2865b4aa86bSJames Feist                                 std::string(name);
2875b4aa86bSJames Feist                             element["@odata.type"] =
2885b4aa86bSJames Feist                                 "#OemManager.PidController";
2895b4aa86bSJames Feist                             element["@odata.context"] =
2905b4aa86bSJames Feist                                 "/redfish/v1/$metadata"
2915b4aa86bSJames Feist                                 "#OemManager.PidController";
2925b4aa86bSJames Feist                         }
293b7a08d04SJames Feist                     }
294b7a08d04SJames Feist                     else
295b7a08d04SJames Feist                     {
296b7a08d04SJames Feist                         BMCWEB_LOG_ERROR << "Unexpected configuration";
297b7a08d04SJames Feist                         messages::internalError(asyncResp->res);
298b7a08d04SJames Feist                         return;
299b7a08d04SJames Feist                     }
300b7a08d04SJames Feist 
301b7a08d04SJames Feist                     // used for making maps out of 2 vectors
302b7a08d04SJames Feist                     const std::vector<double>* keys = nullptr;
303b7a08d04SJames Feist                     const std::vector<double>* values = nullptr;
304b7a08d04SJames Feist 
305b7a08d04SJames Feist                     for (const auto& propertyPair : intfPair.second)
306b7a08d04SJames Feist                     {
307b7a08d04SJames Feist                         if (propertyPair.first == "Type" ||
308b7a08d04SJames Feist                             propertyPair.first == "Class" ||
309b7a08d04SJames Feist                             propertyPair.first == "Name")
310b7a08d04SJames Feist                         {
311b7a08d04SJames Feist                             continue;
312b7a08d04SJames Feist                         }
313b7a08d04SJames Feist 
314b7a08d04SJames Feist                         // zones
315b7a08d04SJames Feist                         if (intfPair.first == pidZoneConfigurationIface)
316b7a08d04SJames Feist                         {
317b7a08d04SJames Feist                             const double* ptr =
318abf2add6SEd Tanous                                 std::get_if<double>(&propertyPair.second);
319b7a08d04SJames Feist                             if (ptr == nullptr)
320b7a08d04SJames Feist                             {
321b7a08d04SJames Feist                                 BMCWEB_LOG_ERROR << "Field Illegal "
322b7a08d04SJames Feist                                                  << propertyPair.first;
323b7a08d04SJames Feist                                 messages::internalError(asyncResp->res);
324b7a08d04SJames Feist                                 return;
325b7a08d04SJames Feist                             }
326b7a08d04SJames Feist                             (*config)[propertyPair.first] = *ptr;
327b7a08d04SJames Feist                         }
328b7a08d04SJames Feist 
329b7a08d04SJames Feist                         if (intfPair.first == stepwiseConfigurationIface)
330b7a08d04SJames Feist                         {
331b7a08d04SJames Feist                             if (propertyPair.first == "Reading" ||
332b7a08d04SJames Feist                                 propertyPair.first == "Output")
333b7a08d04SJames Feist                             {
334b7a08d04SJames Feist                                 const std::vector<double>* ptr =
335abf2add6SEd Tanous                                     std::get_if<std::vector<double>>(
336b7a08d04SJames Feist                                         &propertyPair.second);
337b7a08d04SJames Feist 
338b7a08d04SJames Feist                                 if (ptr == nullptr)
339b7a08d04SJames Feist                                 {
340b7a08d04SJames Feist                                     BMCWEB_LOG_ERROR << "Field Illegal "
341b7a08d04SJames Feist                                                      << propertyPair.first;
342b7a08d04SJames Feist                                     messages::internalError(asyncResp->res);
343b7a08d04SJames Feist                                     return;
344b7a08d04SJames Feist                                 }
345b7a08d04SJames Feist 
346b7a08d04SJames Feist                                 if (propertyPair.first == "Reading")
347b7a08d04SJames Feist                                 {
348b7a08d04SJames Feist                                     keys = ptr;
349b7a08d04SJames Feist                                 }
350b7a08d04SJames Feist                                 else
351b7a08d04SJames Feist                                 {
352b7a08d04SJames Feist                                     values = ptr;
353b7a08d04SJames Feist                                 }
354b7a08d04SJames Feist                                 if (keys && values)
355b7a08d04SJames Feist                                 {
356b7a08d04SJames Feist                                     if (keys->size() != values->size())
357b7a08d04SJames Feist                                     {
358b7a08d04SJames Feist                                         BMCWEB_LOG_ERROR
359b7a08d04SJames Feist                                             << "Reading and Output size don't "
360b7a08d04SJames Feist                                                "match ";
361b7a08d04SJames Feist                                         messages::internalError(asyncResp->res);
362b7a08d04SJames Feist                                         return;
363b7a08d04SJames Feist                                     }
364b7a08d04SJames Feist                                     nlohmann::json& steps = (*config)["Steps"];
365b7a08d04SJames Feist                                     steps = nlohmann::json::array();
366b7a08d04SJames Feist                                     for (size_t ii = 0; ii < keys->size(); ii++)
367b7a08d04SJames Feist                                     {
368b7a08d04SJames Feist                                         steps.push_back(
369b7a08d04SJames Feist                                             {{"Target", (*keys)[ii]},
370b7a08d04SJames Feist                                              {"Output", (*values)[ii]}});
371b7a08d04SJames Feist                                     }
372b7a08d04SJames Feist                                 }
373b7a08d04SJames Feist                             }
374b7a08d04SJames Feist                             if (propertyPair.first == "NegativeHysteresis" ||
375b7a08d04SJames Feist                                 propertyPair.first == "PositiveHysteresis")
376b7a08d04SJames Feist                             {
377b7a08d04SJames Feist                                 const double* ptr =
378abf2add6SEd Tanous                                     std::get_if<double>(&propertyPair.second);
379b7a08d04SJames Feist                                 if (ptr == nullptr)
380b7a08d04SJames Feist                                 {
381b7a08d04SJames Feist                                     BMCWEB_LOG_ERROR << "Field Illegal "
382b7a08d04SJames Feist                                                      << propertyPair.first;
383b7a08d04SJames Feist                                     messages::internalError(asyncResp->res);
384b7a08d04SJames Feist                                     return;
385b7a08d04SJames Feist                                 }
386b7a08d04SJames Feist                                 (*config)[propertyPair.first] = *ptr;
387b7a08d04SJames Feist                             }
388b7a08d04SJames Feist                         }
389b7a08d04SJames Feist 
390b7a08d04SJames Feist                         // pid and fans are off the same configuration
391b7a08d04SJames Feist                         if (intfPair.first == pidConfigurationIface ||
392b7a08d04SJames Feist                             intfPair.first == stepwiseConfigurationIface)
393b7a08d04SJames Feist                         {
3945b4aa86bSJames Feist 
3955b4aa86bSJames Feist                             if (propertyPair.first == "Zones")
3965b4aa86bSJames Feist                             {
3975b4aa86bSJames Feist                                 const std::vector<std::string>* inputs =
398abf2add6SEd Tanous                                     std::get_if<std::vector<std::string>>(
3991b6b96c5SEd Tanous                                         &propertyPair.second);
4005b4aa86bSJames Feist 
4015b4aa86bSJames Feist                                 if (inputs == nullptr)
4025b4aa86bSJames Feist                                 {
4035b4aa86bSJames Feist                                     BMCWEB_LOG_ERROR
4045b4aa86bSJames Feist                                         << "Zones Pid Field Illegal";
405a08b46ccSJason M. Bills                                     messages::internalError(asyncResp->res);
4065b4aa86bSJames Feist                                     return;
4075b4aa86bSJames Feist                                 }
408b7a08d04SJames Feist                                 auto& data = (*config)[propertyPair.first];
4095b4aa86bSJames Feist                                 data = nlohmann::json::array();
4105b4aa86bSJames Feist                                 for (std::string itemCopy : *inputs)
4115b4aa86bSJames Feist                                 {
4125b4aa86bSJames Feist                                     dbus::utility::escapePathForDbus(itemCopy);
4135b4aa86bSJames Feist                                     data.push_back(
4145b4aa86bSJames Feist                                         {{"@odata.id",
4155b4aa86bSJames Feist                                           "/redfish/v1/Managers/bmc#/Oem/"
4165b4aa86bSJames Feist                                           "OpenBmc/Fan/FanZones/" +
4175b4aa86bSJames Feist                                               itemCopy}});
4185b4aa86bSJames Feist                                 }
4195b4aa86bSJames Feist                             }
4205b4aa86bSJames Feist                             // todo(james): may never happen, but this
4215b4aa86bSJames Feist                             // assumes configuration data referenced in the
4225b4aa86bSJames Feist                             // PID config is provided by the same daemon, we
4235b4aa86bSJames Feist                             // could add another loop to cover all cases,
4245b4aa86bSJames Feist                             // but I'm okay kicking this can down the road a
4255b4aa86bSJames Feist                             // bit
4265b4aa86bSJames Feist 
4275b4aa86bSJames Feist                             else if (propertyPair.first == "Inputs" ||
4285b4aa86bSJames Feist                                      propertyPair.first == "Outputs")
4295b4aa86bSJames Feist                             {
430b7a08d04SJames Feist                                 auto& data = (*config)[propertyPair.first];
4315b4aa86bSJames Feist                                 const std::vector<std::string>* inputs =
432abf2add6SEd Tanous                                     std::get_if<std::vector<std::string>>(
4331b6b96c5SEd Tanous                                         &propertyPair.second);
4345b4aa86bSJames Feist 
4355b4aa86bSJames Feist                                 if (inputs == nullptr)
4365b4aa86bSJames Feist                                 {
4375b4aa86bSJames Feist                                     BMCWEB_LOG_ERROR << "Field Illegal "
4385b4aa86bSJames Feist                                                      << propertyPair.first;
439f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
4405b4aa86bSJames Feist                                     return;
4415b4aa86bSJames Feist                                 }
4425b4aa86bSJames Feist                                 data = *inputs;
4435b4aa86bSJames Feist                             } // doubles
4445b4aa86bSJames Feist                             else if (propertyPair.first ==
4455b4aa86bSJames Feist                                          "FFGainCoefficient" ||
4465b4aa86bSJames Feist                                      propertyPair.first == "FFOffCoefficient" ||
4475b4aa86bSJames Feist                                      propertyPair.first == "ICoefficient" ||
4485b4aa86bSJames Feist                                      propertyPair.first == "ILimitMax" ||
4495b4aa86bSJames Feist                                      propertyPair.first == "ILimitMin" ||
450aad1a257SJames Feist                                      propertyPair.first ==
451aad1a257SJames Feist                                          "PositiveHysteresis" ||
452aad1a257SJames Feist                                      propertyPair.first ==
453aad1a257SJames Feist                                          "NegativeHysteresis" ||
4545b4aa86bSJames Feist                                      propertyPair.first == "OutLimitMax" ||
4555b4aa86bSJames Feist                                      propertyPair.first == "OutLimitMin" ||
4565b4aa86bSJames Feist                                      propertyPair.first == "PCoefficient" ||
4577625cb81SJames Feist                                      propertyPair.first == "SetPoint" ||
4585b4aa86bSJames Feist                                      propertyPair.first == "SlewNeg" ||
4595b4aa86bSJames Feist                                      propertyPair.first == "SlewPos")
4605b4aa86bSJames Feist                             {
4615b4aa86bSJames Feist                                 const double* ptr =
462abf2add6SEd Tanous                                     std::get_if<double>(&propertyPair.second);
4635b4aa86bSJames Feist                                 if (ptr == nullptr)
4645b4aa86bSJames Feist                                 {
4655b4aa86bSJames Feist                                     BMCWEB_LOG_ERROR << "Field Illegal "
4665b4aa86bSJames Feist                                                      << propertyPair.first;
467f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
4685b4aa86bSJames Feist                                     return;
4695b4aa86bSJames Feist                                 }
470b7a08d04SJames Feist                                 (*config)[propertyPair.first] = *ptr;
4715b4aa86bSJames Feist                             }
4725b4aa86bSJames Feist                         }
4735b4aa86bSJames Feist                     }
4745b4aa86bSJames Feist                 }
4755b4aa86bSJames Feist             }
4765b4aa86bSJames Feist         },
4775b4aa86bSJames Feist         connection, path, objectManagerIface, "GetManagedObjects");
4785b4aa86bSJames Feist }
479ca537928SJennifer Lee 
48083ff9ab6SJames Feist enum class CreatePIDRet
48183ff9ab6SJames Feist {
48283ff9ab6SJames Feist     fail,
48383ff9ab6SJames Feist     del,
48483ff9ab6SJames Feist     patch
48583ff9ab6SJames Feist };
48683ff9ab6SJames Feist 
4875f2caaefSJames Feist static bool getZonesFromJsonReq(const std::shared_ptr<AsyncResp>& response,
4885f2caaefSJames Feist                                 std::vector<nlohmann::json>& config,
4895f2caaefSJames Feist                                 std::vector<std::string>& zones)
4905f2caaefSJames Feist {
491b6baeaa4SJames Feist     if (config.empty())
492b6baeaa4SJames Feist     {
493b6baeaa4SJames Feist         BMCWEB_LOG_ERROR << "Empty Zones";
494b6baeaa4SJames Feist         messages::propertyValueFormatError(response->res,
495b6baeaa4SJames Feist                                            nlohmann::json::array(), "Zones");
496b6baeaa4SJames Feist         return false;
497b6baeaa4SJames Feist     }
4985f2caaefSJames Feist     for (auto& odata : config)
4995f2caaefSJames Feist     {
5005f2caaefSJames Feist         std::string path;
5015f2caaefSJames Feist         if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
5025f2caaefSJames Feist                                           path))
5035f2caaefSJames Feist         {
5045f2caaefSJames Feist             return false;
5055f2caaefSJames Feist         }
5065f2caaefSJames Feist         std::string input;
50761adbda3SJames Feist 
50861adbda3SJames Feist         // 8 below comes from
50961adbda3SJames Feist         // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
51061adbda3SJames Feist         //     0    1     2      3    4    5      6     7      8
51161adbda3SJames Feist         if (!dbus::utility::getNthStringFromPath(path, 8, input))
5125f2caaefSJames Feist         {
5135f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Got invalid path " << path;
5145f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Illegal Type Zones";
5155f2caaefSJames Feist             messages::propertyValueFormatError(response->res, odata.dump(),
5165f2caaefSJames Feist                                                "Zones");
5175f2caaefSJames Feist             return false;
5185f2caaefSJames Feist         }
5195f2caaefSJames Feist         boost::replace_all(input, "_", " ");
5205f2caaefSJames Feist         zones.emplace_back(std::move(input));
5215f2caaefSJames Feist     }
5225f2caaefSJames Feist     return true;
5235f2caaefSJames Feist }
5245f2caaefSJames Feist 
525b6baeaa4SJames Feist static bool findChassis(const dbus::utility::ManagedObjectType& managedObj,
526b6baeaa4SJames Feist                         const std::string& value, std::string& chassis)
527b6baeaa4SJames Feist {
528b6baeaa4SJames Feist     BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
529b6baeaa4SJames Feist 
530b6baeaa4SJames Feist     std::string escaped = boost::replace_all_copy(value, " ", "_");
531b6baeaa4SJames Feist     escaped = "/" + escaped;
532b6baeaa4SJames Feist     auto it = std::find_if(
533b6baeaa4SJames Feist         managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
534b6baeaa4SJames Feist             if (boost::algorithm::ends_with(obj.first.str, escaped))
535b6baeaa4SJames Feist             {
536b6baeaa4SJames Feist                 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
537b6baeaa4SJames Feist                 return true;
538b6baeaa4SJames Feist             }
539b6baeaa4SJames Feist             return false;
540b6baeaa4SJames Feist         });
541b6baeaa4SJames Feist 
542b6baeaa4SJames Feist     if (it == managedObj.end())
543b6baeaa4SJames Feist     {
544b6baeaa4SJames Feist         return false;
545b6baeaa4SJames Feist     }
546b6baeaa4SJames Feist     // 5 comes from <chassis-name> being the 5th element
547b6baeaa4SJames Feist     // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
548b6baeaa4SJames Feist     return dbus::utility::getNthStringFromPath(it->first.str, 5, chassis);
549b6baeaa4SJames Feist }
550b6baeaa4SJames Feist 
55183ff9ab6SJames Feist static CreatePIDRet createPidInterface(
55283ff9ab6SJames Feist     const std::shared_ptr<AsyncResp>& response, const std::string& type,
553b6baeaa4SJames Feist     nlohmann::json::iterator it, const std::string& path,
55483ff9ab6SJames Feist     const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
55583ff9ab6SJames Feist     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
55683ff9ab6SJames Feist         output,
55783ff9ab6SJames Feist     std::string& chassis)
55883ff9ab6SJames Feist {
55983ff9ab6SJames Feist 
5605f2caaefSJames Feist     // common deleter
561b6baeaa4SJames Feist     if (it.value() == nullptr)
5625f2caaefSJames Feist     {
5635f2caaefSJames Feist         std::string iface;
5645f2caaefSJames Feist         if (type == "PidControllers" || type == "FanControllers")
5655f2caaefSJames Feist         {
5665f2caaefSJames Feist             iface = pidConfigurationIface;
5675f2caaefSJames Feist         }
5685f2caaefSJames Feist         else if (type == "FanZones")
5695f2caaefSJames Feist         {
5705f2caaefSJames Feist             iface = pidZoneConfigurationIface;
5715f2caaefSJames Feist         }
5725f2caaefSJames Feist         else if (type == "StepwiseControllers")
5735f2caaefSJames Feist         {
5745f2caaefSJames Feist             iface = stepwiseConfigurationIface;
5755f2caaefSJames Feist         }
5765f2caaefSJames Feist         else
5775f2caaefSJames Feist         {
5785f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type "
5795f2caaefSJames Feist                              << type;
5805f2caaefSJames Feist             messages::propertyUnknown(response->res, type);
5815f2caaefSJames Feist             return CreatePIDRet::fail;
5825f2caaefSJames Feist         }
5835f2caaefSJames Feist         // delete interface
5845f2caaefSJames Feist         crow::connections::systemBus->async_method_call(
5855f2caaefSJames Feist             [response, path](const boost::system::error_code ec) {
5865f2caaefSJames Feist                 if (ec)
5875f2caaefSJames Feist                 {
5885f2caaefSJames Feist                     BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
5895f2caaefSJames Feist                     messages::internalError(response->res);
590b6baeaa4SJames Feist                     return;
5915f2caaefSJames Feist                 }
592b6baeaa4SJames Feist                 messages::success(response->res);
5935f2caaefSJames Feist             },
5945f2caaefSJames Feist             "xyz.openbmc_project.EntityManager", path, iface, "Delete");
5955f2caaefSJames Feist         return CreatePIDRet::del;
5965f2caaefSJames Feist     }
5975f2caaefSJames Feist 
598b6baeaa4SJames Feist     if (!createNewObject)
599b6baeaa4SJames Feist     {
600b6baeaa4SJames Feist         // if we aren't creating a new object, we should be able to find it on
601b6baeaa4SJames Feist         // d-bus
602b6baeaa4SJames Feist         if (!findChassis(managedObj, it.key(), chassis))
603b6baeaa4SJames Feist         {
604b6baeaa4SJames Feist             BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
605b6baeaa4SJames Feist             messages::invalidObject(response->res, it.key());
606b6baeaa4SJames Feist             return CreatePIDRet::fail;
607b6baeaa4SJames Feist         }
608b6baeaa4SJames Feist     }
609b6baeaa4SJames Feist 
61083ff9ab6SJames Feist     if (type == "PidControllers" || type == "FanControllers")
61183ff9ab6SJames Feist     {
61283ff9ab6SJames Feist         if (createNewObject)
61383ff9ab6SJames Feist         {
61483ff9ab6SJames Feist             output["Class"] = type == "PidControllers" ? std::string("temp")
61583ff9ab6SJames Feist                                                        : std::string("fan");
61683ff9ab6SJames Feist             output["Type"] = std::string("Pid");
61783ff9ab6SJames Feist         }
6185f2caaefSJames Feist 
6195f2caaefSJames Feist         std::optional<std::vector<nlohmann::json>> zones;
6205f2caaefSJames Feist         std::optional<std::vector<std::string>> inputs;
6215f2caaefSJames Feist         std::optional<std::vector<std::string>> outputs;
6225f2caaefSJames Feist         std::map<std::string, std::optional<double>> doubles;
6235f2caaefSJames Feist         if (!redfish::json_util::readJson(
624b6baeaa4SJames Feist                 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
6255f2caaefSJames Feist                 "Zones", zones, "FFGainCoefficient",
6265f2caaefSJames Feist                 doubles["FFGainCoefficient"], "FFOffCoefficient",
6275f2caaefSJames Feist                 doubles["FFOffCoefficient"], "ICoefficient",
6285f2caaefSJames Feist                 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
6295f2caaefSJames Feist                 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
6305f2caaefSJames Feist                 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
6315f2caaefSJames Feist                 "PCoefficient", doubles["PCoefficient"], "SetPoint",
6325f2caaefSJames Feist                 doubles["SetPoint"], "SlewNeg", doubles["SlewNeg"], "SlewPos",
633aad1a257SJames Feist                 doubles["SlewPos"], "PositiveHysteresis",
634aad1a257SJames Feist                 doubles["PositiveHysteresis"], "NegativeHysteresis",
635aad1a257SJames Feist                 doubles["NegativeHysteresis"]))
63683ff9ab6SJames Feist         {
6375f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
638b6baeaa4SJames Feist                              << it.value().dump();
6395f2caaefSJames Feist             return CreatePIDRet::fail;
64083ff9ab6SJames Feist         }
6415f2caaefSJames Feist         if (zones)
6425f2caaefSJames Feist         {
6435f2caaefSJames Feist             std::vector<std::string> zonesStr;
6445f2caaefSJames Feist             if (!getZonesFromJsonReq(response, *zones, zonesStr))
6455f2caaefSJames Feist             {
6465f2caaefSJames Feist                 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
6475f2caaefSJames Feist                 return CreatePIDRet::fail;
6485f2caaefSJames Feist             }
649b6baeaa4SJames Feist             if (chassis.empty() &&
650b6baeaa4SJames Feist                 !findChassis(managedObj, zonesStr[0], chassis))
651b6baeaa4SJames Feist             {
652b6baeaa4SJames Feist                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
653b6baeaa4SJames Feist                 messages::invalidObject(response->res, it.key());
654b6baeaa4SJames Feist                 return CreatePIDRet::fail;
655b6baeaa4SJames Feist             }
656b6baeaa4SJames Feist 
6575f2caaefSJames Feist             output["Zones"] = std::move(zonesStr);
6585f2caaefSJames Feist         }
6595f2caaefSJames Feist         if (inputs || outputs)
6605f2caaefSJames Feist         {
6615f2caaefSJames Feist             std::array<std::optional<std::vector<std::string>>*, 2> containers =
6625f2caaefSJames Feist                 {&inputs, &outputs};
6635f2caaefSJames Feist             size_t index = 0;
6645f2caaefSJames Feist             for (const auto& containerPtr : containers)
6655f2caaefSJames Feist             {
6665f2caaefSJames Feist                 std::optional<std::vector<std::string>>& container =
6675f2caaefSJames Feist                     *containerPtr;
6685f2caaefSJames Feist                 if (!container)
6695f2caaefSJames Feist                 {
6705f2caaefSJames Feist                     index++;
6715f2caaefSJames Feist                     continue;
67283ff9ab6SJames Feist                 }
67383ff9ab6SJames Feist 
6745f2caaefSJames Feist                 for (std::string& value : *container)
67583ff9ab6SJames Feist                 {
6765f2caaefSJames Feist                     boost::replace_all(value, "_", " ");
67783ff9ab6SJames Feist                 }
6785f2caaefSJames Feist                 std::string key;
6795f2caaefSJames Feist                 if (index == 0)
6805f2caaefSJames Feist                 {
6815f2caaefSJames Feist                     key = "Inputs";
6825f2caaefSJames Feist                 }
6835f2caaefSJames Feist                 else
6845f2caaefSJames Feist                 {
6855f2caaefSJames Feist                     key = "Outputs";
6865f2caaefSJames Feist                 }
6875f2caaefSJames Feist                 output[key] = *container;
6885f2caaefSJames Feist                 index++;
6895f2caaefSJames Feist             }
69083ff9ab6SJames Feist         }
69183ff9ab6SJames Feist 
69283ff9ab6SJames Feist         // doubles
6935f2caaefSJames Feist         for (const auto& pairs : doubles)
69483ff9ab6SJames Feist         {
6955f2caaefSJames Feist             if (!pairs.second)
69683ff9ab6SJames Feist             {
6975f2caaefSJames Feist                 continue;
69883ff9ab6SJames Feist             }
6995f2caaefSJames Feist             BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
7005f2caaefSJames Feist             output[pairs.first] = *(pairs.second);
7015f2caaefSJames Feist         }
70283ff9ab6SJames Feist     }
70383ff9ab6SJames Feist 
70483ff9ab6SJames Feist     else if (type == "FanZones")
70583ff9ab6SJames Feist     {
70683ff9ab6SJames Feist         output["Type"] = std::string("Pid.Zone");
70783ff9ab6SJames Feist 
7085f2caaefSJames Feist         std::optional<nlohmann::json> chassisContainer;
7095f2caaefSJames Feist         std::optional<double> failSafePercent;
710d3ec07f8SJames Feist         std::optional<double> minThermalOutput;
711b6baeaa4SJames Feist         if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
7125f2caaefSJames Feist                                           chassisContainer, "FailSafePercent",
713d3ec07f8SJames Feist                                           failSafePercent, "MinThermalOutput",
714d3ec07f8SJames Feist                                           minThermalOutput))
71583ff9ab6SJames Feist         {
7165f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
717b6baeaa4SJames Feist                              << it.value().dump();
71883ff9ab6SJames Feist             return CreatePIDRet::fail;
71983ff9ab6SJames Feist         }
7205f2caaefSJames Feist 
7215f2caaefSJames Feist         if (chassisContainer)
72283ff9ab6SJames Feist         {
7235f2caaefSJames Feist 
7245f2caaefSJames Feist             std::string chassisId;
7255f2caaefSJames Feist             if (!redfish::json_util::readJson(*chassisContainer, response->res,
7265f2caaefSJames Feist                                               "@odata.id", chassisId))
7275f2caaefSJames Feist             {
7285f2caaefSJames Feist                 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
7295f2caaefSJames Feist                                  << chassisContainer->dump();
73083ff9ab6SJames Feist                 return CreatePIDRet::fail;
73183ff9ab6SJames Feist             }
73283ff9ab6SJames Feist 
73383ff9ab6SJames Feist             // /refish/v1/chassis/chassis_name/
7345f2caaefSJames Feist             if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
73583ff9ab6SJames Feist             {
7365f2caaefSJames Feist                 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
7375f2caaefSJames Feist                 messages::invalidObject(response->res, chassisId);
73883ff9ab6SJames Feist                 return CreatePIDRet::fail;
73983ff9ab6SJames Feist             }
74083ff9ab6SJames Feist         }
741d3ec07f8SJames Feist         if (minThermalOutput)
74283ff9ab6SJames Feist         {
743d3ec07f8SJames Feist             output["MinThermalOutput"] = *minThermalOutput;
7445f2caaefSJames Feist         }
7455f2caaefSJames Feist         if (failSafePercent)
74683ff9ab6SJames Feist         {
7475f2caaefSJames Feist             output["FailSafePercent"] = *failSafePercent;
7485f2caaefSJames Feist         }
7495f2caaefSJames Feist     }
7505f2caaefSJames Feist     else if (type == "StepwiseControllers")
7515f2caaefSJames Feist     {
7525f2caaefSJames Feist         output["Type"] = std::string("Stepwise");
7535f2caaefSJames Feist 
7545f2caaefSJames Feist         std::optional<std::vector<nlohmann::json>> zones;
7555f2caaefSJames Feist         std::optional<std::vector<nlohmann::json>> steps;
7565f2caaefSJames Feist         std::optional<std::vector<std::string>> inputs;
7575f2caaefSJames Feist         std::optional<double> positiveHysteresis;
7585f2caaefSJames Feist         std::optional<double> negativeHysteresis;
759c33a90ecSJames Feist         std::optional<std::string> direction; // upper clipping curve vs lower
7605f2caaefSJames Feist         if (!redfish::json_util::readJson(
761b6baeaa4SJames Feist                 it.value(), response->res, "Zones", zones, "Steps", steps,
762b6baeaa4SJames Feist                 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
763c33a90ecSJames Feist                 "NegativeHysteresis", negativeHysteresis, "Direction",
764c33a90ecSJames Feist                 direction))
7655f2caaefSJames Feist         {
7665f2caaefSJames Feist             BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
767b6baeaa4SJames Feist                              << it.value().dump();
76883ff9ab6SJames Feist             return CreatePIDRet::fail;
76983ff9ab6SJames Feist         }
7705f2caaefSJames Feist 
7715f2caaefSJames Feist         if (zones)
77283ff9ab6SJames Feist         {
773b6baeaa4SJames Feist             std::vector<std::string> zonesStrs;
774b6baeaa4SJames Feist             if (!getZonesFromJsonReq(response, *zones, zonesStrs))
7755f2caaefSJames Feist             {
7765f2caaefSJames Feist                 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Zones";
77783ff9ab6SJames Feist                 return CreatePIDRet::fail;
77883ff9ab6SJames Feist             }
779b6baeaa4SJames Feist             if (chassis.empty() &&
780b6baeaa4SJames Feist                 !findChassis(managedObj, zonesStrs[0], chassis))
781b6baeaa4SJames Feist             {
782b6baeaa4SJames Feist                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
783b6baeaa4SJames Feist                 messages::invalidObject(response->res, it.key());
784b6baeaa4SJames Feist                 return CreatePIDRet::fail;
785b6baeaa4SJames Feist             }
786b6baeaa4SJames Feist             output["Zones"] = std::move(zonesStrs);
7875f2caaefSJames Feist         }
7885f2caaefSJames Feist         if (steps)
7895f2caaefSJames Feist         {
7905f2caaefSJames Feist             std::vector<double> readings;
7915f2caaefSJames Feist             std::vector<double> outputs;
7925f2caaefSJames Feist             for (auto& step : *steps)
7935f2caaefSJames Feist             {
7945f2caaefSJames Feist                 double target;
795b01bf299SEd Tanous                 double output;
7965f2caaefSJames Feist 
7975f2caaefSJames Feist                 if (!redfish::json_util::readJson(step, response->res, "Target",
798b01bf299SEd Tanous                                                   target, "Output", output))
7995f2caaefSJames Feist                 {
8005f2caaefSJames Feist                     BMCWEB_LOG_ERROR << "Line:" << __LINE__
801b6baeaa4SJames Feist                                      << ", Illegal Property "
802b6baeaa4SJames Feist                                      << it.value().dump();
8035f2caaefSJames Feist                     return CreatePIDRet::fail;
8045f2caaefSJames Feist                 }
8055f2caaefSJames Feist                 readings.emplace_back(target);
806b01bf299SEd Tanous                 outputs.emplace_back(output);
8075f2caaefSJames Feist             }
8085f2caaefSJames Feist             output["Reading"] = std::move(readings);
8095f2caaefSJames Feist             output["Output"] = std::move(outputs);
8105f2caaefSJames Feist         }
8115f2caaefSJames Feist         if (inputs)
8125f2caaefSJames Feist         {
8135f2caaefSJames Feist             for (std::string& value : *inputs)
8145f2caaefSJames Feist             {
8155f2caaefSJames Feist                 boost::replace_all(value, "_", " ");
8165f2caaefSJames Feist             }
8175f2caaefSJames Feist             output["Inputs"] = std::move(*inputs);
8185f2caaefSJames Feist         }
8195f2caaefSJames Feist         if (negativeHysteresis)
8205f2caaefSJames Feist         {
8215f2caaefSJames Feist             output["NegativeHysteresis"] = *negativeHysteresis;
8225f2caaefSJames Feist         }
8235f2caaefSJames Feist         if (positiveHysteresis)
8245f2caaefSJames Feist         {
8255f2caaefSJames Feist             output["PositiveHysteresis"] = *positiveHysteresis;
82683ff9ab6SJames Feist         }
827c33a90ecSJames Feist         if (direction)
828c33a90ecSJames Feist         {
829c33a90ecSJames Feist             constexpr const std::array<const char*, 2> allowedDirections = {
830c33a90ecSJames Feist                 "Ceiling", "Floor"};
831c33a90ecSJames Feist             if (std::find(allowedDirections.begin(), allowedDirections.end(),
832c33a90ecSJames Feist                           *direction) == allowedDirections.end())
833c33a90ecSJames Feist             {
834c33a90ecSJames Feist                 messages::propertyValueTypeError(response->res, "Direction",
835c33a90ecSJames Feist                                                  *direction);
836c33a90ecSJames Feist                 return CreatePIDRet::fail;
837c33a90ecSJames Feist             }
838c33a90ecSJames Feist             output["Class"] = *direction;
839c33a90ecSJames Feist         }
84083ff9ab6SJames Feist     }
84183ff9ab6SJames Feist     else
84283ff9ab6SJames Feist     {
8435f2caaefSJames Feist         BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Type " << type;
84435a62c7cSJason M. Bills         messages::propertyUnknown(response->res, type);
84583ff9ab6SJames Feist         return CreatePIDRet::fail;
84683ff9ab6SJames Feist     }
84783ff9ab6SJames Feist     return CreatePIDRet::patch;
84883ff9ab6SJames Feist }
84983ff9ab6SJames Feist 
8501abe55efSEd Tanous class Manager : public Node
8511abe55efSEd Tanous {
8529c310685SBorawski.Lukasz   public:
8535b4aa86bSJames Feist     Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
8541abe55efSEd Tanous     {
8550f74e643SEd Tanous         uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
85655c7b7a2SEd Tanous                    .systemUuid;
857a434f2bdSEd Tanous         entityPrivileges = {
858a434f2bdSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
859e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
860e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
861e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
862e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
863e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
8649c310685SBorawski.Lukasz     }
8659c310685SBorawski.Lukasz 
8669c310685SBorawski.Lukasz   private:
8675b4aa86bSJames Feist     void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
8685b4aa86bSJames Feist     {
8695b4aa86bSJames Feist         crow::connections::systemBus->async_method_call(
8705b4aa86bSJames Feist             [asyncResp](const boost::system::error_code ec,
8715b4aa86bSJames Feist                         const crow::openbmc_mapper::GetSubTreeType& subtree) {
8725b4aa86bSJames Feist                 if (ec)
8735b4aa86bSJames Feist                 {
8745b4aa86bSJames Feist                     BMCWEB_LOG_ERROR << ec;
875f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
8765b4aa86bSJames Feist                     return;
8775b4aa86bSJames Feist                 }
8785b4aa86bSJames Feist 
8795b4aa86bSJames Feist                 // create map of <connection, path to objMgr>>
8805b4aa86bSJames Feist                 boost::container::flat_map<std::string, std::string>
8815b4aa86bSJames Feist                     objectMgrPaths;
8826bce33bcSJames Feist                 boost::container::flat_set<std::string> calledConnections;
8835b4aa86bSJames Feist                 for (const auto& pathGroup : subtree)
8845b4aa86bSJames Feist                 {
8855b4aa86bSJames Feist                     for (const auto& connectionGroup : pathGroup.second)
8865b4aa86bSJames Feist                     {
8876bce33bcSJames Feist                         auto findConnection =
8886bce33bcSJames Feist                             calledConnections.find(connectionGroup.first);
8896bce33bcSJames Feist                         if (findConnection != calledConnections.end())
8906bce33bcSJames Feist                         {
8916bce33bcSJames Feist                             break;
8926bce33bcSJames Feist                         }
8935b4aa86bSJames Feist                         for (const std::string& interface :
8945b4aa86bSJames Feist                              connectionGroup.second)
8955b4aa86bSJames Feist                         {
8965b4aa86bSJames Feist                             if (interface == objectManagerIface)
8975b4aa86bSJames Feist                             {
8985b4aa86bSJames Feist                                 objectMgrPaths[connectionGroup.first] =
8995b4aa86bSJames Feist                                     pathGroup.first;
9005b4aa86bSJames Feist                             }
9015b4aa86bSJames Feist                             // this list is alphabetical, so we
9025b4aa86bSJames Feist                             // should have found the objMgr by now
9035b4aa86bSJames Feist                             if (interface == pidConfigurationIface ||
904b7a08d04SJames Feist                                 interface == pidZoneConfigurationIface ||
905b7a08d04SJames Feist                                 interface == stepwiseConfigurationIface)
9065b4aa86bSJames Feist                             {
9075b4aa86bSJames Feist                                 auto findObjMgr =
9085b4aa86bSJames Feist                                     objectMgrPaths.find(connectionGroup.first);
9095b4aa86bSJames Feist                                 if (findObjMgr == objectMgrPaths.end())
9105b4aa86bSJames Feist                                 {
9115b4aa86bSJames Feist                                     BMCWEB_LOG_DEBUG << connectionGroup.first
9125b4aa86bSJames Feist                                                      << "Has no Object Manager";
9135b4aa86bSJames Feist                                     continue;
9145b4aa86bSJames Feist                                 }
9156bce33bcSJames Feist 
9166bce33bcSJames Feist                                 calledConnections.insert(connectionGroup.first);
9176bce33bcSJames Feist 
9185b4aa86bSJames Feist                                 asyncPopulatePid(findObjMgr->first,
9195b4aa86bSJames Feist                                                  findObjMgr->second, asyncResp);
9205b4aa86bSJames Feist                                 break;
9215b4aa86bSJames Feist                             }
9225b4aa86bSJames Feist                         }
9235b4aa86bSJames Feist                     }
9245b4aa86bSJames Feist                 }
9255b4aa86bSJames Feist             },
9265b4aa86bSJames Feist             "xyz.openbmc_project.ObjectMapper",
9275b4aa86bSJames Feist             "/xyz/openbmc_project/object_mapper",
9285b4aa86bSJames Feist             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
929b7a08d04SJames Feist             std::array<const char*, 4>{
930b7a08d04SJames Feist                 pidConfigurationIface, pidZoneConfigurationIface,
931b7a08d04SJames Feist                 objectManagerIface, stepwiseConfigurationIface});
9325b4aa86bSJames Feist     }
9335b4aa86bSJames Feist 
93455c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
9351abe55efSEd Tanous                const std::vector<std::string>& params) override
9361abe55efSEd Tanous     {
9370f74e643SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
9380f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
9390f74e643SEd Tanous         res.jsonValue["@odata.context"] =
9400f74e643SEd Tanous             "/redfish/v1/$metadata#Manager.Manager";
9410f74e643SEd Tanous         res.jsonValue["Id"] = "bmc";
9420f74e643SEd Tanous         res.jsonValue["Name"] = "OpenBmc Manager";
9430f74e643SEd Tanous         res.jsonValue["Description"] = "Baseboard Management Controller";
9440f74e643SEd Tanous         res.jsonValue["PowerState"] = "On";
945029573d4SEd Tanous         res.jsonValue["Status"] = {{"State", "Enabled"}, {"Health", "OK"}};
9460f74e643SEd Tanous         res.jsonValue["ManagerType"] = "BMC";
9477517658eSEd Tanous         res.jsonValue["UUID"] = uuid;
9487bffdb7eSBernard Wong         res.jsonValue["ServiceEntryPointUUID"] = systemd_utils::getUuid();
9490f74e643SEd Tanous         res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
9500f74e643SEd Tanous 
9510f74e643SEd Tanous         res.jsonValue["LogServices"] = {
9520f74e643SEd Tanous             {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
9530f74e643SEd Tanous 
9540f74e643SEd Tanous         res.jsonValue["NetworkProtocol"] = {
9550f74e643SEd Tanous             {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
9560f74e643SEd Tanous 
9570f74e643SEd Tanous         res.jsonValue["EthernetInterfaces"] = {
9580f74e643SEd Tanous             {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
9590f74e643SEd Tanous         // default oem data
9600f74e643SEd Tanous         nlohmann::json& oem = res.jsonValue["Oem"];
9610f74e643SEd Tanous         nlohmann::json& oemOpenbmc = oem["OpenBmc"];
9620f74e643SEd Tanous         oem["@odata.type"] = "#OemManager.Oem";
9630f74e643SEd Tanous         oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
9640f74e643SEd Tanous         oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
9650f74e643SEd Tanous         oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
9660f74e643SEd Tanous         oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
9670f74e643SEd Tanous         oemOpenbmc["@odata.context"] =
9680f74e643SEd Tanous             "/redfish/v1/$metadata#OemManager.OpenBmc";
9690f74e643SEd Tanous 
970ed5befbdSJennifer Lee         // Update Actions object.
9710f74e643SEd Tanous         nlohmann::json& manager_reset =
9720f74e643SEd Tanous             res.jsonValue["Actions"]["#Manager.Reset"];
973ed5befbdSJennifer Lee         manager_reset["target"] =
974ed5befbdSJennifer Lee             "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
975ed5befbdSJennifer Lee         manager_reset["ResetType@Redfish.AllowableValues"] = {
976ed5befbdSJennifer Lee             "GracefulRestart"};
977ca537928SJennifer Lee 
978cb92c03bSAndrew Geissler         res.jsonValue["DateTime"] = crow::utility::dateTimeNow();
979474bfad5SSantosh Puranik 
980474bfad5SSantosh Puranik         // Fill in GraphicalConsole and SerialConsole info
981474bfad5SSantosh Puranik         res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
982474bfad5SSantosh Puranik         res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = {"IPMI",
983474bfad5SSantosh Puranik                                                                    "SSH"};
984474bfad5SSantosh Puranik         // TODO (Santosh) : Uncomment when KVM support is in.
985474bfad5SSantosh Puranik         // res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
986474bfad5SSantosh Puranik         // res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
987474bfad5SSantosh Puranik         //    {"KVMIP"};
988474bfad5SSantosh Puranik 
989603a6640SGunnar Mills         res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
990603a6640SGunnar Mills         res.jsonValue["Links"]["ManagerForServers"] = {
991603a6640SGunnar Mills             {{"@odata.id", "/redfish/v1/Systems/system"}}};
992*26f03899SShawn McCarney 
993ed5befbdSJennifer Lee         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
9945b4aa86bSJames Feist 
995ca537928SJennifer Lee         crow::connections::systemBus->async_method_call(
996ca537928SJennifer Lee             [asyncResp](const boost::system::error_code ec,
9975b4aa86bSJames Feist                         const dbus::utility::ManagedObjectType& resp) {
998ca537928SJennifer Lee                 if (ec)
999ca537928SJennifer Lee                 {
1000ca537928SJennifer Lee                     BMCWEB_LOG_ERROR << "Error while getting Software Version";
1001f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
1002ca537928SJennifer Lee                     return;
1003ca537928SJennifer Lee                 }
1004ca537928SJennifer Lee 
1005ca537928SJennifer Lee                 for (auto& objpath : resp)
1006ca537928SJennifer Lee                 {
1007ca537928SJennifer Lee                     for (auto& interface : objpath.second)
1008ca537928SJennifer Lee                     {
10095f2caaefSJames Feist                         // If interface is
10105f2caaefSJames Feist                         // xyz.openbmc_project.Software.Version, this is
10115f2caaefSJames Feist                         // what we're looking for.
1012ca537928SJennifer Lee                         if (interface.first ==
1013ca537928SJennifer Lee                             "xyz.openbmc_project.Software.Version")
1014ca537928SJennifer Lee                         {
1015ca537928SJennifer Lee                             // Cut out everyting until last "/", ...
1016ca537928SJennifer Lee                             for (auto& property : interface.second)
1017ca537928SJennifer Lee                             {
1018ca537928SJennifer Lee                                 if (property.first == "Version")
1019ca537928SJennifer Lee                                 {
1020ca537928SJennifer Lee                                     const std::string* value =
1021abf2add6SEd Tanous                                         std::get_if<std::string>(
1022abf2add6SEd Tanous                                             &property.second);
1023ca537928SJennifer Lee                                     if (value == nullptr)
1024ca537928SJennifer Lee                                     {
1025ca537928SJennifer Lee                                         continue;
1026ca537928SJennifer Lee                                     }
1027ca537928SJennifer Lee                                     asyncResp->res
1028ca537928SJennifer Lee                                         .jsonValue["FirmwareVersion"] = *value;
1029ca537928SJennifer Lee                                 }
1030ca537928SJennifer Lee                             }
1031ca537928SJennifer Lee                         }
1032ca537928SJennifer Lee                     }
1033ca537928SJennifer Lee                 }
1034ca537928SJennifer Lee             },
1035ca537928SJennifer Lee             "xyz.openbmc_project.Software.BMC.Updater",
1036ca537928SJennifer Lee             "/xyz/openbmc_project/software",
1037ca537928SJennifer Lee             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
10385b4aa86bSJames Feist         getPidValues(asyncResp);
10395b4aa86bSJames Feist     }
10405f2caaefSJames Feist     void setPidValues(std::shared_ptr<AsyncResp> response, nlohmann::json& data)
104183ff9ab6SJames Feist     {
10425f2caaefSJames Feist 
104383ff9ab6SJames Feist         // todo(james): might make sense to do a mapper call here if this
104483ff9ab6SJames Feist         // interface gets more traction
104583ff9ab6SJames Feist         crow::connections::systemBus->async_method_call(
104683ff9ab6SJames Feist             [response,
104783ff9ab6SJames Feist              data](const boost::system::error_code ec,
104883ff9ab6SJames Feist                    const dbus::utility::ManagedObjectType& managedObj) {
104983ff9ab6SJames Feist                 if (ec)
105083ff9ab6SJames Feist                 {
105183ff9ab6SJames Feist                     BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
105235a62c7cSJason M. Bills                     messages::internalError(response->res);
105383ff9ab6SJames Feist                     return;
105483ff9ab6SJames Feist                 }
10555f2caaefSJames Feist 
10565f2caaefSJames Feist                 // todo(james) mutable doesn't work with asio bindings
10575f2caaefSJames Feist                 nlohmann::json jsonData = data;
10585f2caaefSJames Feist 
10595f2caaefSJames Feist                 std::optional<nlohmann::json> pidControllers;
10605f2caaefSJames Feist                 std::optional<nlohmann::json> fanControllers;
10615f2caaefSJames Feist                 std::optional<nlohmann::json> fanZones;
10625f2caaefSJames Feist                 std::optional<nlohmann::json> stepwiseControllers;
10635f2caaefSJames Feist                 if (!redfish::json_util::readJson(
10645f2caaefSJames Feist                         jsonData, response->res, "PidControllers",
10655f2caaefSJames Feist                         pidControllers, "FanControllers", fanControllers,
10665f2caaefSJames Feist                         "FanZones", fanZones, "StepwiseControllers",
10675f2caaefSJames Feist                         stepwiseControllers))
106883ff9ab6SJames Feist                 {
10695f2caaefSJames Feist                     BMCWEB_LOG_ERROR << "Line:" << __LINE__
10705f2caaefSJames Feist                                      << ", Illegal Property "
10715f2caaefSJames Feist                                      << jsonData.dump();
107283ff9ab6SJames Feist                     return;
107383ff9ab6SJames Feist                 }
10745f2caaefSJames Feist                 std::array<
107543b761d0SEd Tanous                     std::pair<std::string, std::optional<nlohmann::json>*>, 4>
10765f2caaefSJames Feist                     sections = {
10775f2caaefSJames Feist                         std::make_pair("PidControllers", &pidControllers),
10785f2caaefSJames Feist                         std::make_pair("FanControllers", &fanControllers),
10795f2caaefSJames Feist                         std::make_pair("FanZones", &fanZones),
10805f2caaefSJames Feist                         std::make_pair("StepwiseControllers",
10815f2caaefSJames Feist                                        &stepwiseControllers)};
10825f2caaefSJames Feist 
10835f2caaefSJames Feist                 for (auto& containerPair : sections)
108483ff9ab6SJames Feist                 {
10855f2caaefSJames Feist                     auto& container = *(containerPair.second);
10865f2caaefSJames Feist                     if (!container)
10875f2caaefSJames Feist                     {
10885f2caaefSJames Feist                         continue;
10895f2caaefSJames Feist                     }
109043b761d0SEd Tanous                     std::string& type = containerPair.first;
10915f2caaefSJames Feist 
1092b6baeaa4SJames Feist                     for (nlohmann::json::iterator it = container->begin();
1093b6baeaa4SJames Feist                          it != container->end(); it++)
10945f2caaefSJames Feist                     {
1095b6baeaa4SJames Feist                         const auto& name = it.key();
109683ff9ab6SJames Feist                         auto pathItr =
109783ff9ab6SJames Feist                             std::find_if(managedObj.begin(), managedObj.end(),
109883ff9ab6SJames Feist                                          [&name](const auto& obj) {
109983ff9ab6SJames Feist                                              return boost::algorithm::ends_with(
1100b6baeaa4SJames Feist                                                  obj.first.str, "/" + name);
110183ff9ab6SJames Feist                                          });
110283ff9ab6SJames Feist                         boost::container::flat_map<
110383ff9ab6SJames Feist                             std::string, dbus::utility::DbusVariantType>
110483ff9ab6SJames Feist                             output;
110583ff9ab6SJames Feist 
110683ff9ab6SJames Feist                         output.reserve(16); // The pid interface length
110783ff9ab6SJames Feist 
110883ff9ab6SJames Feist                         // determines if we're patching entity-manager or
110983ff9ab6SJames Feist                         // creating a new object
111083ff9ab6SJames Feist                         bool createNewObject = (pathItr == managedObj.end());
11115f2caaefSJames Feist                         std::string iface;
11125f2caaefSJames Feist                         if (type == "PidControllers" ||
11135f2caaefSJames Feist                             type == "FanControllers")
111483ff9ab6SJames Feist                         {
11155f2caaefSJames Feist                             iface = pidConfigurationIface;
111683ff9ab6SJames Feist                             if (!createNewObject &&
111783ff9ab6SJames Feist                                 pathItr->second.find(pidConfigurationIface) ==
111883ff9ab6SJames Feist                                     pathItr->second.end())
111983ff9ab6SJames Feist                             {
112083ff9ab6SJames Feist                                 createNewObject = true;
112183ff9ab6SJames Feist                             }
112283ff9ab6SJames Feist                         }
11235f2caaefSJames Feist                         else if (type == "FanZones")
11245f2caaefSJames Feist                         {
11255f2caaefSJames Feist                             iface = pidZoneConfigurationIface;
11265f2caaefSJames Feist                             if (!createNewObject &&
112783ff9ab6SJames Feist                                 pathItr->second.find(
112883ff9ab6SJames Feist                                     pidZoneConfigurationIface) ==
112983ff9ab6SJames Feist                                     pathItr->second.end())
113083ff9ab6SJames Feist                             {
11315f2caaefSJames Feist 
113283ff9ab6SJames Feist                                 createNewObject = true;
113383ff9ab6SJames Feist                             }
11345f2caaefSJames Feist                         }
11355f2caaefSJames Feist                         else if (type == "StepwiseControllers")
11365f2caaefSJames Feist                         {
11375f2caaefSJames Feist                             iface = stepwiseConfigurationIface;
11385f2caaefSJames Feist                             if (!createNewObject &&
11395f2caaefSJames Feist                                 pathItr->second.find(
11405f2caaefSJames Feist                                     stepwiseConfigurationIface) ==
11415f2caaefSJames Feist                                     pathItr->second.end())
11425f2caaefSJames Feist                             {
11435f2caaefSJames Feist                                 createNewObject = true;
11445f2caaefSJames Feist                             }
11455f2caaefSJames Feist                         }
1146b6baeaa4SJames Feist                         BMCWEB_LOG_DEBUG << "Create new = " << createNewObject
1147b6baeaa4SJames Feist                                          << "\n";
114883ff9ab6SJames Feist                         output["Name"] =
114983ff9ab6SJames Feist                             boost::replace_all_copy(name, "_", " ");
115083ff9ab6SJames Feist 
115183ff9ab6SJames Feist                         std::string chassis;
115283ff9ab6SJames Feist                         CreatePIDRet ret = createPidInterface(
1153b6baeaa4SJames Feist                             response, type, it, pathItr->first.str, managedObj,
1154b6baeaa4SJames Feist                             createNewObject, output, chassis);
115583ff9ab6SJames Feist                         if (ret == CreatePIDRet::fail)
115683ff9ab6SJames Feist                         {
115783ff9ab6SJames Feist                             return;
115883ff9ab6SJames Feist                         }
115983ff9ab6SJames Feist                         else if (ret == CreatePIDRet::del)
116083ff9ab6SJames Feist                         {
116183ff9ab6SJames Feist                             continue;
116283ff9ab6SJames Feist                         }
116383ff9ab6SJames Feist 
116483ff9ab6SJames Feist                         if (!createNewObject)
116583ff9ab6SJames Feist                         {
116683ff9ab6SJames Feist                             for (const auto& property : output)
116783ff9ab6SJames Feist                             {
116883ff9ab6SJames Feist                                 crow::connections::systemBus->async_method_call(
116983ff9ab6SJames Feist                                     [response,
117083ff9ab6SJames Feist                                      propertyName{std::string(property.first)}](
117183ff9ab6SJames Feist                                         const boost::system::error_code ec) {
117283ff9ab6SJames Feist                                         if (ec)
117383ff9ab6SJames Feist                                         {
117483ff9ab6SJames Feist                                             BMCWEB_LOG_ERROR
117583ff9ab6SJames Feist                                                 << "Error patching "
117683ff9ab6SJames Feist                                                 << propertyName << ": " << ec;
117735a62c7cSJason M. Bills                                             messages::internalError(
117835a62c7cSJason M. Bills                                                 response->res);
1179b6baeaa4SJames Feist                                             return;
118083ff9ab6SJames Feist                                         }
1181b6baeaa4SJames Feist                                         messages::success(response->res);
118283ff9ab6SJames Feist                                     },
118383ff9ab6SJames Feist                                     "xyz.openbmc_project.EntityManager",
118483ff9ab6SJames Feist                                     pathItr->first.str,
118583ff9ab6SJames Feist                                     "org.freedesktop.DBus.Properties", "Set",
11865f2caaefSJames Feist                                     iface, property.first, property.second);
118783ff9ab6SJames Feist                             }
118883ff9ab6SJames Feist                         }
118983ff9ab6SJames Feist                         else
119083ff9ab6SJames Feist                         {
119183ff9ab6SJames Feist                             if (chassis.empty())
119283ff9ab6SJames Feist                             {
119383ff9ab6SJames Feist                                 BMCWEB_LOG_ERROR
119483ff9ab6SJames Feist                                     << "Failed to get chassis from config";
119535a62c7cSJason M. Bills                                 messages::invalidObject(response->res, name);
119683ff9ab6SJames Feist                                 return;
119783ff9ab6SJames Feist                             }
119883ff9ab6SJames Feist 
119983ff9ab6SJames Feist                             bool foundChassis = false;
120083ff9ab6SJames Feist                             for (const auto& obj : managedObj)
120183ff9ab6SJames Feist                             {
120283ff9ab6SJames Feist                                 if (boost::algorithm::ends_with(obj.first.str,
120383ff9ab6SJames Feist                                                                 chassis))
120483ff9ab6SJames Feist                                 {
120583ff9ab6SJames Feist                                     chassis = obj.first.str;
120683ff9ab6SJames Feist                                     foundChassis = true;
120783ff9ab6SJames Feist                                     break;
120883ff9ab6SJames Feist                                 }
120983ff9ab6SJames Feist                             }
121083ff9ab6SJames Feist                             if (!foundChassis)
121183ff9ab6SJames Feist                             {
121283ff9ab6SJames Feist                                 BMCWEB_LOG_ERROR
121383ff9ab6SJames Feist                                     << "Failed to find chassis on dbus";
121483ff9ab6SJames Feist                                 messages::resourceMissingAtURI(
121535a62c7cSJason M. Bills                                     response->res,
121635a62c7cSJason M. Bills                                     "/redfish/v1/Chassis/" + chassis);
121783ff9ab6SJames Feist                                 return;
121883ff9ab6SJames Feist                             }
121983ff9ab6SJames Feist 
122083ff9ab6SJames Feist                             crow::connections::systemBus->async_method_call(
122183ff9ab6SJames Feist                                 [response](const boost::system::error_code ec) {
122283ff9ab6SJames Feist                                     if (ec)
122383ff9ab6SJames Feist                                     {
122483ff9ab6SJames Feist                                         BMCWEB_LOG_ERROR
122583ff9ab6SJames Feist                                             << "Error Adding Pid Object " << ec;
122635a62c7cSJason M. Bills                                         messages::internalError(response->res);
1227b6baeaa4SJames Feist                                         return;
122883ff9ab6SJames Feist                                     }
1229b6baeaa4SJames Feist                                     messages::success(response->res);
123083ff9ab6SJames Feist                                 },
123183ff9ab6SJames Feist                                 "xyz.openbmc_project.EntityManager", chassis,
123283ff9ab6SJames Feist                                 "xyz.openbmc_project.AddObject", "AddObject",
123383ff9ab6SJames Feist                                 output);
123483ff9ab6SJames Feist                         }
123583ff9ab6SJames Feist                     }
123683ff9ab6SJames Feist                 }
123783ff9ab6SJames Feist             },
123883ff9ab6SJames Feist             "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
123983ff9ab6SJames Feist             "GetManagedObjects");
124083ff9ab6SJames Feist     }
12415b4aa86bSJames Feist 
12425b4aa86bSJames Feist     void doPatch(crow::Response& res, const crow::Request& req,
12435b4aa86bSJames Feist                  const std::vector<std::string>& params) override
12445b4aa86bSJames Feist     {
12450627a2c7SEd Tanous         std::optional<nlohmann::json> oem;
1246af5d6058SSantosh Puranik         std::optional<std::string> datetime;
12470627a2c7SEd Tanous 
1248af5d6058SSantosh Puranik         if (!json_util::readJson(req, res, "Oem", oem, "DateTime", datetime))
124983ff9ab6SJames Feist         {
125083ff9ab6SJames Feist             return;
125183ff9ab6SJames Feist         }
12520627a2c7SEd Tanous 
125383ff9ab6SJames Feist         std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
12540627a2c7SEd Tanous 
12550627a2c7SEd Tanous         if (oem)
125683ff9ab6SJames Feist         {
12575f2caaefSJames Feist             std::optional<nlohmann::json> openbmc;
125843b761d0SEd Tanous             if (!redfish::json_util::readJson(*oem, res, "OpenBmc", openbmc))
125983ff9ab6SJames Feist             {
126043b761d0SEd Tanous                 BMCWEB_LOG_ERROR << "Line:" << __LINE__ << ", Illegal Property "
126143b761d0SEd Tanous                                  << oem->dump();
126283ff9ab6SJames Feist                 return;
126383ff9ab6SJames Feist             }
12645f2caaefSJames Feist             if (openbmc)
126583ff9ab6SJames Feist             {
12665f2caaefSJames Feist                 std::optional<nlohmann::json> fan;
126743b761d0SEd Tanous                 if (!redfish::json_util::readJson(*openbmc, res, "Fan", fan))
126883ff9ab6SJames Feist                 {
12695f2caaefSJames Feist                     BMCWEB_LOG_ERROR << "Line:" << __LINE__
12705f2caaefSJames Feist                                      << ", Illegal Property "
12715f2caaefSJames Feist                                      << openbmc->dump();
127283ff9ab6SJames Feist                     return;
127383ff9ab6SJames Feist                 }
12745f2caaefSJames Feist                 if (fan)
127583ff9ab6SJames Feist                 {
12765f2caaefSJames Feist                     setPidValues(response, *fan);
127783ff9ab6SJames Feist                 }
127883ff9ab6SJames Feist             }
127983ff9ab6SJames Feist         }
1280af5d6058SSantosh Puranik         if (datetime)
1281af5d6058SSantosh Puranik         {
1282af5d6058SSantosh Puranik             setDateTime(response, std::move(*datetime));
1283af5d6058SSantosh Puranik         }
1284af5d6058SSantosh Puranik     }
1285af5d6058SSantosh Puranik 
1286af5d6058SSantosh Puranik     void setDateTime(std::shared_ptr<AsyncResp> aResp,
1287af5d6058SSantosh Puranik                      std::string datetime) const
1288af5d6058SSantosh Puranik     {
1289af5d6058SSantosh Puranik         BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1290af5d6058SSantosh Puranik 
1291af5d6058SSantosh Puranik         std::stringstream stream(datetime);
1292af5d6058SSantosh Puranik         // Convert from ISO 8601 to boost local_time
1293af5d6058SSantosh Puranik         // (BMC only has time in UTC)
1294af5d6058SSantosh Puranik         boost::posix_time::ptime posixTime;
1295af5d6058SSantosh Puranik         boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1296af5d6058SSantosh Puranik         // Facet gets deleted with the stringsteam
1297af5d6058SSantosh Puranik         auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1298af5d6058SSantosh Puranik             "%Y-%m-%d %H:%M:%S%F %ZP");
1299af5d6058SSantosh Puranik         stream.imbue(std::locale(stream.getloc(), ifc.release()));
1300af5d6058SSantosh Puranik 
1301af5d6058SSantosh Puranik         boost::local_time::local_date_time ldt(
1302af5d6058SSantosh Puranik             boost::local_time::not_a_date_time);
1303af5d6058SSantosh Puranik 
1304af5d6058SSantosh Puranik         if (stream >> ldt)
1305af5d6058SSantosh Puranik         {
1306af5d6058SSantosh Puranik             posixTime = ldt.utc_time();
1307af5d6058SSantosh Puranik             boost::posix_time::time_duration dur = posixTime - epoch;
1308af5d6058SSantosh Puranik             uint64_t durMicroSecs =
1309af5d6058SSantosh Puranik                 static_cast<uint64_t>(dur.total_microseconds());
1310af5d6058SSantosh Puranik             crow::connections::systemBus->async_method_call(
1311af5d6058SSantosh Puranik                 [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1312af5d6058SSantosh Puranik                     const boost::system::error_code ec) {
1313af5d6058SSantosh Puranik                     if (ec)
1314af5d6058SSantosh Puranik                     {
1315af5d6058SSantosh Puranik                         BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1316af5d6058SSantosh Puranik                                             "DBUS response error "
1317af5d6058SSantosh Puranik                                          << ec;
1318af5d6058SSantosh Puranik                         messages::internalError(aResp->res);
1319af5d6058SSantosh Puranik                         return;
1320af5d6058SSantosh Puranik                     }
1321af5d6058SSantosh Puranik                     aResp->res.jsonValue["DateTime"] = datetime;
1322af5d6058SSantosh Puranik                 },
1323af5d6058SSantosh Puranik                 "xyz.openbmc_project.Time.Manager",
1324af5d6058SSantosh Puranik                 "/xyz/openbmc_project/time/bmc",
1325af5d6058SSantosh Puranik                 "org.freedesktop.DBus.Properties", "Set",
1326af5d6058SSantosh Puranik                 "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1327af5d6058SSantosh Puranik                 std::variant<uint64_t>(durMicroSecs));
1328af5d6058SSantosh Puranik         }
1329af5d6058SSantosh Puranik         else
1330af5d6058SSantosh Puranik         {
1331af5d6058SSantosh Puranik             messages::propertyValueFormatError(aResp->res, datetime,
1332af5d6058SSantosh Puranik                                                "DateTime");
1333af5d6058SSantosh Puranik             return;
1334af5d6058SSantosh Puranik         }
133583ff9ab6SJames Feist     }
13369c310685SBorawski.Lukasz 
13370f74e643SEd Tanous     std::string uuid;
13389c310685SBorawski.Lukasz };
13399c310685SBorawski.Lukasz 
13401abe55efSEd Tanous class ManagerCollection : public Node
13411abe55efSEd Tanous {
13429c310685SBorawski.Lukasz   public:
13431abe55efSEd Tanous     ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
13441abe55efSEd Tanous     {
1345a434f2bdSEd Tanous         entityPrivileges = {
1346a434f2bdSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
1347e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
1348e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1349e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1350e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1351e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13529c310685SBorawski.Lukasz     }
13539c310685SBorawski.Lukasz 
13549c310685SBorawski.Lukasz   private:
135555c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
13561abe55efSEd Tanous                const std::vector<std::string>& params) override
13571abe55efSEd Tanous     {
135883ff9ab6SJames Feist         // Collections don't include the static data added by SubRoute
135983ff9ab6SJames Feist         // because it has a duplicate entry for members
136055c7b7a2SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
136155c7b7a2SEd Tanous         res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
136255c7b7a2SEd Tanous         res.jsonValue["@odata.context"] =
136355c7b7a2SEd Tanous             "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
136455c7b7a2SEd Tanous         res.jsonValue["Name"] = "Manager Collection";
136555c7b7a2SEd Tanous         res.jsonValue["Members@odata.count"] = 1;
136655c7b7a2SEd Tanous         res.jsonValue["Members"] = {
13675b4aa86bSJames Feist             {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
13689c310685SBorawski.Lukasz         res.end();
13699c310685SBorawski.Lukasz     }
13709c310685SBorawski.Lukasz };
13719c310685SBorawski.Lukasz } // namespace redfish
1372