1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "node.hpp"
19 
20 #include <boost/algorithm/string/replace.hpp>
21 #include <dbus_utility.hpp>
22 
23 namespace redfish
24 {
25 
26 /**
27  * ManagerActionsReset class supports handle POST method for Reset action.
28  * The class retrieves and sends data directly to dbus.
29  */
30 class ManagerActionsReset : public Node
31 {
32   public:
33     ManagerActionsReset(CrowApp& app) :
34         Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
35     {
36         entityPrivileges = {
37             {boost::beast::http::verb::get, {{"Login"}}},
38             {boost::beast::http::verb::head, {{"Login"}}},
39             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
40             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
41             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
42             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
43     }
44 
45   private:
46     /**
47      * Function handles POST method request.
48      * Analyzes POST body message before sends Reset request data to dbus.
49      * OpenBMC allows for ResetType is GracefulRestart only.
50      */
51     void doPost(crow::Response& res, const crow::Request& req,
52                 const std::vector<std::string>& params) override
53     {
54         std::string resetType;
55 
56         if (!json_util::readJson(req, res, "ResetType", resetType))
57         {
58             return;
59         }
60 
61         if (resetType != "GracefulRestart")
62         {
63             res.result(boost::beast::http::status::bad_request);
64             messages::actionParameterNotSupported(res, resetType, "ResetType");
65             BMCWEB_LOG_ERROR << "Request incorrect action parameter: "
66                              << resetType;
67             res.end();
68             return;
69         }
70         doBMCGracefulRestart(res, req, params);
71     }
72 
73     /**
74      * Function transceives data with dbus directly.
75      * All BMC state properties will be retrieved before sending reset request.
76      */
77     void doBMCGracefulRestart(crow::Response& res, const crow::Request& req,
78                               const std::vector<std::string>& params)
79     {
80         const char* processName = "xyz.openbmc_project.State.BMC";
81         const char* objectPath = "/xyz/openbmc_project/state/bmc0";
82         const char* interfaceName = "xyz.openbmc_project.State.BMC";
83         const std::string& propertyValue =
84             "xyz.openbmc_project.State.BMC.Transition.Reboot";
85         const char* destProperty = "RequestedBMCTransition";
86 
87         // Create the D-Bus variant for D-Bus call.
88         VariantType dbusPropertyValue(propertyValue);
89 
90         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
91 
92         crow::connections::systemBus->async_method_call(
93             [asyncResp](const boost::system::error_code ec) {
94                 // Use "Set" method to set the property value.
95                 if (ec)
96                 {
97                     BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec;
98                     messages::internalError(asyncResp->res);
99                     return;
100                 }
101 
102                 messages::success(asyncResp->res);
103             },
104             processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
105             interfaceName, destProperty, dbusPropertyValue);
106     }
107 };
108 
109 static constexpr const char* objectManagerIface =
110     "org.freedesktop.DBus.ObjectManager";
111 static constexpr const char* pidConfigurationIface =
112     "xyz.openbmc_project.Configuration.Pid";
113 static constexpr const char* pidZoneConfigurationIface =
114     "xyz.openbmc_project.Configuration.Pid.Zone";
115 
116 static void asyncPopulatePid(const std::string& connection,
117                              const std::string& path,
118                              std::shared_ptr<AsyncResp> asyncResp)
119 {
120 
121     crow::connections::systemBus->async_method_call(
122         [asyncResp](const boost::system::error_code ec,
123                     const dbus::utility::ManagedObjectType& managedObj) {
124             if (ec)
125             {
126                 BMCWEB_LOG_ERROR << ec;
127                 asyncResp->res.jsonValue.clear();
128                 messages::internalError(asyncResp->res);
129                 return;
130             }
131             nlohmann::json& configRoot =
132                 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
133             nlohmann::json& fans = configRoot["FanControllers"];
134             fans["@odata.type"] = "#OemManager.FanControllers";
135             fans["@odata.context"] =
136                 "/redfish/v1/$metadata#OemManager.FanControllers";
137             fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
138                                 "Fan/FanControllers";
139 
140             nlohmann::json& pids = configRoot["PidControllers"];
141             pids["@odata.type"] = "#OemManager.PidControllers";
142             pids["@odata.context"] =
143                 "/redfish/v1/$metadata#OemManager.PidControllers";
144             pids["@odata.id"] =
145                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
146 
147             nlohmann::json& zones = configRoot["FanZones"];
148             zones["@odata.id"] =
149                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
150             zones["@odata.type"] = "#OemManager.FanZones";
151             zones["@odata.context"] =
152                 "/redfish/v1/$metadata#OemManager.FanZones";
153             configRoot["@odata.id"] =
154                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
155             configRoot["@odata.type"] = "#OemManager.Fan";
156             configRoot["@odata.context"] =
157                 "/redfish/v1/$metadata#OemManager.Fan";
158 
159             bool propertyError = false;
160             for (const auto& pathPair : managedObj)
161             {
162                 for (const auto& intfPair : pathPair.second)
163                 {
164                     if (intfPair.first != pidConfigurationIface &&
165                         intfPair.first != pidZoneConfigurationIface)
166                     {
167                         continue;
168                     }
169                     auto findName = intfPair.second.find("Name");
170                     if (findName == intfPair.second.end())
171                     {
172                         BMCWEB_LOG_ERROR << "Pid Field missing Name";
173                         messages::internalError(asyncResp->res);
174                         return;
175                     }
176                     const std::string* namePtr =
177                         sdbusplus::message::variant_ns::get_if<std::string>(
178                             &findName->second);
179                     if (namePtr == nullptr)
180                     {
181                         BMCWEB_LOG_ERROR << "Pid Name Field illegal";
182                         return;
183                     }
184 
185                     std::string name = *namePtr;
186                     dbus::utility::escapePathForDbus(name);
187                     if (intfPair.first == pidZoneConfigurationIface)
188                     {
189                         std::string chassis;
190                         if (!dbus::utility::getNthStringFromPath(
191                                 pathPair.first.str, 5, chassis))
192                         {
193                             chassis = "#IllegalValue";
194                         }
195                         nlohmann::json& zone = zones[name];
196                         zone["Chassis"] = {
197                             {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
198                         zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
199                                             "OpenBmc/Fan/FanZones/" +
200                                             name;
201                         zone["@odata.type"] = "#OemManager.FanZone";
202                         zone["@odata.context"] =
203                             "/redfish/v1/$metadata#OemManager.FanZone";
204                     }
205 
206                     for (const auto& propertyPair : intfPair.second)
207                     {
208                         if (propertyPair.first == "Type" ||
209                             propertyPair.first == "Class" ||
210                             propertyPair.first == "Name")
211                         {
212                             continue;
213                         }
214 
215                         // zones
216                         if (intfPair.first == pidZoneConfigurationIface)
217                         {
218                             const double* ptr =
219                                 sdbusplus::message::variant_ns::get_if<double>(
220                                     &propertyPair.second);
221                             if (ptr == nullptr)
222                             {
223                                 BMCWEB_LOG_ERROR << "Field Illegal "
224                                                  << propertyPair.first;
225                                 messages::internalError(asyncResp->res);
226                                 return;
227                             }
228                             zones[name][propertyPair.first] = *ptr;
229                         }
230 
231                         // pid and fans are off the same configuration
232                         if (intfPair.first == pidConfigurationIface)
233                         {
234                             const std::string* classPtr = nullptr;
235                             auto findClass = intfPair.second.find("Class");
236                             if (findClass != intfPair.second.end())
237                             {
238                                 classPtr =
239                                     sdbusplus::message::variant_ns::get_if<
240                                         std::string>(&findClass->second);
241                             }
242                             if (classPtr == nullptr)
243                             {
244                                 BMCWEB_LOG_ERROR << "Pid Class Field illegal";
245                                 messages::internalError(asyncResp->res);
246                                 return;
247                             }
248                             bool isFan = *classPtr == "fan";
249                             nlohmann::json& element =
250                                 isFan ? fans[name] : pids[name];
251                             if (isFan)
252                             {
253                                 element["@odata.id"] =
254                                     "/redfish/v1/Managers/bmc#/Oem/"
255                                     "OpenBmc/Fan/FanControllers/" +
256                                     std::string(name);
257                                 element["@odata.type"] =
258                                     "#OemManager.FanController";
259 
260                                 element["@odata.context"] =
261                                     "/redfish/v1/"
262                                     "$metadata#OemManager.FanController";
263                             }
264                             else
265                             {
266                                 element["@odata.id"] =
267                                     "/redfish/v1/Managers/bmc#/Oem/"
268                                     "OpenBmc/Fan/PidControllers/" +
269                                     std::string(name);
270                                 element["@odata.type"] =
271                                     "#OemManager.PidController";
272                                 element["@odata.context"] =
273                                     "/redfish/v1/$metadata"
274                                     "#OemManager.PidController";
275                             }
276 
277                             if (propertyPair.first == "Zones")
278                             {
279                                 const std::vector<std::string>* inputs =
280                                     sdbusplus::message::variant_ns::get_if<
281                                         std::vector<std::string>>(
282                                         &propertyPair.second);
283 
284                                 if (inputs == nullptr)
285                                 {
286                                     BMCWEB_LOG_ERROR
287                                         << "Zones Pid Field Illegal";
288                                     messages::internalError(asyncResp->res);
289                                     return;
290                                 }
291                                 auto& data = element[propertyPair.first];
292                                 data = nlohmann::json::array();
293                                 for (std::string itemCopy : *inputs)
294                                 {
295                                     dbus::utility::escapePathForDbus(itemCopy);
296                                     data.push_back(
297                                         {{"@odata.id",
298                                           "/redfish/v1/Managers/bmc#/Oem/"
299                                           "OpenBmc/Fan/FanZones/" +
300                                               itemCopy}});
301                                 }
302                             }
303                             // todo(james): may never happen, but this
304                             // assumes configuration data referenced in the
305                             // PID config is provided by the same daemon, we
306                             // could add another loop to cover all cases,
307                             // but I'm okay kicking this can down the road a
308                             // bit
309 
310                             else if (propertyPair.first == "Inputs" ||
311                                      propertyPair.first == "Outputs")
312                             {
313                                 auto& data = element[propertyPair.first];
314                                 const std::vector<std::string>* inputs =
315                                     sdbusplus::message::variant_ns::get_if<
316                                         std::vector<std::string>>(
317                                         &propertyPair.second);
318 
319                                 if (inputs == nullptr)
320                                 {
321                                     BMCWEB_LOG_ERROR << "Field Illegal "
322                                                      << propertyPair.first;
323                                     messages::internalError(asyncResp->res);
324                                     return;
325                                 }
326                                 data = *inputs;
327                             } // doubles
328                             else if (propertyPair.first ==
329                                          "FFGainCoefficient" ||
330                                      propertyPair.first == "FFOffCoefficient" ||
331                                      propertyPair.first == "ICoefficient" ||
332                                      propertyPair.first == "ILimitMax" ||
333                                      propertyPair.first == "ILimitMin" ||
334                                      propertyPair.first == "OutLimitMax" ||
335                                      propertyPair.first == "OutLimitMin" ||
336                                      propertyPair.first == "PCoefficient" ||
337                                      propertyPair.first == "SlewNeg" ||
338                                      propertyPair.first == "SlewPos")
339                             {
340                                 const double* ptr =
341                                     sdbusplus::message::variant_ns::get_if<
342                                         double>(&propertyPair.second);
343                                 if (ptr == nullptr)
344                                 {
345                                     BMCWEB_LOG_ERROR << "Field Illegal "
346                                                      << propertyPair.first;
347                                     messages::internalError(asyncResp->res);
348                                     return;
349                                 }
350                                 element[propertyPair.first] = *ptr;
351                             }
352                         }
353                     }
354                 }
355             }
356         },
357         connection, path, objectManagerIface, "GetManagedObjects");
358 }
359 
360 enum class CreatePIDRet
361 {
362     fail,
363     del,
364     patch
365 };
366 
367 static CreatePIDRet createPidInterface(
368     const std::shared_ptr<AsyncResp>& response, const std::string& type,
369     const nlohmann::json& record, const std::string& path,
370     const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
371     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
372         output,
373     std::string& chassis)
374 {
375 
376     if (type == "PidControllers" || type == "FanControllers")
377     {
378         if (createNewObject)
379         {
380             output["Class"] = type == "PidControllers" ? std::string("temp")
381                                                        : std::string("fan");
382             output["Type"] = std::string("Pid");
383         }
384         else if (record == nullptr)
385         {
386             // delete interface
387             crow::connections::systemBus->async_method_call(
388                 [response,
389                  path{std::string(path)}](const boost::system::error_code ec) {
390                     if (ec)
391                     {
392                         BMCWEB_LOG_ERROR << "Error patching " << path << ": "
393                                          << ec;
394                         messages::internalError(response->res);
395                     }
396                 },
397                 "xyz.openbmc_project.EntityManager", path,
398                 pidConfigurationIface, "Delete");
399             return CreatePIDRet::del;
400         }
401 
402         for (auto& field : record.items())
403         {
404             if (field.key() == "Zones")
405             {
406                 if (!field.value().is_array())
407                 {
408                     BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
409                     messages::propertyValueFormatError(
410                         response->res, field.value(), field.key());
411                     return CreatePIDRet::fail;
412                 }
413                 std::vector<std::string> inputs;
414                 for (const auto& odata : field.value().items())
415                 {
416                     for (const auto& value : odata.value().items())
417                     {
418                         const std::string* path =
419                             value.value().get_ptr<const std::string*>();
420                         if (path == nullptr)
421                         {
422                             BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
423                             messages::propertyValueFormatError(
424                                 response->res, field.value().dump(),
425                                 field.key());
426                             return CreatePIDRet::fail;
427                         }
428                         std::string input;
429                         if (!dbus::utility::getNthStringFromPath(*path, 4,
430                                                                  input))
431                         {
432                             BMCWEB_LOG_ERROR << "Got invalid path " << *path;
433                             messages::propertyValueFormatError(
434                                 response->res, field.value().dump(),
435                                 field.key());
436                             return CreatePIDRet::fail;
437                         }
438                         boost::replace_all(input, "_", " ");
439                         inputs.emplace_back(std::move(input));
440                     }
441                 }
442                 output["Zones"] = std::move(inputs);
443             }
444             else if (field.key() == "Inputs" || field.key() == "Outputs")
445             {
446                 if (!field.value().is_array())
447                 {
448                     BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
449                     messages::propertyValueFormatError(
450                         response->res, field.value().dump(), field.key());
451                     return CreatePIDRet::fail;
452                 }
453                 std::vector<std::string> inputs;
454                 for (const auto& value : field.value().items())
455                 {
456                     const std::string* sensor =
457                         value.value().get_ptr<const std::string*>();
458 
459                     if (sensor == nullptr)
460                     {
461                         BMCWEB_LOG_ERROR << "Illegal Type "
462                                          << field.value().dump();
463                         messages::propertyValueFormatError(
464                             response->res, field.value().dump(), field.key());
465                         return CreatePIDRet::fail;
466                     }
467 
468                     std::string input =
469                         boost::replace_all_copy(*sensor, "_", " ");
470                     inputs.push_back(std::move(input));
471                     // try to find the sensor in the
472                     // configuration
473                     if (chassis.empty())
474                     {
475                         std::find_if(
476                             managedObj.begin(), managedObj.end(),
477                             [&chassis, sensor](const auto& obj) {
478                                 if (boost::algorithm::ends_with(obj.first.str,
479                                                                 *sensor))
480                                 {
481                                     return dbus::utility::getNthStringFromPath(
482                                         obj.first.str, 5, chassis);
483                                 }
484                                 return false;
485                             });
486                     }
487                 }
488                 output[field.key()] = inputs;
489             }
490 
491             // doubles
492             else if (field.key() == "FFGainCoefficient" ||
493                      field.key() == "FFOffCoefficient" ||
494                      field.key() == "ICoefficient" ||
495                      field.key() == "ILimitMax" || field.key() == "ILimitMin" ||
496                      field.key() == "OutLimitMax" ||
497                      field.key() == "OutLimitMin" ||
498                      field.key() == "PCoefficient" ||
499                      field.key() == "SetPoint" || field.key() == "SlewNeg" ||
500                      field.key() == "SlewPos")
501             {
502                 const double* ptr = field.value().get_ptr<const double*>();
503                 if (ptr == nullptr)
504                 {
505                     BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
506                     messages::propertyValueFormatError(
507                         response->res, field.value().dump(), field.key());
508                     return CreatePIDRet::fail;
509                 }
510                 output[field.key()] = *ptr;
511             }
512 
513             else
514             {
515                 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
516                 messages::propertyUnknown(response->res, field.key());
517                 return CreatePIDRet::fail;
518             }
519         }
520     }
521     else if (type == "FanZones")
522     {
523         if (!createNewObject && record == nullptr)
524         {
525             // delete interface
526             crow::connections::systemBus->async_method_call(
527                 [response,
528                  path{std::string(path)}](const boost::system::error_code ec) {
529                     if (ec)
530                     {
531                         BMCWEB_LOG_ERROR << "Error patching " << path << ": "
532                                          << ec;
533                         messages::internalError(response->res);
534                     }
535                 },
536                 "xyz.openbmc_project.EntityManager", path,
537                 pidZoneConfigurationIface, "Delete");
538             return CreatePIDRet::del;
539         }
540         output["Type"] = std::string("Pid.Zone");
541 
542         for (auto& field : record.items())
543         {
544             if (field.key() == "Chassis")
545             {
546                 const std::string* chassisId = nullptr;
547                 for (const auto& id : field.value().items())
548                 {
549                     if (id.key() != "@odata.id")
550                     {
551                         BMCWEB_LOG_ERROR << "Illegal Type " << id.key();
552                         messages::propertyUnknown(response->res, field.key());
553                         return CreatePIDRet::fail;
554                     }
555                     chassisId = id.value().get_ptr<const std::string*>();
556                     if (chassisId == nullptr)
557                     {
558                         messages::createFailedMissingReqProperties(
559                             response->res, field.key());
560                         return CreatePIDRet::fail;
561                     }
562                 }
563 
564                 // /refish/v1/chassis/chassis_name/
565                 if (!dbus::utility::getNthStringFromPath(*chassisId, 3,
566                                                          chassis))
567                 {
568                     BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId;
569                     messages::invalidObject(response->res, *chassisId);
570                     return CreatePIDRet::fail;
571                 }
572             }
573             else if (field.key() == "FailSafePercent" ||
574                      field.key() == "MinThermalRpm")
575             {
576                 const double* ptr = field.value().get_ptr<const double*>();
577                 if (ptr == nullptr)
578                 {
579                     BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
580                     messages::propertyValueFormatError(
581                         response->res, field.value().dump(), field.key());
582                     return CreatePIDRet::fail;
583                 }
584                 output[field.key()] = *ptr;
585             }
586             else
587             {
588                 BMCWEB_LOG_ERROR << "Illegal Type " << field.key();
589                 messages::propertyUnknown(response->res, field.key());
590                 return CreatePIDRet::fail;
591             }
592         }
593     }
594     else
595     {
596         BMCWEB_LOG_ERROR << "Illegal Type " << type;
597         messages::propertyUnknown(response->res, type);
598         return CreatePIDRet::fail;
599     }
600     return CreatePIDRet::patch;
601 }
602 
603 class Manager : public Node
604 {
605   public:
606     Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/")
607     {
608         uuid = app.template getMiddleware<crow::persistent_data::Middleware>()
609                    .systemUuid;
610         entityPrivileges = {
611             {boost::beast::http::verb::get, {{"Login"}}},
612             {boost::beast::http::verb::head, {{"Login"}}},
613             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
614             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
615             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
616             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
617     }
618 
619   private:
620     void getPidValues(std::shared_ptr<AsyncResp> asyncResp)
621     {
622         crow::connections::systemBus->async_method_call(
623             [asyncResp](const boost::system::error_code ec,
624                         const crow::openbmc_mapper::GetSubTreeType& subtree) {
625                 if (ec)
626                 {
627                     BMCWEB_LOG_ERROR << ec;
628                     messages::internalError(asyncResp->res);
629                     return;
630                 }
631 
632                 // create map of <connection, path to objMgr>>
633                 boost::container::flat_map<std::string, std::string>
634                     objectMgrPaths;
635                 boost::container::flat_set<std::string> calledConnections;
636                 for (const auto& pathGroup : subtree)
637                 {
638                     for (const auto& connectionGroup : pathGroup.second)
639                     {
640                         auto findConnection =
641                             calledConnections.find(connectionGroup.first);
642                         if (findConnection != calledConnections.end())
643                         {
644                             break;
645                         }
646                         for (const std::string& interface :
647                              connectionGroup.second)
648                         {
649                             if (interface == objectManagerIface)
650                             {
651                                 objectMgrPaths[connectionGroup.first] =
652                                     pathGroup.first;
653                             }
654                             // this list is alphabetical, so we
655                             // should have found the objMgr by now
656                             if (interface == pidConfigurationIface ||
657                                 interface == pidZoneConfigurationIface)
658                             {
659                                 auto findObjMgr =
660                                     objectMgrPaths.find(connectionGroup.first);
661                                 if (findObjMgr == objectMgrPaths.end())
662                                 {
663                                     BMCWEB_LOG_DEBUG << connectionGroup.first
664                                                      << "Has no Object Manager";
665                                     continue;
666                                 }
667 
668                                 calledConnections.insert(connectionGroup.first);
669 
670                                 asyncPopulatePid(findObjMgr->first,
671                                                  findObjMgr->second, asyncResp);
672                                 break;
673                             }
674                         }
675                     }
676                 }
677             },
678             "xyz.openbmc_project.ObjectMapper",
679             "/xyz/openbmc_project/object_mapper",
680             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
681             std::array<const char*, 3>{pidConfigurationIface,
682                                        pidZoneConfigurationIface,
683                                        objectManagerIface});
684     }
685 
686     void doGet(crow::Response& res, const crow::Request& req,
687                const std::vector<std::string>& params) override
688     {
689         res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
690         res.jsonValue["@odata.type"] = "#Manager.v1_3_0.Manager";
691         res.jsonValue["@odata.context"] =
692             "/redfish/v1/$metadata#Manager.Manager";
693         res.jsonValue["Id"] = "bmc";
694         res.jsonValue["Name"] = "OpenBmc Manager";
695         res.jsonValue["Description"] = "Baseboard Management Controller";
696         res.jsonValue["PowerState"] = "On";
697         res.jsonValue["ManagerType"] = "BMC";
698         res.jsonValue["UUID"] = uuid;
699         res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
700 
701         res.jsonValue["LogServices"] = {
702             {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
703 
704         res.jsonValue["NetworkProtocol"] = {
705             {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
706 
707         res.jsonValue["EthernetInterfaces"] = {
708             {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
709         // default oem data
710         nlohmann::json& oem = res.jsonValue["Oem"];
711         nlohmann::json& oemOpenbmc = oem["OpenBmc"];
712         oem["@odata.type"] = "#OemManager.Oem";
713         oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
714         oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem";
715         oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
716         oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
717         oemOpenbmc["@odata.context"] =
718             "/redfish/v1/$metadata#OemManager.OpenBmc";
719 
720         // Update Actions object.
721         nlohmann::json& manager_reset =
722             res.jsonValue["Actions"]["#Manager.Reset"];
723         manager_reset["target"] =
724             "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
725         manager_reset["ResetType@Redfish.AllowableValues"] = {
726             "GracefulRestart"};
727 
728         res.jsonValue["DateTime"] = getDateTime();
729         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
730 
731         crow::connections::systemBus->async_method_call(
732             [asyncResp](const boost::system::error_code ec,
733                         const dbus::utility::ManagedObjectType& resp) {
734                 if (ec)
735                 {
736                     BMCWEB_LOG_ERROR << "Error while getting Software Version";
737                     messages::internalError(asyncResp->res);
738                     return;
739                 }
740 
741                 for (auto& objpath : resp)
742                 {
743                     for (auto& interface : objpath.second)
744                     {
745                         // If interface is xyz.openbmc_project.Software.Version,
746                         // this is what we're looking for.
747                         if (interface.first ==
748                             "xyz.openbmc_project.Software.Version")
749                         {
750                             // Cut out everyting until last "/", ...
751                             const std::string& iface_id = objpath.first;
752                             for (auto& property : interface.second)
753                             {
754                                 if (property.first == "Version")
755                                 {
756                                     const std::string* value =
757                                         sdbusplus::message::variant_ns::get_if<
758                                             std::string>(&property.second);
759                                     if (value == nullptr)
760                                     {
761                                         continue;
762                                     }
763                                     asyncResp->res
764                                         .jsonValue["FirmwareVersion"] = *value;
765                                 }
766                             }
767                         }
768                     }
769                 }
770             },
771             "xyz.openbmc_project.Software.BMC.Updater",
772             "/xyz/openbmc_project/software",
773             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
774         getPidValues(asyncResp);
775     }
776     void setPidValues(std::shared_ptr<AsyncResp> response,
777                       const nlohmann::json& data)
778     {
779         // todo(james): might make sense to do a mapper call here if this
780         // interface gets more traction
781         crow::connections::systemBus->async_method_call(
782             [response,
783              data](const boost::system::error_code ec,
784                    const dbus::utility::ManagedObjectType& managedObj) {
785                 if (ec)
786                 {
787                     BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
788                     messages::internalError(response->res);
789                     return;
790                 }
791                 for (const auto& type : data.items())
792                 {
793                     if (!type.value().is_object())
794                     {
795                         BMCWEB_LOG_ERROR << "Illegal Type " << type.key();
796                         messages::propertyValueFormatError(
797                             response->res, type.value(), type.key());
798                         return;
799                     }
800                     for (const auto& record : type.value().items())
801                     {
802                         const std::string& name = record.key();
803                         auto pathItr =
804                             std::find_if(managedObj.begin(), managedObj.end(),
805                                          [&name](const auto& obj) {
806                                              return boost::algorithm::ends_with(
807                                                  obj.first.str, name);
808                                          });
809                         boost::container::flat_map<
810                             std::string, dbus::utility::DbusVariantType>
811                             output;
812 
813                         output.reserve(16); // The pid interface length
814 
815                         // determines if we're patching entity-manager or
816                         // creating a new object
817                         bool createNewObject = (pathItr == managedObj.end());
818                         if (type.key() == "PidControllers" ||
819                             type.key() == "FanControllers")
820                         {
821                             if (!createNewObject &&
822                                 pathItr->second.find(pidConfigurationIface) ==
823                                     pathItr->second.end())
824                             {
825                                 createNewObject = true;
826                             }
827                         }
828                         else if (!createNewObject &&
829                                  pathItr->second.find(
830                                      pidZoneConfigurationIface) ==
831                                      pathItr->second.end())
832                         {
833                             createNewObject = true;
834                         }
835                         output["Name"] =
836                             boost::replace_all_copy(name, "_", " ");
837 
838                         std::string chassis;
839                         CreatePIDRet ret = createPidInterface(
840                             response, type.key(), record.value(),
841                             pathItr->first.str, managedObj, createNewObject,
842                             output, chassis);
843                         if (ret == CreatePIDRet::fail)
844                         {
845                             return;
846                         }
847                         else if (ret == CreatePIDRet::del)
848                         {
849                             continue;
850                         }
851 
852                         if (!createNewObject)
853                         {
854                             for (const auto& property : output)
855                             {
856                                 const char* iface =
857                                     type.key() == "FanZones"
858                                         ? pidZoneConfigurationIface
859                                         : pidConfigurationIface;
860                                 crow::connections::systemBus->async_method_call(
861                                     [response,
862                                      propertyName{std::string(property.first)}](
863                                         const boost::system::error_code ec) {
864                                         if (ec)
865                                         {
866                                             BMCWEB_LOG_ERROR
867                                                 << "Error patching "
868                                                 << propertyName << ": " << ec;
869                                             messages::internalError(
870                                                 response->res);
871                                         }
872                                     },
873                                     "xyz.openbmc_project.EntityManager",
874                                     pathItr->first.str,
875                                     "org.freedesktop.DBus.Properties", "Set",
876                                     std::string(iface), property.first,
877                                     property.second);
878                             }
879                         }
880                         else
881                         {
882                             if (chassis.empty())
883                             {
884                                 BMCWEB_LOG_ERROR
885                                     << "Failed to get chassis from config";
886                                 messages::invalidObject(response->res, name);
887                                 return;
888                             }
889 
890                             bool foundChassis = false;
891                             for (const auto& obj : managedObj)
892                             {
893                                 if (boost::algorithm::ends_with(obj.first.str,
894                                                                 chassis))
895                                 {
896                                     chassis = obj.first.str;
897                                     foundChassis = true;
898                                     break;
899                                 }
900                             }
901                             if (!foundChassis)
902                             {
903                                 BMCWEB_LOG_ERROR
904                                     << "Failed to find chassis on dbus";
905                                 messages::resourceMissingAtURI(
906                                     response->res,
907                                     "/redfish/v1/Chassis/" + chassis);
908                                 return;
909                             }
910 
911                             crow::connections::systemBus->async_method_call(
912                                 [response](const boost::system::error_code ec) {
913                                     if (ec)
914                                     {
915                                         BMCWEB_LOG_ERROR
916                                             << "Error Adding Pid Object " << ec;
917                                         messages::internalError(response->res);
918                                     }
919                                 },
920                                 "xyz.openbmc_project.EntityManager", chassis,
921                                 "xyz.openbmc_project.AddObject", "AddObject",
922                                 output);
923                         }
924                     }
925                 }
926             },
927             "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
928             "GetManagedObjects");
929     }
930 
931     void doPatch(crow::Response& res, const crow::Request& req,
932                  const std::vector<std::string>& params) override
933     {
934         std::optional<nlohmann::json> oem;
935 
936         if (!json_util::readJson(req, res, "Oem", oem))
937         {
938             return;
939         }
940 
941         std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res);
942 
943         if (oem)
944         {
945             for (const auto& oemLevel : oem->items())
946             {
947                 if (oemLevel.key() == "OpenBmc")
948                 {
949                     if (!oemLevel.value().is_object())
950                     {
951                         BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
952                         messages::propertyValueFormatError(
953                             response->res, "Oem", "OemManager.OpenBmc");
954                         return;
955                     }
956                     for (const auto& typeLevel : oemLevel.value().items())
957                     {
958 
959                         if (typeLevel.key() == "Fan")
960                         {
961                             if (!typeLevel.value().is_object())
962                             {
963                                 BMCWEB_LOG_ERROR << "Bad Patch "
964                                                  << typeLevel.key();
965                                 messages::propertyValueFormatError(
966                                     response->res, typeLevel.value().dump(),
967                                     typeLevel.key());
968                                 return;
969                             }
970                             setPidValues(response,
971                                          std::move(typeLevel.value()));
972                         }
973                         else
974                         {
975                             BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key();
976                             messages::propertyUnknown(response->res,
977                                                       typeLevel.key());
978                             return;
979                         }
980                     }
981                 }
982                 else
983                 {
984                     BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key();
985                     messages::propertyUnknown(response->res, oemLevel.key());
986                     return;
987                 }
988             }
989         }
990     }
991 
992     std::string getDateTime() const
993     {
994         std::array<char, 128> dateTime;
995         std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
996         std::time_t time = std::time(nullptr);
997 
998         if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
999                           std::localtime(&time)))
1000         {
1001             // insert the colon required by the ISO 8601 standard
1002             redfishDateTime = std::string(dateTime.data());
1003             redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1004         }
1005 
1006         return redfishDateTime;
1007     }
1008 
1009     std::string uuid;
1010 };
1011 
1012 class ManagerCollection : public Node
1013 {
1014   public:
1015     ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/")
1016     {
1017         entityPrivileges = {
1018             {boost::beast::http::verb::get, {{"Login"}}},
1019             {boost::beast::http::verb::head, {{"Login"}}},
1020             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1021             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1022             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1023             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1024     }
1025 
1026   private:
1027     void doGet(crow::Response& res, const crow::Request& req,
1028                const std::vector<std::string>& params) override
1029     {
1030         // Collections don't include the static data added by SubRoute
1031         // because it has a duplicate entry for members
1032         res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
1033         res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection";
1034         res.jsonValue["@odata.context"] =
1035             "/redfish/v1/$metadata#ManagerCollection.ManagerCollection";
1036         res.jsonValue["Name"] = "Manager Collection";
1037         res.jsonValue["Members@odata.count"] = 1;
1038         res.jsonValue["Members"] = {
1039             {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
1040         res.end();
1041     }
1042 };
1043 } // namespace redfish
1044