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