xref: /openbmc/bmcweb/features/redfish/lib/managers.hpp (revision 7c8c4058e265f784679b9144ff33b89f50f1bf59)
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 "health.hpp"
19 #include "redfish_util.hpp"
20 
21 #include <app.hpp>
22 #include <boost/algorithm/string/replace.hpp>
23 #include <boost/date_time.hpp>
24 #include <dbus_utility.hpp>
25 #include <utils/fw_utils.hpp>
26 #include <utils/systemd_utils.hpp>
27 
28 #include <cstdint>
29 #include <memory>
30 #include <sstream>
31 #include <variant>
32 
33 namespace redfish
34 {
35 
36 /**
37  * Function reboots the BMC.
38  *
39  * @param[in] asyncResp - Shared pointer for completing asynchronous calls
40  */
41 inline void
42     doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
43 {
44     const char* processName = "xyz.openbmc_project.State.BMC";
45     const char* objectPath = "/xyz/openbmc_project/state/bmc0";
46     const char* interfaceName = "xyz.openbmc_project.State.BMC";
47     const std::string& propertyValue =
48         "xyz.openbmc_project.State.BMC.Transition.Reboot";
49     const char* destProperty = "RequestedBMCTransition";
50 
51     // Create the D-Bus variant for D-Bus call.
52     VariantType dbusPropertyValue(propertyValue);
53 
54     crow::connections::systemBus->async_method_call(
55         [asyncResp](const boost::system::error_code ec) {
56             // Use "Set" method to set the property value.
57             if (ec)
58             {
59                 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
60                 messages::internalError(asyncResp->res);
61                 return;
62             }
63 
64             messages::success(asyncResp->res);
65         },
66         processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
67         interfaceName, destProperty, dbusPropertyValue);
68 }
69 
70 inline void
71     doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
72 {
73     const char* processName = "xyz.openbmc_project.State.BMC";
74     const char* objectPath = "/xyz/openbmc_project/state/bmc0";
75     const char* interfaceName = "xyz.openbmc_project.State.BMC";
76     const std::string& propertyValue =
77         "xyz.openbmc_project.State.BMC.Transition.HardReboot";
78     const char* destProperty = "RequestedBMCTransition";
79 
80     // Create the D-Bus variant for D-Bus call.
81     VariantType dbusPropertyValue(propertyValue);
82 
83     crow::connections::systemBus->async_method_call(
84         [asyncResp](const boost::system::error_code ec) {
85             // Use "Set" method to set the property value.
86             if (ec)
87             {
88                 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec;
89                 messages::internalError(asyncResp->res);
90                 return;
91             }
92 
93             messages::success(asyncResp->res);
94         },
95         processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
96         interfaceName, destProperty, dbusPropertyValue);
97 }
98 
99 /**
100  * ManagerResetAction class supports the POST method for the Reset (reboot)
101  * action.
102  */
103 inline void requestRoutesManagerResetAction(App& app)
104 {
105     /**
106      * Function handles POST method request.
107      * Analyzes POST body before sending Reset (Reboot) request data to D-Bus.
108      * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart".
109      */
110 
111     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/")
112         .privileges({{"ConfigureManager"}})
113         .methods(boost::beast::http::verb::post)(
114             [](const crow::Request& req,
115                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
116                 BMCWEB_LOG_DEBUG << "Post Manager Reset.";
117 
118                 std::string resetType;
119 
120                 if (!json_util::readJson(req, asyncResp->res, "ResetType",
121                                          resetType))
122                 {
123                     return;
124                 }
125 
126                 if (resetType == "GracefulRestart")
127                 {
128                     BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
129                     doBMCGracefulRestart(asyncResp);
130                     return;
131                 }
132                 if (resetType == "ForceRestart")
133                 {
134                     BMCWEB_LOG_DEBUG << "Proceeding with " << resetType;
135                     doBMCForceRestart(asyncResp);
136                     return;
137                 }
138                 BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
139                                  << resetType;
140                 messages::actionParameterNotSupported(asyncResp->res, resetType,
141                                                       "ResetType");
142 
143                 return;
144             });
145 }
146 
147 /**
148  * ManagerResetToDefaultsAction class supports POST method for factory reset
149  * action.
150  */
151 inline void requestRoutesManagerResetToDefaultsAction(App& app)
152 {
153 
154     /**
155      * Function handles ResetToDefaults POST method request.
156      *
157      * Analyzes POST body message and factory resets BMC by calling
158      * BMC code updater factory reset followed by a BMC reboot.
159      *
160      * BMC code updater factory reset wipes the whole BMC read-write
161      * filesystem which includes things like the network settings.
162      *
163      * OpenBMC only supports ResetToDefaultsType "ResetAll".
164      */
165 
166     BMCWEB_ROUTE(app,
167                  "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults/")
168         .privileges({{"ConfigureManager"}})
169         .methods(boost::beast::http::verb::post)(
170             [](const crow::Request& req,
171                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
172                 BMCWEB_LOG_DEBUG << "Post ResetToDefaults.";
173 
174                 std::string resetType;
175 
176                 if (!json_util::readJson(req, asyncResp->res,
177                                          "ResetToDefaultsType", resetType))
178                 {
179                     BMCWEB_LOG_DEBUG << "Missing property ResetToDefaultsType.";
180 
181                     messages::actionParameterMissing(asyncResp->res,
182                                                      "ResetToDefaults",
183                                                      "ResetToDefaultsType");
184                     return;
185                 }
186 
187                 if (resetType != "ResetAll")
188                 {
189                     BMCWEB_LOG_DEBUG << "Invalid property value for "
190                                         "ResetToDefaultsType: "
191                                      << resetType;
192                     messages::actionParameterNotSupported(
193                         asyncResp->res, resetType, "ResetToDefaultsType");
194                     return;
195                 }
196 
197                 crow::connections::systemBus->async_method_call(
198                     [asyncResp](const boost::system::error_code ec) {
199                         if (ec)
200                         {
201                             BMCWEB_LOG_DEBUG << "Failed to ResetToDefaults: "
202                                              << ec;
203                             messages::internalError(asyncResp->res);
204                             return;
205                         }
206                         // Factory Reset doesn't actually happen until a reboot
207                         // Can't erase what the BMC is running on
208                         doBMCGracefulRestart(asyncResp);
209                     },
210                     "xyz.openbmc_project.Software.BMC.Updater",
211                     "/xyz/openbmc_project/software",
212                     "xyz.openbmc_project.Common.FactoryReset", "Reset");
213             });
214 }
215 
216 /**
217  * ManagerResetActionInfo derived class for delivering Manager
218  * ResetType AllowableValues using ResetInfo schema.
219  */
220 inline void requestRoutesManagerResetActionInfo(App& app)
221 {
222     /**
223      * Functions triggers appropriate requests on DBus
224      */
225 
226     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/ResetActionInfo/")
227         .privileges({{"Login"}})
228         .methods(boost::beast::http::verb::get)(
229             [](const crow::Request&,
230                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
231                 asyncResp->res.jsonValue = {
232                     {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"},
233                     {"@odata.id", "/redfish/v1/Managers/bmc/ResetActionInfo"},
234                     {"Name", "Reset Action Info"},
235                     {"Id", "ResetActionInfo"},
236                     {"Parameters",
237                      {{{"Name", "ResetType"},
238                        {"Required", true},
239                        {"DataType", "String"},
240                        {"AllowableValues",
241                         {"GracefulRestart", "ForceRestart"}}}}}};
242             });
243 }
244 
245 static constexpr const char* objectManagerIface =
246     "org.freedesktop.DBus.ObjectManager";
247 static constexpr const char* pidConfigurationIface =
248     "xyz.openbmc_project.Configuration.Pid";
249 static constexpr const char* pidZoneConfigurationIface =
250     "xyz.openbmc_project.Configuration.Pid.Zone";
251 static constexpr const char* stepwiseConfigurationIface =
252     "xyz.openbmc_project.Configuration.Stepwise";
253 static constexpr const char* thermalModeIface =
254     "xyz.openbmc_project.Control.ThermalMode";
255 
256 inline void
257     asyncPopulatePid(const std::string& connection, const std::string& path,
258                      const std::string& currentProfile,
259                      const std::vector<std::string>& supportedProfiles,
260                      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
261 {
262 
263     crow::connections::systemBus->async_method_call(
264         [asyncResp, currentProfile, supportedProfiles](
265             const boost::system::error_code ec,
266             const dbus::utility::ManagedObjectType& managedObj) {
267             if (ec)
268             {
269                 BMCWEB_LOG_ERROR << ec;
270                 asyncResp->res.jsonValue.clear();
271                 messages::internalError(asyncResp->res);
272                 return;
273             }
274             nlohmann::json& configRoot =
275                 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"];
276             nlohmann::json& fans = configRoot["FanControllers"];
277             fans["@odata.type"] = "#OemManager.FanControllers";
278             fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/"
279                                 "Fan/FanControllers";
280 
281             nlohmann::json& pids = configRoot["PidControllers"];
282             pids["@odata.type"] = "#OemManager.PidControllers";
283             pids["@odata.id"] =
284                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers";
285 
286             nlohmann::json& stepwise = configRoot["StepwiseControllers"];
287             stepwise["@odata.type"] = "#OemManager.StepwiseControllers";
288             stepwise["@odata.id"] =
289                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/StepwiseControllers";
290 
291             nlohmann::json& zones = configRoot["FanZones"];
292             zones["@odata.id"] =
293                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones";
294             zones["@odata.type"] = "#OemManager.FanZones";
295             configRoot["@odata.id"] =
296                 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan";
297             configRoot["@odata.type"] = "#OemManager.Fan";
298             configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
299 
300             if (!currentProfile.empty())
301             {
302                 configRoot["Profile"] = currentProfile;
303             }
304             BMCWEB_LOG_ERROR << "profile = " << currentProfile << " !";
305 
306             for (const auto& pathPair : managedObj)
307             {
308                 for (const auto& intfPair : pathPair.second)
309                 {
310                     if (intfPair.first != pidConfigurationIface &&
311                         intfPair.first != pidZoneConfigurationIface &&
312                         intfPair.first != stepwiseConfigurationIface)
313                     {
314                         continue;
315                     }
316                     auto findName = intfPair.second.find("Name");
317                     if (findName == intfPair.second.end())
318                     {
319                         BMCWEB_LOG_ERROR << "Pid Field missing Name";
320                         messages::internalError(asyncResp->res);
321                         return;
322                     }
323 
324                     const std::string* namePtr =
325                         std::get_if<std::string>(&findName->second);
326                     if (namePtr == nullptr)
327                     {
328                         BMCWEB_LOG_ERROR << "Pid Name Field illegal";
329                         messages::internalError(asyncResp->res);
330                         return;
331                     }
332                     std::string name = *namePtr;
333                     dbus::utility::escapePathForDbus(name);
334 
335                     auto findProfiles = intfPair.second.find("Profiles");
336                     if (findProfiles != intfPair.second.end())
337                     {
338                         const std::vector<std::string>* profiles =
339                             std::get_if<std::vector<std::string>>(
340                                 &findProfiles->second);
341                         if (profiles == nullptr)
342                         {
343                             BMCWEB_LOG_ERROR << "Pid Profiles Field illegal";
344                             messages::internalError(asyncResp->res);
345                             return;
346                         }
347                         if (std::find(profiles->begin(), profiles->end(),
348                                       currentProfile) == profiles->end())
349                         {
350                             BMCWEB_LOG_INFO
351                                 << name << " not supported in current profile";
352                             continue;
353                         }
354                     }
355                     nlohmann::json* config = nullptr;
356 
357                     const std::string* classPtr = nullptr;
358                     auto findClass = intfPair.second.find("Class");
359                     if (findClass != intfPair.second.end())
360                     {
361                         classPtr = std::get_if<std::string>(&findClass->second);
362                     }
363 
364                     if (intfPair.first == pidZoneConfigurationIface)
365                     {
366                         std::string chassis;
367                         if (!dbus::utility::getNthStringFromPath(
368                                 pathPair.first.str, 5, chassis))
369                         {
370                             chassis = "#IllegalValue";
371                         }
372                         nlohmann::json& zone = zones[name];
373                         zone["Chassis"] = {
374                             {"@odata.id", "/redfish/v1/Chassis/" + chassis}};
375                         zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/"
376                                             "OpenBmc/Fan/FanZones/" +
377                                             name;
378                         zone["@odata.type"] = "#OemManager.FanZone";
379                         config = &zone;
380                     }
381 
382                     else if (intfPair.first == stepwiseConfigurationIface)
383                     {
384                         if (classPtr == nullptr)
385                         {
386                             BMCWEB_LOG_ERROR << "Pid Class Field illegal";
387                             messages::internalError(asyncResp->res);
388                             return;
389                         }
390 
391                         nlohmann::json& controller = stepwise[name];
392                         config = &controller;
393 
394                         controller["@odata.id"] =
395                             "/redfish/v1/Managers/bmc#/Oem/"
396                             "OpenBmc/Fan/StepwiseControllers/" +
397                             name;
398                         controller["@odata.type"] =
399                             "#OemManager.StepwiseController";
400 
401                         controller["Direction"] = *classPtr;
402                     }
403 
404                     // pid and fans are off the same configuration
405                     else if (intfPair.first == pidConfigurationIface)
406                     {
407 
408                         if (classPtr == nullptr)
409                         {
410                             BMCWEB_LOG_ERROR << "Pid Class Field illegal";
411                             messages::internalError(asyncResp->res);
412                             return;
413                         }
414                         bool isFan = *classPtr == "fan";
415                         nlohmann::json& element =
416                             isFan ? fans[name] : pids[name];
417                         config = &element;
418                         if (isFan)
419                         {
420                             element["@odata.id"] =
421                                 "/redfish/v1/Managers/bmc#/Oem/"
422                                 "OpenBmc/Fan/FanControllers/" +
423                                 name;
424                             element["@odata.type"] =
425                                 "#OemManager.FanController";
426                         }
427                         else
428                         {
429                             element["@odata.id"] =
430                                 "/redfish/v1/Managers/bmc#/Oem/"
431                                 "OpenBmc/Fan/PidControllers/" +
432                                 name;
433                             element["@odata.type"] =
434                                 "#OemManager.PidController";
435                         }
436                     }
437                     else
438                     {
439                         BMCWEB_LOG_ERROR << "Unexpected configuration";
440                         messages::internalError(asyncResp->res);
441                         return;
442                     }
443 
444                     // used for making maps out of 2 vectors
445                     const std::vector<double>* keys = nullptr;
446                     const std::vector<double>* values = nullptr;
447 
448                     for (const auto& propertyPair : intfPair.second)
449                     {
450                         if (propertyPair.first == "Type" ||
451                             propertyPair.first == "Class" ||
452                             propertyPair.first == "Name")
453                         {
454                             continue;
455                         }
456 
457                         // zones
458                         if (intfPair.first == pidZoneConfigurationIface)
459                         {
460                             const double* ptr =
461                                 std::get_if<double>(&propertyPair.second);
462                             if (ptr == nullptr)
463                             {
464                                 BMCWEB_LOG_ERROR << "Field Illegal "
465                                                  << propertyPair.first;
466                                 messages::internalError(asyncResp->res);
467                                 return;
468                             }
469                             (*config)[propertyPair.first] = *ptr;
470                         }
471 
472                         if (intfPair.first == stepwiseConfigurationIface)
473                         {
474                             if (propertyPair.first == "Reading" ||
475                                 propertyPair.first == "Output")
476                             {
477                                 const std::vector<double>* ptr =
478                                     std::get_if<std::vector<double>>(
479                                         &propertyPair.second);
480 
481                                 if (ptr == nullptr)
482                                 {
483                                     BMCWEB_LOG_ERROR << "Field Illegal "
484                                                      << propertyPair.first;
485                                     messages::internalError(asyncResp->res);
486                                     return;
487                                 }
488 
489                                 if (propertyPair.first == "Reading")
490                                 {
491                                     keys = ptr;
492                                 }
493                                 else
494                                 {
495                                     values = ptr;
496                                 }
497                                 if (keys && values)
498                                 {
499                                     if (keys->size() != values->size())
500                                     {
501                                         BMCWEB_LOG_ERROR
502                                             << "Reading and Output size don't "
503                                                "match ";
504                                         messages::internalError(asyncResp->res);
505                                         return;
506                                     }
507                                     nlohmann::json& steps = (*config)["Steps"];
508                                     steps = nlohmann::json::array();
509                                     for (size_t ii = 0; ii < keys->size(); ii++)
510                                     {
511                                         steps.push_back(
512                                             {{"Target", (*keys)[ii]},
513                                              {"Output", (*values)[ii]}});
514                                     }
515                                 }
516                             }
517                             if (propertyPair.first == "NegativeHysteresis" ||
518                                 propertyPair.first == "PositiveHysteresis")
519                             {
520                                 const double* ptr =
521                                     std::get_if<double>(&propertyPair.second);
522                                 if (ptr == nullptr)
523                                 {
524                                     BMCWEB_LOG_ERROR << "Field Illegal "
525                                                      << propertyPair.first;
526                                     messages::internalError(asyncResp->res);
527                                     return;
528                                 }
529                                 (*config)[propertyPair.first] = *ptr;
530                             }
531                         }
532 
533                         // pid and fans are off the same configuration
534                         if (intfPair.first == pidConfigurationIface ||
535                             intfPair.first == stepwiseConfigurationIface)
536                         {
537 
538                             if (propertyPair.first == "Zones")
539                             {
540                                 const std::vector<std::string>* inputs =
541                                     std::get_if<std::vector<std::string>>(
542                                         &propertyPair.second);
543 
544                                 if (inputs == nullptr)
545                                 {
546                                     BMCWEB_LOG_ERROR
547                                         << "Zones Pid Field Illegal";
548                                     messages::internalError(asyncResp->res);
549                                     return;
550                                 }
551                                 auto& data = (*config)[propertyPair.first];
552                                 data = nlohmann::json::array();
553                                 for (std::string itemCopy : *inputs)
554                                 {
555                                     dbus::utility::escapePathForDbus(itemCopy);
556                                     data.push_back(
557                                         {{"@odata.id",
558                                           "/redfish/v1/Managers/bmc#/Oem/"
559                                           "OpenBmc/Fan/FanZones/" +
560                                               itemCopy}});
561                                 }
562                             }
563                             // todo(james): may never happen, but this
564                             // assumes configuration data referenced in the
565                             // PID config is provided by the same daemon, we
566                             // could add another loop to cover all cases,
567                             // but I'm okay kicking this can down the road a
568                             // bit
569 
570                             else if (propertyPair.first == "Inputs" ||
571                                      propertyPair.first == "Outputs")
572                             {
573                                 auto& data = (*config)[propertyPair.first];
574                                 const std::vector<std::string>* inputs =
575                                     std::get_if<std::vector<std::string>>(
576                                         &propertyPair.second);
577 
578                                 if (inputs == nullptr)
579                                 {
580                                     BMCWEB_LOG_ERROR << "Field Illegal "
581                                                      << propertyPair.first;
582                                     messages::internalError(asyncResp->res);
583                                     return;
584                                 }
585                                 data = *inputs;
586                             }
587                             else if (propertyPair.first == "SetPointOffset")
588                             {
589                                 const std::string* ptr =
590                                     std::get_if<std::string>(
591                                         &propertyPair.second);
592 
593                                 if (ptr == nullptr)
594                                 {
595                                     BMCWEB_LOG_ERROR << "Field Illegal "
596                                                      << propertyPair.first;
597                                     messages::internalError(asyncResp->res);
598                                     return;
599                                 }
600                                 // translate from dbus to redfish
601                                 if (*ptr == "WarningHigh")
602                                 {
603                                     (*config)["SetPointOffset"] =
604                                         "UpperThresholdNonCritical";
605                                 }
606                                 else if (*ptr == "WarningLow")
607                                 {
608                                     (*config)["SetPointOffset"] =
609                                         "LowerThresholdNonCritical";
610                                 }
611                                 else if (*ptr == "CriticalHigh")
612                                 {
613                                     (*config)["SetPointOffset"] =
614                                         "UpperThresholdCritical";
615                                 }
616                                 else if (*ptr == "CriticalLow")
617                                 {
618                                     (*config)["SetPointOffset"] =
619                                         "LowerThresholdCritical";
620                                 }
621                                 else
622                                 {
623                                     BMCWEB_LOG_ERROR << "Value Illegal "
624                                                      << *ptr;
625                                     messages::internalError(asyncResp->res);
626                                     return;
627                                 }
628                             }
629                             // doubles
630                             else if (propertyPair.first ==
631                                          "FFGainCoefficient" ||
632                                      propertyPair.first == "FFOffCoefficient" ||
633                                      propertyPair.first == "ICoefficient" ||
634                                      propertyPair.first == "ILimitMax" ||
635                                      propertyPair.first == "ILimitMin" ||
636                                      propertyPair.first ==
637                                          "PositiveHysteresis" ||
638                                      propertyPair.first ==
639                                          "NegativeHysteresis" ||
640                                      propertyPair.first == "OutLimitMax" ||
641                                      propertyPair.first == "OutLimitMin" ||
642                                      propertyPair.first == "PCoefficient" ||
643                                      propertyPair.first == "SetPoint" ||
644                                      propertyPair.first == "SlewNeg" ||
645                                      propertyPair.first == "SlewPos")
646                             {
647                                 const double* ptr =
648                                     std::get_if<double>(&propertyPair.second);
649                                 if (ptr == nullptr)
650                                 {
651                                     BMCWEB_LOG_ERROR << "Field Illegal "
652                                                      << propertyPair.first;
653                                     messages::internalError(asyncResp->res);
654                                     return;
655                                 }
656                                 (*config)[propertyPair.first] = *ptr;
657                             }
658                         }
659                     }
660                 }
661             }
662         },
663         connection, path, objectManagerIface, "GetManagedObjects");
664 }
665 
666 enum class CreatePIDRet
667 {
668     fail,
669     del,
670     patch
671 };
672 
673 inline bool
674     getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp>& response,
675                         std::vector<nlohmann::json>& config,
676                         std::vector<std::string>& zones)
677 {
678     if (config.empty())
679     {
680         BMCWEB_LOG_ERROR << "Empty Zones";
681         messages::propertyValueFormatError(response->res,
682                                            nlohmann::json::array(), "Zones");
683         return false;
684     }
685     for (auto& odata : config)
686     {
687         std::string path;
688         if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
689                                           path))
690         {
691             return false;
692         }
693         std::string input;
694 
695         // 8 below comes from
696         // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
697         //     0    1     2      3    4    5      6     7      8
698         if (!dbus::utility::getNthStringFromPath(path, 8, input))
699         {
700             BMCWEB_LOG_ERROR << "Got invalid path " << path;
701             BMCWEB_LOG_ERROR << "Illegal Type Zones";
702             messages::propertyValueFormatError(response->res, odata.dump(),
703                                                "Zones");
704             return false;
705         }
706         boost::replace_all(input, "_", " ");
707         zones.emplace_back(std::move(input));
708     }
709     return true;
710 }
711 
712 inline const dbus::utility::ManagedItem*
713     findChassis(const dbus::utility::ManagedObjectType& managedObj,
714                 const std::string& value, std::string& chassis)
715 {
716     BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
717 
718     std::string escaped = boost::replace_all_copy(value, " ", "_");
719     escaped = "/" + escaped;
720     auto it = std::find_if(
721         managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
722             if (boost::algorithm::ends_with(obj.first.str, escaped))
723             {
724                 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
725                 return true;
726             }
727             return false;
728         });
729 
730     if (it == managedObj.end())
731     {
732         return nullptr;
733     }
734     // 5 comes from <chassis-name> being the 5th element
735     // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
736     if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
737     {
738         return &(*it);
739     }
740 
741     return nullptr;
742 }
743 
744 inline CreatePIDRet createPidInterface(
745     const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type,
746     const nlohmann::json::iterator& it, const std::string& path,
747     const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
748     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
749         output,
750     std::string& chassis, const std::string& profile)
751 {
752 
753     // common deleter
754     if (it.value() == nullptr)
755     {
756         std::string iface;
757         if (type == "PidControllers" || type == "FanControllers")
758         {
759             iface = pidConfigurationIface;
760         }
761         else if (type == "FanZones")
762         {
763             iface = pidZoneConfigurationIface;
764         }
765         else if (type == "StepwiseControllers")
766         {
767             iface = stepwiseConfigurationIface;
768         }
769         else
770         {
771             BMCWEB_LOG_ERROR << "Illegal Type " << type;
772             messages::propertyUnknown(response->res, type);
773             return CreatePIDRet::fail;
774         }
775 
776         BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n";
777         // delete interface
778         crow::connections::systemBus->async_method_call(
779             [response, path](const boost::system::error_code ec) {
780                 if (ec)
781                 {
782                     BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
783                     messages::internalError(response->res);
784                     return;
785                 }
786                 messages::success(response->res);
787             },
788             "xyz.openbmc_project.EntityManager", path, iface, "Delete");
789         return CreatePIDRet::del;
790     }
791 
792     const dbus::utility::ManagedItem* managedItem = nullptr;
793     if (!createNewObject)
794     {
795         // if we aren't creating a new object, we should be able to find it on
796         // d-bus
797         managedItem = findChassis(managedObj, it.key(), chassis);
798         if (managedItem == nullptr)
799         {
800             BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
801             messages::invalidObject(response->res, it.key());
802             return CreatePIDRet::fail;
803         }
804     }
805 
806     if (profile.size() &&
807         (type == "PidControllers" || type == "FanControllers" ||
808          type == "StepwiseControllers"))
809     {
810         if (managedItem == nullptr)
811         {
812             output["Profiles"] = std::vector<std::string>{profile};
813         }
814         else
815         {
816             std::string interface;
817             if (type == "StepwiseControllers")
818             {
819                 interface = stepwiseConfigurationIface;
820             }
821             else
822             {
823                 interface = pidConfigurationIface;
824             }
825             auto findConfig = managedItem->second.find(interface);
826             if (findConfig == managedItem->second.end())
827             {
828                 BMCWEB_LOG_ERROR
829                     << "Failed to find interface in managed object";
830                 messages::internalError(response->res);
831                 return CreatePIDRet::fail;
832             }
833             auto findProfiles = findConfig->second.find("Profiles");
834             if (findProfiles != findConfig->second.end())
835             {
836                 const std::vector<std::string>* curProfiles =
837                     std::get_if<std::vector<std::string>>(
838                         &(findProfiles->second));
839                 if (curProfiles == nullptr)
840                 {
841                     BMCWEB_LOG_ERROR << "Illegal profiles in managed object";
842                     messages::internalError(response->res);
843                     return CreatePIDRet::fail;
844                 }
845                 if (std::find(curProfiles->begin(), curProfiles->end(),
846                               profile) == curProfiles->end())
847                 {
848                     std::vector<std::string> newProfiles = *curProfiles;
849                     newProfiles.push_back(profile);
850                     output["Profiles"] = newProfiles;
851                 }
852             }
853         }
854     }
855 
856     if (type == "PidControllers" || type == "FanControllers")
857     {
858         if (createNewObject)
859         {
860             output["Class"] = type == "PidControllers" ? std::string("temp")
861                                                        : std::string("fan");
862             output["Type"] = std::string("Pid");
863         }
864 
865         std::optional<std::vector<nlohmann::json>> zones;
866         std::optional<std::vector<std::string>> inputs;
867         std::optional<std::vector<std::string>> outputs;
868         std::map<std::string, std::optional<double>> doubles;
869         std::optional<std::string> setpointOffset;
870         if (!redfish::json_util::readJson(
871                 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
872                 "Zones", zones, "FFGainCoefficient",
873                 doubles["FFGainCoefficient"], "FFOffCoefficient",
874                 doubles["FFOffCoefficient"], "ICoefficient",
875                 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
876                 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
877                 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
878                 "PCoefficient", doubles["PCoefficient"], "SetPoint",
879                 doubles["SetPoint"], "SetPointOffset", setpointOffset,
880                 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"],
881                 "PositiveHysteresis", doubles["PositiveHysteresis"],
882                 "NegativeHysteresis", doubles["NegativeHysteresis"]))
883         {
884             BMCWEB_LOG_ERROR
885                 << "Illegal Property "
886                 << it.value().dump(2, ' ', true,
887                                    nlohmann::json::error_handler_t::replace);
888             return CreatePIDRet::fail;
889         }
890         if (zones)
891         {
892             std::vector<std::string> zonesStr;
893             if (!getZonesFromJsonReq(response, *zones, zonesStr))
894             {
895                 BMCWEB_LOG_ERROR << "Illegal Zones";
896                 return CreatePIDRet::fail;
897             }
898             if (chassis.empty() &&
899                 !findChassis(managedObj, zonesStr[0], chassis))
900             {
901                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
902                 messages::invalidObject(response->res, it.key());
903                 return CreatePIDRet::fail;
904             }
905 
906             output["Zones"] = std::move(zonesStr);
907         }
908         if (inputs || outputs)
909         {
910             std::array<std::optional<std::vector<std::string>>*, 2> containers =
911                 {&inputs, &outputs};
912             size_t index = 0;
913             for (const auto& containerPtr : containers)
914             {
915                 std::optional<std::vector<std::string>>& container =
916                     *containerPtr;
917                 if (!container)
918                 {
919                     index++;
920                     continue;
921                 }
922 
923                 for (std::string& value : *container)
924                 {
925                     boost::replace_all(value, "_", " ");
926                 }
927                 std::string key;
928                 if (index == 0)
929                 {
930                     key = "Inputs";
931                 }
932                 else
933                 {
934                     key = "Outputs";
935                 }
936                 output[key] = *container;
937                 index++;
938             }
939         }
940 
941         if (setpointOffset)
942         {
943             // translate between redfish and dbus names
944             if (*setpointOffset == "UpperThresholdNonCritical")
945             {
946                 output["SetPointOffset"] = std::string("WarningLow");
947             }
948             else if (*setpointOffset == "LowerThresholdNonCritical")
949             {
950                 output["SetPointOffset"] = std::string("WarningHigh");
951             }
952             else if (*setpointOffset == "LowerThresholdCritical")
953             {
954                 output["SetPointOffset"] = std::string("CriticalLow");
955             }
956             else if (*setpointOffset == "UpperThresholdCritical")
957             {
958                 output["SetPointOffset"] = std::string("CriticalHigh");
959             }
960             else
961             {
962                 BMCWEB_LOG_ERROR << "Invalid setpointoffset "
963                                  << *setpointOffset;
964                 messages::invalidObject(response->res, it.key());
965                 return CreatePIDRet::fail;
966             }
967         }
968 
969         // doubles
970         for (const auto& pairs : doubles)
971         {
972             if (!pairs.second)
973             {
974                 continue;
975             }
976             BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
977             output[pairs.first] = *(pairs.second);
978         }
979     }
980 
981     else if (type == "FanZones")
982     {
983         output["Type"] = std::string("Pid.Zone");
984 
985         std::optional<nlohmann::json> chassisContainer;
986         std::optional<double> failSafePercent;
987         std::optional<double> minThermalOutput;
988         if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
989                                           chassisContainer, "FailSafePercent",
990                                           failSafePercent, "MinThermalOutput",
991                                           minThermalOutput))
992         {
993             BMCWEB_LOG_ERROR
994                 << "Illegal Property "
995                 << it.value().dump(2, ' ', true,
996                                    nlohmann::json::error_handler_t::replace);
997             return CreatePIDRet::fail;
998         }
999 
1000         if (chassisContainer)
1001         {
1002 
1003             std::string chassisId;
1004             if (!redfish::json_util::readJson(*chassisContainer, response->res,
1005                                               "@odata.id", chassisId))
1006             {
1007                 BMCWEB_LOG_ERROR
1008                     << "Illegal Property "
1009                     << chassisContainer->dump(
1010                            2, ' ', true,
1011                            nlohmann::json::error_handler_t::replace);
1012                 return CreatePIDRet::fail;
1013             }
1014 
1015             // /redfish/v1/chassis/chassis_name/
1016             if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
1017             {
1018                 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
1019                 messages::invalidObject(response->res, chassisId);
1020                 return CreatePIDRet::fail;
1021             }
1022         }
1023         if (minThermalOutput)
1024         {
1025             output["MinThermalOutput"] = *minThermalOutput;
1026         }
1027         if (failSafePercent)
1028         {
1029             output["FailSafePercent"] = *failSafePercent;
1030         }
1031     }
1032     else if (type == "StepwiseControllers")
1033     {
1034         output["Type"] = std::string("Stepwise");
1035 
1036         std::optional<std::vector<nlohmann::json>> zones;
1037         std::optional<std::vector<nlohmann::json>> steps;
1038         std::optional<std::vector<std::string>> inputs;
1039         std::optional<double> positiveHysteresis;
1040         std::optional<double> negativeHysteresis;
1041         std::optional<std::string> direction; // upper clipping curve vs lower
1042         if (!redfish::json_util::readJson(
1043                 it.value(), response->res, "Zones", zones, "Steps", steps,
1044                 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
1045                 "NegativeHysteresis", negativeHysteresis, "Direction",
1046                 direction))
1047         {
1048             BMCWEB_LOG_ERROR
1049                 << "Illegal Property "
1050                 << it.value().dump(2, ' ', true,
1051                                    nlohmann::json::error_handler_t::replace);
1052             return CreatePIDRet::fail;
1053         }
1054 
1055         if (zones)
1056         {
1057             std::vector<std::string> zonesStrs;
1058             if (!getZonesFromJsonReq(response, *zones, zonesStrs))
1059             {
1060                 BMCWEB_LOG_ERROR << "Illegal Zones";
1061                 return CreatePIDRet::fail;
1062             }
1063             if (chassis.empty() &&
1064                 !findChassis(managedObj, zonesStrs[0], chassis))
1065             {
1066                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
1067                 messages::invalidObject(response->res, it.key());
1068                 return CreatePIDRet::fail;
1069             }
1070             output["Zones"] = std::move(zonesStrs);
1071         }
1072         if (steps)
1073         {
1074             std::vector<double> readings;
1075             std::vector<double> outputs;
1076             for (auto& step : *steps)
1077             {
1078                 double target;
1079                 double out;
1080 
1081                 if (!redfish::json_util::readJson(step, response->res, "Target",
1082                                                   target, "Output", out))
1083                 {
1084                     BMCWEB_LOG_ERROR
1085                         << "Illegal Property "
1086                         << it.value().dump(
1087                                2, ' ', true,
1088                                nlohmann::json::error_handler_t::replace);
1089                     return CreatePIDRet::fail;
1090                 }
1091                 readings.emplace_back(target);
1092                 outputs.emplace_back(out);
1093             }
1094             output["Reading"] = std::move(readings);
1095             output["Output"] = std::move(outputs);
1096         }
1097         if (inputs)
1098         {
1099             for (std::string& value : *inputs)
1100             {
1101                 boost::replace_all(value, "_", " ");
1102             }
1103             output["Inputs"] = std::move(*inputs);
1104         }
1105         if (negativeHysteresis)
1106         {
1107             output["NegativeHysteresis"] = *negativeHysteresis;
1108         }
1109         if (positiveHysteresis)
1110         {
1111             output["PositiveHysteresis"] = *positiveHysteresis;
1112         }
1113         if (direction)
1114         {
1115             constexpr const std::array<const char*, 2> allowedDirections = {
1116                 "Ceiling", "Floor"};
1117             if (std::find(allowedDirections.begin(), allowedDirections.end(),
1118                           *direction) == allowedDirections.end())
1119             {
1120                 messages::propertyValueTypeError(response->res, "Direction",
1121                                                  *direction);
1122                 return CreatePIDRet::fail;
1123             }
1124             output["Class"] = *direction;
1125         }
1126     }
1127     else
1128     {
1129         BMCWEB_LOG_ERROR << "Illegal Type " << type;
1130         messages::propertyUnknown(response->res, type);
1131         return CreatePIDRet::fail;
1132     }
1133     return CreatePIDRet::patch;
1134 }
1135 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
1136 {
1137 
1138     GetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
1139         asyncResp(asyncRespIn)
1140 
1141     {}
1142 
1143     void run()
1144     {
1145         std::shared_ptr<GetPIDValues> self = shared_from_this();
1146 
1147         // get all configurations
1148         crow::connections::systemBus->async_method_call(
1149             [self](const boost::system::error_code ec,
1150                    const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
1151                 if (ec)
1152                 {
1153                     BMCWEB_LOG_ERROR << ec;
1154                     messages::internalError(self->asyncResp->res);
1155                     return;
1156                 }
1157                 self->subtree = subtreeLocal;
1158             },
1159             "xyz.openbmc_project.ObjectMapper",
1160             "/xyz/openbmc_project/object_mapper",
1161             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1162             std::array<const char*, 4>{
1163                 pidConfigurationIface, pidZoneConfigurationIface,
1164                 objectManagerIface, stepwiseConfigurationIface});
1165 
1166         // at the same time get the selected profile
1167         crow::connections::systemBus->async_method_call(
1168             [self](const boost::system::error_code ec,
1169                    const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
1170                 if (ec || subtreeLocal.empty())
1171                 {
1172                     return;
1173                 }
1174                 if (subtreeLocal[0].second.size() != 1)
1175                 {
1176                     // invalid mapper response, should never happen
1177                     BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
1178                     messages::internalError(self->asyncResp->res);
1179                     return;
1180                 }
1181 
1182                 const std::string& path = subtreeLocal[0].first;
1183                 const std::string& owner = subtreeLocal[0].second[0].first;
1184                 crow::connections::systemBus->async_method_call(
1185                     [path, owner, self](
1186                         const boost::system::error_code ec2,
1187                         const boost::container::flat_map<
1188                             std::string, std::variant<std::vector<std::string>,
1189                                                       std::string>>& resp) {
1190                         if (ec2)
1191                         {
1192                             BMCWEB_LOG_ERROR << "GetPIDValues: Can't get "
1193                                                 "thermalModeIface "
1194                                              << path;
1195                             messages::internalError(self->asyncResp->res);
1196                             return;
1197                         }
1198                         const std::string* current = nullptr;
1199                         const std::vector<std::string>* supported = nullptr;
1200                         for (auto& [key, value] : resp)
1201                         {
1202                             if (key == "Current")
1203                             {
1204                                 current = std::get_if<std::string>(&value);
1205                                 if (current == nullptr)
1206                                 {
1207                                     BMCWEB_LOG_ERROR
1208                                         << "GetPIDValues: thermal mode "
1209                                            "iface invalid "
1210                                         << path;
1211                                     messages::internalError(
1212                                         self->asyncResp->res);
1213                                     return;
1214                                 }
1215                             }
1216                             if (key == "Supported")
1217                             {
1218                                 supported =
1219                                     std::get_if<std::vector<std::string>>(
1220                                         &value);
1221                                 if (supported == nullptr)
1222                                 {
1223                                     BMCWEB_LOG_ERROR
1224                                         << "GetPIDValues: thermal mode "
1225                                            "iface invalid"
1226                                         << path;
1227                                     messages::internalError(
1228                                         self->asyncResp->res);
1229                                     return;
1230                                 }
1231                             }
1232                         }
1233                         if (current == nullptr || supported == nullptr)
1234                         {
1235                             BMCWEB_LOG_ERROR << "GetPIDValues: thermal mode "
1236                                                 "iface invalid "
1237                                              << path;
1238                             messages::internalError(self->asyncResp->res);
1239                             return;
1240                         }
1241                         self->currentProfile = *current;
1242                         self->supportedProfiles = *supported;
1243                     },
1244                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1245                     thermalModeIface);
1246             },
1247             "xyz.openbmc_project.ObjectMapper",
1248             "/xyz/openbmc_project/object_mapper",
1249             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1250             std::array<const char*, 1>{thermalModeIface});
1251     }
1252 
1253     ~GetPIDValues()
1254     {
1255         if (asyncResp->res.result() != boost::beast::http::status::ok)
1256         {
1257             return;
1258         }
1259         // create map of <connection, path to objMgr>>
1260         boost::container::flat_map<std::string, std::string> objectMgrPaths;
1261         boost::container::flat_set<std::string> calledConnections;
1262         for (const auto& pathGroup : subtree)
1263         {
1264             for (const auto& connectionGroup : pathGroup.second)
1265             {
1266                 auto findConnection =
1267                     calledConnections.find(connectionGroup.first);
1268                 if (findConnection != calledConnections.end())
1269                 {
1270                     break;
1271                 }
1272                 for (const std::string& interface : connectionGroup.second)
1273                 {
1274                     if (interface == objectManagerIface)
1275                     {
1276                         objectMgrPaths[connectionGroup.first] = pathGroup.first;
1277                     }
1278                     // this list is alphabetical, so we
1279                     // should have found the objMgr by now
1280                     if (interface == pidConfigurationIface ||
1281                         interface == pidZoneConfigurationIface ||
1282                         interface == stepwiseConfigurationIface)
1283                     {
1284                         auto findObjMgr =
1285                             objectMgrPaths.find(connectionGroup.first);
1286                         if (findObjMgr == objectMgrPaths.end())
1287                         {
1288                             BMCWEB_LOG_DEBUG << connectionGroup.first
1289                                              << "Has no Object Manager";
1290                             continue;
1291                         }
1292 
1293                         calledConnections.insert(connectionGroup.first);
1294 
1295                         asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1296                                          currentProfile, supportedProfiles,
1297                                          asyncResp);
1298                         break;
1299                     }
1300                 }
1301             }
1302         }
1303     }
1304 
1305     std::vector<std::string> supportedProfiles;
1306     std::string currentProfile;
1307     crow::openbmc_mapper::GetSubTreeType subtree;
1308     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1309 };
1310 
1311 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1312 {
1313 
1314     SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
1315                  nlohmann::json& data) :
1316         asyncResp(asyncRespIn)
1317     {
1318 
1319         std::optional<nlohmann::json> pidControllers;
1320         std::optional<nlohmann::json> fanControllers;
1321         std::optional<nlohmann::json> fanZones;
1322         std::optional<nlohmann::json> stepwiseControllers;
1323 
1324         if (!redfish::json_util::readJson(
1325                 data, asyncResp->res, "PidControllers", pidControllers,
1326                 "FanControllers", fanControllers, "FanZones", fanZones,
1327                 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1328         {
1329             BMCWEB_LOG_ERROR
1330                 << "Illegal Property "
1331                 << data.dump(2, ' ', true,
1332                              nlohmann::json::error_handler_t::replace);
1333             return;
1334         }
1335         configuration.emplace_back("PidControllers", std::move(pidControllers));
1336         configuration.emplace_back("FanControllers", std::move(fanControllers));
1337         configuration.emplace_back("FanZones", std::move(fanZones));
1338         configuration.emplace_back("StepwiseControllers",
1339                                    std::move(stepwiseControllers));
1340     }
1341     void run()
1342     {
1343         if (asyncResp->res.result() != boost::beast::http::status::ok)
1344         {
1345             return;
1346         }
1347 
1348         std::shared_ptr<SetPIDValues> self = shared_from_this();
1349 
1350         // todo(james): might make sense to do a mapper call here if this
1351         // interface gets more traction
1352         crow::connections::systemBus->async_method_call(
1353             [self](const boost::system::error_code ec,
1354                    dbus::utility::ManagedObjectType& mObj) {
1355                 if (ec)
1356                 {
1357                     BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1358                     messages::internalError(self->asyncResp->res);
1359                     return;
1360                 }
1361                 const std::array<const char*, 3> configurations = {
1362                     pidConfigurationIface, pidZoneConfigurationIface,
1363                     stepwiseConfigurationIface};
1364 
1365                 for (const auto& [path, object] : mObj)
1366                 {
1367                     for (const auto& [interface, _] : object)
1368                     {
1369                         if (std::find(configurations.begin(),
1370                                       configurations.end(),
1371                                       interface) != configurations.end())
1372                         {
1373                             self->objectCount++;
1374                             break;
1375                         }
1376                     }
1377                 }
1378                 self->managedObj = std::move(mObj);
1379             },
1380             "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1381             "GetManagedObjects");
1382 
1383         // at the same time get the profile information
1384         crow::connections::systemBus->async_method_call(
1385             [self](const boost::system::error_code ec,
1386                    const crow::openbmc_mapper::GetSubTreeType& subtree) {
1387                 if (ec || subtree.empty())
1388                 {
1389                     return;
1390                 }
1391                 if (subtree[0].second.empty())
1392                 {
1393                     // invalid mapper response, should never happen
1394                     BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1395                     messages::internalError(self->asyncResp->res);
1396                     return;
1397                 }
1398 
1399                 const std::string& path = subtree[0].first;
1400                 const std::string& owner = subtree[0].second[0].first;
1401                 crow::connections::systemBus->async_method_call(
1402                     [self, path, owner](
1403                         const boost::system::error_code ec2,
1404                         const boost::container::flat_map<
1405                             std::string, std::variant<std::vector<std::string>,
1406                                                       std::string>>& r) {
1407                         if (ec2)
1408                         {
1409                             BMCWEB_LOG_ERROR << "SetPIDValues: Can't get "
1410                                                 "thermalModeIface "
1411                                              << path;
1412                             messages::internalError(self->asyncResp->res);
1413                             return;
1414                         }
1415                         const std::string* current = nullptr;
1416                         const std::vector<std::string>* supported = nullptr;
1417                         for (auto& [key, value] : r)
1418                         {
1419                             if (key == "Current")
1420                             {
1421                                 current = std::get_if<std::string>(&value);
1422                                 if (current == nullptr)
1423                                 {
1424                                     BMCWEB_LOG_ERROR
1425                                         << "SetPIDValues: thermal mode "
1426                                            "iface invalid "
1427                                         << path;
1428                                     messages::internalError(
1429                                         self->asyncResp->res);
1430                                     return;
1431                                 }
1432                             }
1433                             if (key == "Supported")
1434                             {
1435                                 supported =
1436                                     std::get_if<std::vector<std::string>>(
1437                                         &value);
1438                                 if (supported == nullptr)
1439                                 {
1440                                     BMCWEB_LOG_ERROR
1441                                         << "SetPIDValues: thermal mode "
1442                                            "iface invalid"
1443                                         << path;
1444                                     messages::internalError(
1445                                         self->asyncResp->res);
1446                                     return;
1447                                 }
1448                             }
1449                         }
1450                         if (current == nullptr || supported == nullptr)
1451                         {
1452                             BMCWEB_LOG_ERROR << "SetPIDValues: thermal mode "
1453                                                 "iface invalid "
1454                                              << path;
1455                             messages::internalError(self->asyncResp->res);
1456                             return;
1457                         }
1458                         self->currentProfile = *current;
1459                         self->supportedProfiles = *supported;
1460                         self->profileConnection = owner;
1461                         self->profilePath = path;
1462                     },
1463                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1464                     thermalModeIface);
1465             },
1466             "xyz.openbmc_project.ObjectMapper",
1467             "/xyz/openbmc_project/object_mapper",
1468             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1469             std::array<const char*, 1>{thermalModeIface});
1470     }
1471     ~SetPIDValues()
1472     {
1473         if (asyncResp->res.result() != boost::beast::http::status::ok)
1474         {
1475             return;
1476         }
1477         std::shared_ptr<bmcweb::AsyncResp> response = asyncResp;
1478         if (profile)
1479         {
1480             if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1481                           *profile) == supportedProfiles.end())
1482             {
1483                 messages::actionParameterUnknown(response->res, "Profile",
1484                                                  *profile);
1485                 return;
1486             }
1487             currentProfile = *profile;
1488             crow::connections::systemBus->async_method_call(
1489                 [response](const boost::system::error_code ec) {
1490                     if (ec)
1491                     {
1492                         BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1493                         messages::internalError(response->res);
1494                     }
1495                 },
1496                 profileConnection, profilePath,
1497                 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1498                 "Current", std::variant<std::string>(*profile));
1499         }
1500 
1501         for (auto& containerPair : configuration)
1502         {
1503             auto& container = containerPair.second;
1504             if (!container)
1505             {
1506                 continue;
1507             }
1508             BMCWEB_LOG_DEBUG << *container;
1509 
1510             std::string& type = containerPair.first;
1511 
1512             for (nlohmann::json::iterator it = container->begin();
1513                  it != container->end(); ++it)
1514             {
1515                 const auto& name = it.key();
1516                 BMCWEB_LOG_DEBUG << "looking for " << name;
1517 
1518                 auto pathItr =
1519                     std::find_if(managedObj.begin(), managedObj.end(),
1520                                  [&name](const auto& obj) {
1521                                      return boost::algorithm::ends_with(
1522                                          obj.first.str, "/" + name);
1523                                  });
1524                 boost::container::flat_map<std::string,
1525                                            dbus::utility::DbusVariantType>
1526                     output;
1527 
1528                 output.reserve(16); // The pid interface length
1529 
1530                 // determines if we're patching entity-manager or
1531                 // creating a new object
1532                 bool createNewObject = (pathItr == managedObj.end());
1533                 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject;
1534 
1535                 std::string iface;
1536                 if (type == "PidControllers" || type == "FanControllers")
1537                 {
1538                     iface = pidConfigurationIface;
1539                     if (!createNewObject &&
1540                         pathItr->second.find(pidConfigurationIface) ==
1541                             pathItr->second.end())
1542                     {
1543                         createNewObject = true;
1544                     }
1545                 }
1546                 else if (type == "FanZones")
1547                 {
1548                     iface = pidZoneConfigurationIface;
1549                     if (!createNewObject &&
1550                         pathItr->second.find(pidZoneConfigurationIface) ==
1551                             pathItr->second.end())
1552                     {
1553 
1554                         createNewObject = true;
1555                     }
1556                 }
1557                 else if (type == "StepwiseControllers")
1558                 {
1559                     iface = stepwiseConfigurationIface;
1560                     if (!createNewObject &&
1561                         pathItr->second.find(stepwiseConfigurationIface) ==
1562                             pathItr->second.end())
1563                     {
1564                         createNewObject = true;
1565                     }
1566                 }
1567 
1568                 if (createNewObject && it.value() == nullptr)
1569                 {
1570                     // can't delete a non-existent object
1571                     messages::invalidObject(response->res, name);
1572                     continue;
1573                 }
1574 
1575                 std::string path;
1576                 if (pathItr != managedObj.end())
1577                 {
1578                     path = pathItr->first.str;
1579                 }
1580 
1581                 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
1582 
1583                 // arbitrary limit to avoid attacks
1584                 constexpr const size_t controllerLimit = 500;
1585                 if (createNewObject && objectCount >= controllerLimit)
1586                 {
1587                     messages::resourceExhaustion(response->res, type);
1588                     continue;
1589                 }
1590 
1591                 output["Name"] = boost::replace_all_copy(name, "_", " ");
1592 
1593                 std::string chassis;
1594                 CreatePIDRet ret = createPidInterface(
1595                     response, type, it, path, managedObj, createNewObject,
1596                     output, chassis, currentProfile);
1597                 if (ret == CreatePIDRet::fail)
1598                 {
1599                     return;
1600                 }
1601                 if (ret == CreatePIDRet::del)
1602                 {
1603                     continue;
1604                 }
1605 
1606                 if (!createNewObject)
1607                 {
1608                     for (const auto& property : output)
1609                     {
1610                         crow::connections::systemBus->async_method_call(
1611                             [response,
1612                              propertyName{std::string(property.first)}](
1613                                 const boost::system::error_code ec) {
1614                                 if (ec)
1615                                 {
1616                                     BMCWEB_LOG_ERROR << "Error patching "
1617                                                      << propertyName << ": "
1618                                                      << ec;
1619                                     messages::internalError(response->res);
1620                                     return;
1621                                 }
1622                                 messages::success(response->res);
1623                             },
1624                             "xyz.openbmc_project.EntityManager", path,
1625                             "org.freedesktop.DBus.Properties", "Set", iface,
1626                             property.first, property.second);
1627                     }
1628                 }
1629                 else
1630                 {
1631                     if (chassis.empty())
1632                     {
1633                         BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1634                         messages::invalidObject(response->res, name);
1635                         return;
1636                     }
1637 
1638                     bool foundChassis = false;
1639                     for (const auto& obj : managedObj)
1640                     {
1641                         if (boost::algorithm::ends_with(obj.first.str, chassis))
1642                         {
1643                             chassis = obj.first.str;
1644                             foundChassis = true;
1645                             break;
1646                         }
1647                     }
1648                     if (!foundChassis)
1649                     {
1650                         BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1651                         messages::resourceMissingAtURI(
1652                             response->res, "/redfish/v1/Chassis/" + chassis);
1653                         return;
1654                     }
1655 
1656                     crow::connections::systemBus->async_method_call(
1657                         [response](const boost::system::error_code ec) {
1658                             if (ec)
1659                             {
1660                                 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1661                                                  << ec;
1662                                 messages::internalError(response->res);
1663                                 return;
1664                             }
1665                             messages::success(response->res);
1666                         },
1667                         "xyz.openbmc_project.EntityManager", chassis,
1668                         "xyz.openbmc_project.AddObject", "AddObject", output);
1669                 }
1670             }
1671         }
1672     }
1673     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1674     std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1675         configuration;
1676     std::optional<std::string> profile;
1677     dbus::utility::ManagedObjectType managedObj;
1678     std::vector<std::string> supportedProfiles;
1679     std::string currentProfile;
1680     std::string profileConnection;
1681     std::string profilePath;
1682     size_t objectCount = 0;
1683 };
1684 
1685 /**
1686  * @brief Retrieves BMC manager location data over DBus
1687  *
1688  * @param[in] aResp Shared pointer for completing asynchronous calls
1689  * @param[in] connectionName - service name
1690  * @param[in] path - object path
1691  * @return none
1692  */
1693 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1694                         const std::string& connectionName,
1695                         const std::string& path)
1696 {
1697     BMCWEB_LOG_DEBUG << "Get BMC manager Location data.";
1698 
1699     crow::connections::systemBus->async_method_call(
1700         [aResp](const boost::system::error_code ec,
1701                 const std::variant<std::string>& property) {
1702             if (ec)
1703             {
1704                 BMCWEB_LOG_DEBUG << "DBUS response error for "
1705                                     "Location";
1706                 messages::internalError(aResp->res);
1707                 return;
1708             }
1709 
1710             const std::string* value = std::get_if<std::string>(&property);
1711 
1712             if (value == nullptr)
1713             {
1714                 // illegal value
1715                 messages::internalError(aResp->res);
1716                 return;
1717             }
1718 
1719             aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
1720                 *value;
1721         },
1722         connectionName, path, "org.freedesktop.DBus.Properties", "Get",
1723         "xyz.openbmc_project.Inventory.Decorator."
1724         "LocationCode",
1725         "LocationCode");
1726 }
1727 // avoid name collision systems.hpp
1728 inline void
1729     managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
1730 {
1731     BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time";
1732 
1733     crow::connections::systemBus->async_method_call(
1734         [aResp](const boost::system::error_code ec,
1735                 std::variant<uint64_t>& lastResetTime) {
1736             if (ec)
1737             {
1738                 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec;
1739                 return;
1740             }
1741 
1742             const uint64_t* lastResetTimePtr =
1743                 std::get_if<uint64_t>(&lastResetTime);
1744 
1745             if (!lastResetTimePtr)
1746             {
1747                 messages::internalError(aResp->res);
1748                 return;
1749             }
1750             // LastRebootTime is epoch time, in milliseconds
1751             // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
1752             time_t lastResetTimeStamp =
1753                 static_cast<time_t>(*lastResetTimePtr / 1000);
1754 
1755             // Convert to ISO 8601 standard
1756             aResp->res.jsonValue["LastResetTime"] =
1757                 crow::utility::getDateTime(lastResetTimeStamp);
1758         },
1759         "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
1760         "org.freedesktop.DBus.Properties", "Get",
1761         "xyz.openbmc_project.State.BMC", "LastRebootTime");
1762 }
1763 
1764 /**
1765  * @brief Set the running firmware image
1766  *
1767  * @param[i,o] aResp - Async response object
1768  * @param[i] runningFirmwareTarget - Image to make the running image
1769  *
1770  * @return void
1771  */
1772 inline void
1773     setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1774                            const std::string& runningFirmwareTarget)
1775 {
1776     // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id>
1777     std::string::size_type idPos = runningFirmwareTarget.rfind('/');
1778     if (idPos == std::string::npos)
1779     {
1780         messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1781                                          "@odata.id");
1782         BMCWEB_LOG_DEBUG << "Can't parse firmware ID!";
1783         return;
1784     }
1785     idPos++;
1786     if (idPos >= runningFirmwareTarget.size())
1787     {
1788         messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1789                                          "@odata.id");
1790         BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1791         return;
1792     }
1793     std::string firmwareId = runningFirmwareTarget.substr(idPos);
1794 
1795     // Make sure the image is valid before setting priority
1796     crow::connections::systemBus->async_method_call(
1797         [aResp, firmwareId, runningFirmwareTarget](
1798             const boost::system::error_code ec, ManagedObjectType& subtree) {
1799             if (ec)
1800             {
1801                 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects.";
1802                 messages::internalError(aResp->res);
1803                 return;
1804             }
1805 
1806             if (subtree.size() == 0)
1807             {
1808                 BMCWEB_LOG_DEBUG << "Can't find image!";
1809                 messages::internalError(aResp->res);
1810                 return;
1811             }
1812 
1813             bool foundImage = false;
1814             for (auto& object : subtree)
1815             {
1816                 const std::string& path =
1817                     static_cast<const std::string&>(object.first);
1818                 std::size_t idPos2 = path.rfind('/');
1819 
1820                 if (idPos2 == std::string::npos)
1821                 {
1822                     continue;
1823                 }
1824 
1825                 idPos2++;
1826                 if (idPos2 >= path.size())
1827                 {
1828                     continue;
1829                 }
1830 
1831                 if (path.substr(idPos2) == firmwareId)
1832                 {
1833                     foundImage = true;
1834                     break;
1835                 }
1836             }
1837 
1838             if (!foundImage)
1839             {
1840                 messages::propertyValueNotInList(
1841                     aResp->res, runningFirmwareTarget, "@odata.id");
1842                 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1843                 return;
1844             }
1845 
1846             BMCWEB_LOG_DEBUG
1847                 << "Setting firmware version " + firmwareId + " to priority 0.";
1848 
1849             // Only support Immediate
1850             // An addition could be a Redfish Setting like
1851             // ActiveSoftwareImageApplyTime and support OnReset
1852             crow::connections::systemBus->async_method_call(
1853                 [aResp](const boost::system::error_code ec) {
1854                     if (ec)
1855                     {
1856                         BMCWEB_LOG_DEBUG << "D-Bus response error setting.";
1857                         messages::internalError(aResp->res);
1858                         return;
1859                     }
1860                     doBMCGracefulRestart(aResp);
1861                 },
1862 
1863                 "xyz.openbmc_project.Software.BMC.Updater",
1864                 "/xyz/openbmc_project/software/" + firmwareId,
1865                 "org.freedesktop.DBus.Properties", "Set",
1866                 "xyz.openbmc_project.Software.RedundancyPriority", "Priority",
1867                 std::variant<uint8_t>(static_cast<uint8_t>(0)));
1868         },
1869         "xyz.openbmc_project.Software.BMC.Updater",
1870         "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager",
1871         "GetManagedObjects");
1872 }
1873 
1874 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp,
1875                         std::string datetime)
1876 {
1877     BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1878 
1879     std::stringstream stream(datetime);
1880     // Convert from ISO 8601 to boost local_time
1881     // (BMC only has time in UTC)
1882     boost::posix_time::ptime posixTime;
1883     boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1884     // Facet gets deleted with the stringsteam
1885     auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1886         "%Y-%m-%d %H:%M:%S%F %ZP");
1887     stream.imbue(std::locale(stream.getloc(), ifc.release()));
1888 
1889     boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
1890 
1891     if (stream >> ldt)
1892     {
1893         posixTime = ldt.utc_time();
1894         boost::posix_time::time_duration dur = posixTime - epoch;
1895         uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
1896         crow::connections::systemBus->async_method_call(
1897             [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1898                 const boost::system::error_code ec) {
1899                 if (ec)
1900                 {
1901                     BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1902                                         "DBUS response error "
1903                                      << ec;
1904                     messages::internalError(aResp->res);
1905                     return;
1906                 }
1907                 aResp->res.jsonValue["DateTime"] = datetime;
1908             },
1909             "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
1910             "org.freedesktop.DBus.Properties", "Set",
1911             "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1912             std::variant<uint64_t>(durMicroSecs));
1913     }
1914     else
1915     {
1916         messages::propertyValueFormatError(aResp->res, datetime, "DateTime");
1917         return;
1918     }
1919 }
1920 
1921 inline void requestRoutesManager(App& app)
1922 {
1923     std::string uuid = persistent_data::getConfig().systemUuid;
1924 
1925     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
1926         .privileges({{"Login"}})
1927         .methods(boost::beast::http::verb::get)([uuid](const crow::Request&,
1928                                                        const std::shared_ptr<
1929                                                            bmcweb::AsyncResp>&
1930                                                            asyncResp) {
1931             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1932             asyncResp->res.jsonValue["@odata.type"] =
1933                 "#Manager.v1_11_0.Manager";
1934             asyncResp->res.jsonValue["Id"] = "bmc";
1935             asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
1936             asyncResp->res.jsonValue["Description"] =
1937                 "Baseboard Management Controller";
1938             asyncResp->res.jsonValue["PowerState"] = "On";
1939             asyncResp->res.jsonValue["Status"] = {{"State", "Enabled"},
1940                                                   {"Health", "OK"}};
1941             asyncResp->res.jsonValue["ManagerType"] = "BMC";
1942             asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
1943             asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
1944             asyncResp->res.jsonValue["Model"] =
1945                 "OpenBmc"; // TODO(ed), get model
1946 
1947             asyncResp->res.jsonValue["LogServices"] = {
1948                 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1949 
1950             asyncResp->res.jsonValue["NetworkProtocol"] = {
1951                 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1952 
1953             asyncResp->res.jsonValue["EthernetInterfaces"] = {
1954                 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
1955 
1956 #ifdef BMCWEB_ENABLE_VM_NBDPROXY
1957             asyncResp->res.jsonValue["VirtualMedia"] = {
1958                 {"@odata.id", "/redfish/v1/Managers/bmc/VirtualMedia"}};
1959 #endif // BMCWEB_ENABLE_VM_NBDPROXY
1960 
1961             // default oem data
1962             nlohmann::json& oem = asyncResp->res.jsonValue["Oem"];
1963             nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1964             oem["@odata.type"] = "#OemManager.Oem";
1965             oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1966             oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1967             oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
1968             oemOpenbmc["Certificates"] = {
1969                 {"@odata.id",
1970                  "/redfish/v1/Managers/bmc/Truststore/Certificates"}};
1971 
1972             // Manager.Reset (an action) can be many values, OpenBMC only
1973             // supports BMC reboot.
1974             nlohmann::json& managerReset =
1975                 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
1976             managerReset["target"] =
1977                 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
1978             managerReset["@Redfish.ActionInfo"] =
1979                 "/redfish/v1/Managers/bmc/ResetActionInfo";
1980 
1981             // ResetToDefaults (Factory Reset) has values like
1982             // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
1983             // on OpenBMC
1984             nlohmann::json& resetToDefaults =
1985                 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
1986             resetToDefaults["target"] =
1987                 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults";
1988             resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"};
1989 
1990             std::pair<std::string, std::string> redfishDateTimeOffset =
1991                 crow::utility::getDateTimeOffsetNow();
1992 
1993             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1994             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1995                 redfishDateTimeOffset.second;
1996 
1997             // TODO (Gunnar): Remove these one day since moved to ComputerSystem
1998             // Still used by OCP profiles
1999             // https://github.com/opencomputeproject/OCP-Profiles/issues/23
2000             // Fill in SerialConsole info
2001             asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
2002             asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] =
2003                 15;
2004             asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] =
2005                 {"IPMI", "SSH"};
2006 #ifdef BMCWEB_ENABLE_KVM
2007             // Fill in GraphicalConsole info
2008             asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] =
2009                 true;
2010             asyncResp->res
2011                 .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4;
2012             asyncResp->res.jsonValue["GraphicalConsole"]
2013                                     ["ConnectTypesSupported"] = {"KVMIP"};
2014 #endif // BMCWEB_ENABLE_KVM
2015 
2016             asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] =
2017                 1;
2018             asyncResp->res.jsonValue["Links"]["ManagerForServers"] = {
2019                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
2020 
2021             auto health = std::make_shared<HealthPopulate>(asyncResp);
2022             health->isManagersHealth = true;
2023             health->populate();
2024 
2025             fw_util::populateFirmwareInformation(asyncResp, fw_util::bmcPurpose,
2026                                                  "FirmwareVersion", true);
2027 
2028             managerGetLastResetTime(asyncResp);
2029 
2030             auto pids = std::make_shared<GetPIDValues>(asyncResp);
2031             pids->run();
2032 
2033             getMainChassisId(
2034                 asyncResp, [](const std::string& chassisId,
2035                               const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
2036                     aRsp->res
2037                         .jsonValue["Links"]["ManagerForChassis@odata.count"] =
2038                         1;
2039                     aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
2040                         {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
2041                     aRsp->res.jsonValue["Links"]["ManagerInChassis"] = {
2042                         {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
2043                 });
2044 
2045             static bool started = false;
2046 
2047             if (!started)
2048             {
2049                 crow::connections::systemBus->async_method_call(
2050                     [asyncResp](const boost::system::error_code ec,
2051                                 const std::variant<double>& resp) {
2052                         if (ec)
2053                         {
2054                             BMCWEB_LOG_ERROR << "Error while getting progress";
2055                             messages::internalError(asyncResp->res);
2056                             return;
2057                         }
2058                         const double* val = std::get_if<double>(&resp);
2059                         if (val == nullptr)
2060                         {
2061                             BMCWEB_LOG_ERROR
2062                                 << "Invalid response while getting progress";
2063                             messages::internalError(asyncResp->res);
2064                             return;
2065                         }
2066                         if (*val < 1.0)
2067                         {
2068                             asyncResp->res.jsonValue["Status"]["State"] =
2069                                 "Starting";
2070                             started = true;
2071                         }
2072                     },
2073                     "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
2074                     "org.freedesktop.DBus.Properties", "Get",
2075                     "org.freedesktop.systemd1.Manager", "Progress");
2076             }
2077 
2078             crow::connections::systemBus->async_method_call(
2079                 [asyncResp](
2080                     const boost::system::error_code ec,
2081                     const std::vector<
2082                         std::pair<std::string,
2083                                   std::vector<std::pair<
2084                                       std::string, std::vector<std::string>>>>>&
2085                         subtree) {
2086                     if (ec)
2087                     {
2088                         BMCWEB_LOG_DEBUG
2089                             << "D-Bus response error on GetSubTree " << ec;
2090                         return;
2091                     }
2092                     if (subtree.size() == 0)
2093                     {
2094                         BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!";
2095                         return;
2096                     }
2097                     // Assume only 1 bmc D-Bus object
2098                     // Throw an error if there is more than 1
2099                     if (subtree.size() > 1)
2100                     {
2101                         BMCWEB_LOG_DEBUG
2102                             << "Found more than 1 bmc D-Bus object!";
2103                         messages::internalError(asyncResp->res);
2104                         return;
2105                     }
2106 
2107                     if (subtree[0].first.empty() ||
2108                         subtree[0].second.size() != 1)
2109                     {
2110                         BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!";
2111                         messages::internalError(asyncResp->res);
2112                         return;
2113                     }
2114 
2115                     const std::string& path = subtree[0].first;
2116                     const std::string& connectionName =
2117                         subtree[0].second[0].first;
2118 
2119                     for (const auto& interfaceName :
2120                          subtree[0].second[0].second)
2121                     {
2122                         if (interfaceName ==
2123                             "xyz.openbmc_project.Inventory.Decorator.Asset")
2124                         {
2125                             crow::connections::systemBus->async_method_call(
2126                                 [asyncResp](
2127                                     const boost::system::error_code ec,
2128                                     const std::vector<
2129                                         std::pair<std::string,
2130                                                   std::variant<std::string>>>&
2131                                         propertiesList) {
2132                                     if (ec)
2133                                     {
2134                                         BMCWEB_LOG_DEBUG
2135                                             << "Can't get bmc asset!";
2136                                         return;
2137                                     }
2138                                     for (const std::pair<
2139                                              std::string,
2140                                              std::variant<std::string>>&
2141                                              property : propertiesList)
2142                                     {
2143                                         const std::string& propertyName =
2144                                             property.first;
2145 
2146                                         if ((propertyName == "PartNumber") ||
2147                                             (propertyName == "SerialNumber") ||
2148                                             (propertyName == "Manufacturer") ||
2149                                             (propertyName == "Model") ||
2150                                             (propertyName == "SparePartNumber"))
2151                                         {
2152                                             const std::string* value =
2153                                                 std::get_if<std::string>(
2154                                                     &property.second);
2155                                             if (value == nullptr)
2156                                             {
2157                                                 // illegal property
2158                                                 messages::internalError(
2159                                                     asyncResp->res);
2160                                                 return;
2161                                             }
2162                                             asyncResp->res
2163                                                 .jsonValue[propertyName] =
2164                                                 *value;
2165                                         }
2166                                     }
2167                                 },
2168                                 connectionName, path,
2169                                 "org.freedesktop.DBus.Properties", "GetAll",
2170                                 "xyz.openbmc_project.Inventory.Decorator."
2171                                 "Asset");
2172                         }
2173                         else if (interfaceName ==
2174                                  "xyz.openbmc_project.Inventory."
2175                                  "Decorator.LocationCode")
2176                         {
2177                             getLocation(asyncResp, connectionName, path);
2178                         }
2179                     }
2180                 },
2181                 "xyz.openbmc_project.ObjectMapper",
2182                 "/xyz/openbmc_project/object_mapper",
2183                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2184                 "/xyz/openbmc_project/inventory", int32_t(0),
2185                 std::array<const char*, 1>{
2186                     "xyz.openbmc_project.Inventory.Item.Bmc"});
2187         });
2188 
2189     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
2190         .privileges({{"ConfigureManager"}})
2191         .methods(
2192             boost::beast::http::verb::
2193                 patch)([](const crow::Request& req,
2194                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2195             std::optional<nlohmann::json> oem;
2196             std::optional<nlohmann::json> links;
2197             std::optional<std::string> datetime;
2198 
2199             if (!json_util::readJson(req, asyncResp->res, "Oem", oem,
2200                                      "DateTime", datetime, "Links", links))
2201             {
2202                 return;
2203             }
2204 
2205             if (oem)
2206             {
2207                 std::optional<nlohmann::json> openbmc;
2208                 if (!redfish::json_util::readJson(*oem, asyncResp->res,
2209                                                   "OpenBmc", openbmc))
2210                 {
2211                     BMCWEB_LOG_ERROR
2212                         << "Illegal Property "
2213                         << oem->dump(2, ' ', true,
2214                                      nlohmann::json::error_handler_t::replace);
2215                     return;
2216                 }
2217                 if (openbmc)
2218                 {
2219                     std::optional<nlohmann::json> fan;
2220                     if (!redfish::json_util::readJson(*openbmc, asyncResp->res,
2221                                                       "Fan", fan))
2222                     {
2223                         BMCWEB_LOG_ERROR
2224                             << "Illegal Property "
2225                             << openbmc->dump(
2226                                    2, ' ', true,
2227                                    nlohmann::json::error_handler_t::replace);
2228                         return;
2229                     }
2230                     if (fan)
2231                     {
2232                         auto pid =
2233                             std::make_shared<SetPIDValues>(asyncResp, *fan);
2234                         pid->run();
2235                     }
2236                 }
2237             }
2238             if (links)
2239             {
2240                 std::optional<nlohmann::json> activeSoftwareImage;
2241                 if (!redfish::json_util::readJson(*links, asyncResp->res,
2242                                                   "ActiveSoftwareImage",
2243                                                   activeSoftwareImage))
2244                 {
2245                     return;
2246                 }
2247                 if (activeSoftwareImage)
2248                 {
2249                     std::optional<std::string> odataId;
2250                     if (!json_util::readJson(*activeSoftwareImage,
2251                                              asyncResp->res, "@odata.id",
2252                                              odataId))
2253                     {
2254                         return;
2255                     }
2256 
2257                     if (odataId)
2258                     {
2259                         setActiveFirmwareImage(asyncResp, *odataId);
2260                     }
2261                 }
2262             }
2263             if (datetime)
2264             {
2265                 setDateTime(asyncResp, std::move(*datetime));
2266             }
2267         });
2268 }
2269 
2270 inline void requestRoutesManagerCollection(App& app)
2271 {
2272     BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
2273         .privileges({{"Login"}})
2274         .methods(boost::beast::http::verb::get)(
2275             [](const crow::Request&,
2276                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2277                 // Collections don't include the static data added by SubRoute
2278                 // because it has a duplicate entry for members
2279                 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
2280                 asyncResp->res.jsonValue["@odata.type"] =
2281                     "#ManagerCollection.ManagerCollection";
2282                 asyncResp->res.jsonValue["Name"] = "Manager Collection";
2283                 asyncResp->res.jsonValue["Members@odata.count"] = 1;
2284                 asyncResp->res.jsonValue["Members"] = {
2285                     {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
2286             });
2287 }
2288 } // namespace redfish
2289