xref: /openbmc/bmcweb/features/redfish/lib/managers.hpp (revision 1668ce6d0d427088b7bb8ab7c2927763fb57c8c3)
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, "[]", "Zones");
686         return false;
687     }
688     for (auto& odata : config)
689     {
690         std::string path;
691         if (!redfish::json_util::readJson(odata, response->res, "@odata.id",
692                                           path))
693         {
694             return false;
695         }
696         std::string input;
697 
698         // 8 below comes from
699         // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
700         //     0    1     2      3    4    5      6     7      8
701         if (!dbus::utility::getNthStringFromPath(path, 8, input))
702         {
703             BMCWEB_LOG_ERROR << "Got invalid path " << path;
704             BMCWEB_LOG_ERROR << "Illegal Type Zones";
705             messages::propertyValueFormatError(response->res, odata.dump(),
706                                                "Zones");
707             return false;
708         }
709         boost::replace_all(input, "_", " ");
710         zones.emplace_back(std::move(input));
711     }
712     return true;
713 }
714 
715 inline const dbus::utility::ManagedObjectType::value_type*
716     findChassis(const dbus::utility::ManagedObjectType& managedObj,
717                 const std::string& value, std::string& chassis)
718 {
719     BMCWEB_LOG_DEBUG << "Find Chassis: " << value << "\n";
720 
721     std::string escaped = boost::replace_all_copy(value, " ", "_");
722     escaped = "/" + escaped;
723     auto it = std::find_if(
724         managedObj.begin(), managedObj.end(), [&escaped](const auto& obj) {
725             if (boost::algorithm::ends_with(obj.first.str, escaped))
726             {
727                 BMCWEB_LOG_DEBUG << "Matched " << obj.first.str << "\n";
728                 return true;
729             }
730             return false;
731         });
732 
733     if (it == managedObj.end())
734     {
735         return nullptr;
736     }
737     // 5 comes from <chassis-name> being the 5th element
738     // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
739     if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
740     {
741         return &(*it);
742     }
743 
744     return nullptr;
745 }
746 
747 inline CreatePIDRet createPidInterface(
748     const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type,
749     const nlohmann::json::iterator& it, const std::string& path,
750     const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
751     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>&
752         output,
753     std::string& chassis, const std::string& profile)
754 {
755 
756     // common deleter
757     if (it.value() == nullptr)
758     {
759         std::string iface;
760         if (type == "PidControllers" || type == "FanControllers")
761         {
762             iface = pidConfigurationIface;
763         }
764         else if (type == "FanZones")
765         {
766             iface = pidZoneConfigurationIface;
767         }
768         else if (type == "StepwiseControllers")
769         {
770             iface = stepwiseConfigurationIface;
771         }
772         else
773         {
774             BMCWEB_LOG_ERROR << "Illegal Type " << type;
775             messages::propertyUnknown(response->res, type);
776             return CreatePIDRet::fail;
777         }
778 
779         BMCWEB_LOG_DEBUG << "del " << path << " " << iface << "\n";
780         // delete interface
781         crow::connections::systemBus->async_method_call(
782             [response, path](const boost::system::error_code ec) {
783                 if (ec)
784                 {
785                     BMCWEB_LOG_ERROR << "Error patching " << path << ": " << ec;
786                     messages::internalError(response->res);
787                     return;
788                 }
789                 messages::success(response->res);
790             },
791             "xyz.openbmc_project.EntityManager", path, iface, "Delete");
792         return CreatePIDRet::del;
793     }
794 
795     const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr;
796     if (!createNewObject)
797     {
798         // if we aren't creating a new object, we should be able to find it on
799         // d-bus
800         managedItem = findChassis(managedObj, it.key(), chassis);
801         if (managedItem == nullptr)
802         {
803             BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
804             messages::invalidObject(response->res,
805                                     crow::utility::urlFromPieces(
806                                         "redfish", "v1", "Chassis", chassis));
807             return CreatePIDRet::fail;
808         }
809     }
810 
811     if (!profile.empty() &&
812         (type == "PidControllers" || type == "FanControllers" ||
813          type == "StepwiseControllers"))
814     {
815         if (managedItem == nullptr)
816         {
817             output["Profiles"] = std::vector<std::string>{profile};
818         }
819         else
820         {
821             std::string interface;
822             if (type == "StepwiseControllers")
823             {
824                 interface = stepwiseConfigurationIface;
825             }
826             else
827             {
828                 interface = pidConfigurationIface;
829             }
830             bool ifaceFound = false;
831             for (const auto& iface : managedItem->second)
832             {
833                 if (iface.first == interface)
834                 {
835                     ifaceFound = true;
836                     for (const auto& prop : iface.second)
837                     {
838                         if (prop.first == "Profiles")
839                         {
840                             const std::vector<std::string>* curProfiles =
841                                 std::get_if<std::vector<std::string>>(
842                                     &(prop.second));
843                             if (curProfiles == nullptr)
844                             {
845                                 BMCWEB_LOG_ERROR
846                                     << "Illegal profiles in managed object";
847                                 messages::internalError(response->res);
848                                 return CreatePIDRet::fail;
849                             }
850                             if (std::find(curProfiles->begin(),
851                                           curProfiles->end(),
852                                           profile) == curProfiles->end())
853                             {
854                                 std::vector<std::string> newProfiles =
855                                     *curProfiles;
856                                 newProfiles.push_back(profile);
857                                 output["Profiles"] = newProfiles;
858                             }
859                         }
860                     }
861                 }
862             }
863 
864             if (!ifaceFound)
865             {
866                 BMCWEB_LOG_ERROR
867                     << "Failed to find interface in managed object";
868                 messages::internalError(response->res);
869                 return CreatePIDRet::fail;
870             }
871         }
872     }
873 
874     if (type == "PidControllers" || type == "FanControllers")
875     {
876         if (createNewObject)
877         {
878             output["Class"] = type == "PidControllers" ? std::string("temp")
879                                                        : std::string("fan");
880             output["Type"] = std::string("Pid");
881         }
882 
883         std::optional<std::vector<nlohmann::json>> zones;
884         std::optional<std::vector<std::string>> inputs;
885         std::optional<std::vector<std::string>> outputs;
886         std::map<std::string, std::optional<double>> doubles;
887         std::optional<std::string> setpointOffset;
888         if (!redfish::json_util::readJson(
889                 it.value(), response->res, "Inputs", inputs, "Outputs", outputs,
890                 "Zones", zones, "FFGainCoefficient",
891                 doubles["FFGainCoefficient"], "FFOffCoefficient",
892                 doubles["FFOffCoefficient"], "ICoefficient",
893                 doubles["ICoefficient"], "ILimitMax", doubles["ILimitMax"],
894                 "ILimitMin", doubles["ILimitMin"], "OutLimitMax",
895                 doubles["OutLimitMax"], "OutLimitMin", doubles["OutLimitMin"],
896                 "PCoefficient", doubles["PCoefficient"], "SetPoint",
897                 doubles["SetPoint"], "SetPointOffset", setpointOffset,
898                 "SlewNeg", doubles["SlewNeg"], "SlewPos", doubles["SlewPos"],
899                 "PositiveHysteresis", doubles["PositiveHysteresis"],
900                 "NegativeHysteresis", doubles["NegativeHysteresis"]))
901         {
902             BMCWEB_LOG_ERROR
903                 << "Illegal Property "
904                 << it.value().dump(2, ' ', true,
905                                    nlohmann::json::error_handler_t::replace);
906             return CreatePIDRet::fail;
907         }
908         if (zones)
909         {
910             std::vector<std::string> zonesStr;
911             if (!getZonesFromJsonReq(response, *zones, zonesStr))
912             {
913                 BMCWEB_LOG_ERROR << "Illegal Zones";
914                 return CreatePIDRet::fail;
915             }
916             if (chassis.empty() &&
917                 findChassis(managedObj, zonesStr[0], chassis) == nullptr)
918             {
919                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
920                 messages::invalidObject(
921                     response->res, crow::utility::urlFromPieces(
922                                        "redfish", "v1", "Chassis", chassis));
923                 return CreatePIDRet::fail;
924             }
925 
926             output["Zones"] = std::move(zonesStr);
927         }
928         if (inputs || outputs)
929         {
930             std::array<std::optional<std::vector<std::string>>*, 2> containers =
931                 {&inputs, &outputs};
932             size_t index = 0;
933             for (const auto& containerPtr : containers)
934             {
935                 std::optional<std::vector<std::string>>& container =
936                     *containerPtr;
937                 if (!container)
938                 {
939                     index++;
940                     continue;
941                 }
942 
943                 for (std::string& value : *container)
944                 {
945                     boost::replace_all(value, "_", " ");
946                 }
947                 std::string key;
948                 if (index == 0)
949                 {
950                     key = "Inputs";
951                 }
952                 else
953                 {
954                     key = "Outputs";
955                 }
956                 output[key] = *container;
957                 index++;
958             }
959         }
960 
961         if (setpointOffset)
962         {
963             // translate between redfish and dbus names
964             if (*setpointOffset == "UpperThresholdNonCritical")
965             {
966                 output["SetPointOffset"] = std::string("WarningLow");
967             }
968             else if (*setpointOffset == "LowerThresholdNonCritical")
969             {
970                 output["SetPointOffset"] = std::string("WarningHigh");
971             }
972             else if (*setpointOffset == "LowerThresholdCritical")
973             {
974                 output["SetPointOffset"] = std::string("CriticalLow");
975             }
976             else if (*setpointOffset == "UpperThresholdCritical")
977             {
978                 output["SetPointOffset"] = std::string("CriticalHigh");
979             }
980             else
981             {
982                 BMCWEB_LOG_ERROR << "Invalid setpointoffset "
983                                  << *setpointOffset;
984                 messages::propertyValueNotInList(response->res, it.key(),
985                                                  "SetPointOffset");
986                 return CreatePIDRet::fail;
987             }
988         }
989 
990         // doubles
991         for (const auto& pairs : doubles)
992         {
993             if (!pairs.second)
994             {
995                 continue;
996             }
997             BMCWEB_LOG_DEBUG << pairs.first << " = " << *pairs.second;
998             output[pairs.first] = *(pairs.second);
999         }
1000     }
1001 
1002     else if (type == "FanZones")
1003     {
1004         output["Type"] = std::string("Pid.Zone");
1005 
1006         std::optional<nlohmann::json> chassisContainer;
1007         std::optional<double> failSafePercent;
1008         std::optional<double> minThermalOutput;
1009         if (!redfish::json_util::readJson(it.value(), response->res, "Chassis",
1010                                           chassisContainer, "FailSafePercent",
1011                                           failSafePercent, "MinThermalOutput",
1012                                           minThermalOutput))
1013         {
1014             BMCWEB_LOG_ERROR
1015                 << "Illegal Property "
1016                 << it.value().dump(2, ' ', true,
1017                                    nlohmann::json::error_handler_t::replace);
1018             return CreatePIDRet::fail;
1019         }
1020 
1021         if (chassisContainer)
1022         {
1023 
1024             std::string chassisId;
1025             if (!redfish::json_util::readJson(*chassisContainer, response->res,
1026                                               "@odata.id", chassisId))
1027             {
1028                 BMCWEB_LOG_ERROR
1029                     << "Illegal Property "
1030                     << chassisContainer->dump(
1031                            2, ' ', true,
1032                            nlohmann::json::error_handler_t::replace);
1033                 return CreatePIDRet::fail;
1034             }
1035 
1036             // /redfish/v1/chassis/chassis_name/
1037             if (!dbus::utility::getNthStringFromPath(chassisId, 3, chassis))
1038             {
1039                 BMCWEB_LOG_ERROR << "Got invalid path " << chassisId;
1040                 messages::invalidObject(
1041                     response->res, crow::utility::urlFromPieces(
1042                                        "redfish", "v1", "Chassis", chassisId));
1043                 return CreatePIDRet::fail;
1044             }
1045         }
1046         if (minThermalOutput)
1047         {
1048             output["MinThermalOutput"] = *minThermalOutput;
1049         }
1050         if (failSafePercent)
1051         {
1052             output["FailSafePercent"] = *failSafePercent;
1053         }
1054     }
1055     else if (type == "StepwiseControllers")
1056     {
1057         output["Type"] = std::string("Stepwise");
1058 
1059         std::optional<std::vector<nlohmann::json>> zones;
1060         std::optional<std::vector<nlohmann::json>> steps;
1061         std::optional<std::vector<std::string>> inputs;
1062         std::optional<double> positiveHysteresis;
1063         std::optional<double> negativeHysteresis;
1064         std::optional<std::string> direction; // upper clipping curve vs lower
1065         if (!redfish::json_util::readJson(
1066                 it.value(), response->res, "Zones", zones, "Steps", steps,
1067                 "Inputs", inputs, "PositiveHysteresis", positiveHysteresis,
1068                 "NegativeHysteresis", negativeHysteresis, "Direction",
1069                 direction))
1070         {
1071             BMCWEB_LOG_ERROR
1072                 << "Illegal Property "
1073                 << it.value().dump(2, ' ', true,
1074                                    nlohmann::json::error_handler_t::replace);
1075             return CreatePIDRet::fail;
1076         }
1077 
1078         if (zones)
1079         {
1080             std::vector<std::string> zonesStrs;
1081             if (!getZonesFromJsonReq(response, *zones, zonesStrs))
1082             {
1083                 BMCWEB_LOG_ERROR << "Illegal Zones";
1084                 return CreatePIDRet::fail;
1085             }
1086             if (chassis.empty() &&
1087                 findChassis(managedObj, zonesStrs[0], chassis) == nullptr)
1088             {
1089                 BMCWEB_LOG_ERROR << "Failed to get chassis from config patch";
1090                 messages::invalidObject(
1091                     response->res, crow::utility::urlFromPieces(
1092                                        "redfish", "v1", "Chassis", chassis));
1093                 return CreatePIDRet::fail;
1094             }
1095             output["Zones"] = std::move(zonesStrs);
1096         }
1097         if (steps)
1098         {
1099             std::vector<double> readings;
1100             std::vector<double> outputs;
1101             for (auto& step : *steps)
1102             {
1103                 double target = 0.0;
1104                 double out = 0.0;
1105 
1106                 if (!redfish::json_util::readJson(step, response->res, "Target",
1107                                                   target, "Output", out))
1108                 {
1109                     BMCWEB_LOG_ERROR
1110                         << "Illegal Property "
1111                         << it.value().dump(
1112                                2, ' ', true,
1113                                nlohmann::json::error_handler_t::replace);
1114                     return CreatePIDRet::fail;
1115                 }
1116                 readings.emplace_back(target);
1117                 outputs.emplace_back(out);
1118             }
1119             output["Reading"] = std::move(readings);
1120             output["Output"] = std::move(outputs);
1121         }
1122         if (inputs)
1123         {
1124             for (std::string& value : *inputs)
1125             {
1126                 boost::replace_all(value, "_", " ");
1127             }
1128             output["Inputs"] = std::move(*inputs);
1129         }
1130         if (negativeHysteresis)
1131         {
1132             output["NegativeHysteresis"] = *negativeHysteresis;
1133         }
1134         if (positiveHysteresis)
1135         {
1136             output["PositiveHysteresis"] = *positiveHysteresis;
1137         }
1138         if (direction)
1139         {
1140             constexpr const std::array<const char*, 2> allowedDirections = {
1141                 "Ceiling", "Floor"};
1142             if (std::find(allowedDirections.begin(), allowedDirections.end(),
1143                           *direction) == allowedDirections.end())
1144             {
1145                 messages::propertyValueTypeError(response->res, "Direction",
1146                                                  *direction);
1147                 return CreatePIDRet::fail;
1148             }
1149             output["Class"] = *direction;
1150         }
1151     }
1152     else
1153     {
1154         BMCWEB_LOG_ERROR << "Illegal Type " << type;
1155         messages::propertyUnknown(response->res, type);
1156         return CreatePIDRet::fail;
1157     }
1158     return CreatePIDRet::patch;
1159 }
1160 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
1161 {
1162 
1163     GetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
1164         asyncResp(asyncRespIn)
1165 
1166     {}
1167 
1168     void run()
1169     {
1170         std::shared_ptr<GetPIDValues> self = shared_from_this();
1171 
1172         // get all configurations
1173         crow::connections::systemBus->async_method_call(
1174             [self](const boost::system::error_code ec,
1175                    const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
1176                 if (ec)
1177                 {
1178                     BMCWEB_LOG_ERROR << ec;
1179                     messages::internalError(self->asyncResp->res);
1180                     return;
1181                 }
1182                 self->subtree = subtreeLocal;
1183             },
1184             "xyz.openbmc_project.ObjectMapper",
1185             "/xyz/openbmc_project/object_mapper",
1186             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1187             std::array<const char*, 4>{
1188                 pidConfigurationIface, pidZoneConfigurationIface,
1189                 objectManagerIface, stepwiseConfigurationIface});
1190 
1191         // at the same time get the selected profile
1192         crow::connections::systemBus->async_method_call(
1193             [self](const boost::system::error_code ec,
1194                    const crow::openbmc_mapper::GetSubTreeType& subtreeLocal) {
1195                 if (ec || subtreeLocal.empty())
1196                 {
1197                     return;
1198                 }
1199                 if (subtreeLocal[0].second.size() != 1)
1200                 {
1201                     // invalid mapper response, should never happen
1202                     BMCWEB_LOG_ERROR << "GetPIDValues: Mapper Error";
1203                     messages::internalError(self->asyncResp->res);
1204                     return;
1205                 }
1206 
1207                 const std::string& path = subtreeLocal[0].first;
1208                 const std::string& owner = subtreeLocal[0].second[0].first;
1209                 crow::connections::systemBus->async_method_call(
1210                     [path, owner,
1211                      self](const boost::system::error_code ec2,
1212                            const boost::container::flat_map<
1213                                std::string, dbus::utility::DbusVariantType>&
1214                                resp) {
1215                         if (ec2)
1216                         {
1217                             BMCWEB_LOG_ERROR
1218                                 << "GetPIDValues: Can't get thermalModeIface "
1219                                 << path;
1220                             messages::internalError(self->asyncResp->res);
1221                             return;
1222                         }
1223                         const std::string* current = nullptr;
1224                         const std::vector<std::string>* supported = nullptr;
1225                         for (const auto& [key, value] : resp)
1226                         {
1227                             if (key == "Current")
1228                             {
1229                                 current = std::get_if<std::string>(&value);
1230                                 if (current == nullptr)
1231                                 {
1232                                     BMCWEB_LOG_ERROR
1233                                         << "GetPIDValues: thermal mode iface invalid "
1234                                         << path;
1235                                     messages::internalError(
1236                                         self->asyncResp->res);
1237                                     return;
1238                                 }
1239                             }
1240                             if (key == "Supported")
1241                             {
1242                                 supported =
1243                                     std::get_if<std::vector<std::string>>(
1244                                         &value);
1245                                 if (supported == nullptr)
1246                                 {
1247                                     BMCWEB_LOG_ERROR
1248                                         << "GetPIDValues: thermal mode iface invalid"
1249                                         << path;
1250                                     messages::internalError(
1251                                         self->asyncResp->res);
1252                                     return;
1253                                 }
1254                             }
1255                         }
1256                         if (current == nullptr || supported == nullptr)
1257                         {
1258                             BMCWEB_LOG_ERROR
1259                                 << "GetPIDValues: thermal mode iface invalid "
1260                                 << path;
1261                             messages::internalError(self->asyncResp->res);
1262                             return;
1263                         }
1264                         self->currentProfile = *current;
1265                         self->supportedProfiles = *supported;
1266                     },
1267                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1268                     thermalModeIface);
1269             },
1270             "xyz.openbmc_project.ObjectMapper",
1271             "/xyz/openbmc_project/object_mapper",
1272             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1273             std::array<const char*, 1>{thermalModeIface});
1274     }
1275 
1276     ~GetPIDValues()
1277     {
1278         if (asyncResp->res.result() != boost::beast::http::status::ok)
1279         {
1280             return;
1281         }
1282         // create map of <connection, path to objMgr>>
1283         boost::container::flat_map<std::string, std::string> objectMgrPaths;
1284         boost::container::flat_set<std::string> calledConnections;
1285         for (const auto& pathGroup : subtree)
1286         {
1287             for (const auto& connectionGroup : pathGroup.second)
1288             {
1289                 auto findConnection =
1290                     calledConnections.find(connectionGroup.first);
1291                 if (findConnection != calledConnections.end())
1292                 {
1293                     break;
1294                 }
1295                 for (const std::string& interface : connectionGroup.second)
1296                 {
1297                     if (interface == objectManagerIface)
1298                     {
1299                         objectMgrPaths[connectionGroup.first] = pathGroup.first;
1300                     }
1301                     // this list is alphabetical, so we
1302                     // should have found the objMgr by now
1303                     if (interface == pidConfigurationIface ||
1304                         interface == pidZoneConfigurationIface ||
1305                         interface == stepwiseConfigurationIface)
1306                     {
1307                         auto findObjMgr =
1308                             objectMgrPaths.find(connectionGroup.first);
1309                         if (findObjMgr == objectMgrPaths.end())
1310                         {
1311                             BMCWEB_LOG_DEBUG << connectionGroup.first
1312                                              << "Has no Object Manager";
1313                             continue;
1314                         }
1315 
1316                         calledConnections.insert(connectionGroup.first);
1317 
1318                         asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1319                                          currentProfile, supportedProfiles,
1320                                          asyncResp);
1321                         break;
1322                     }
1323                 }
1324             }
1325         }
1326     }
1327 
1328     GetPIDValues(const GetPIDValues&) = delete;
1329     GetPIDValues(GetPIDValues&&) = delete;
1330     GetPIDValues& operator=(const GetPIDValues&) = delete;
1331     GetPIDValues& operator=(GetPIDValues&&) = delete;
1332 
1333     std::vector<std::string> supportedProfiles;
1334     std::string currentProfile;
1335     crow::openbmc_mapper::GetSubTreeType subtree;
1336     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1337 };
1338 
1339 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1340 {
1341 
1342     SetPIDValues(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
1343                  nlohmann::json& data) :
1344         asyncResp(asyncRespIn)
1345     {
1346 
1347         std::optional<nlohmann::json> pidControllers;
1348         std::optional<nlohmann::json> fanControllers;
1349         std::optional<nlohmann::json> fanZones;
1350         std::optional<nlohmann::json> stepwiseControllers;
1351 
1352         if (!redfish::json_util::readJson(
1353                 data, asyncResp->res, "PidControllers", pidControllers,
1354                 "FanControllers", fanControllers, "FanZones", fanZones,
1355                 "StepwiseControllers", stepwiseControllers, "Profile", profile))
1356         {
1357             BMCWEB_LOG_ERROR
1358                 << "Illegal Property "
1359                 << data.dump(2, ' ', true,
1360                              nlohmann::json::error_handler_t::replace);
1361             return;
1362         }
1363         configuration.emplace_back("PidControllers", std::move(pidControllers));
1364         configuration.emplace_back("FanControllers", std::move(fanControllers));
1365         configuration.emplace_back("FanZones", std::move(fanZones));
1366         configuration.emplace_back("StepwiseControllers",
1367                                    std::move(stepwiseControllers));
1368     }
1369 
1370     SetPIDValues(const SetPIDValues&) = delete;
1371     SetPIDValues(SetPIDValues&&) = delete;
1372     SetPIDValues& operator=(const SetPIDValues&) = delete;
1373     SetPIDValues& operator=(SetPIDValues&&) = delete;
1374 
1375     void run()
1376     {
1377         if (asyncResp->res.result() != boost::beast::http::status::ok)
1378         {
1379             return;
1380         }
1381 
1382         std::shared_ptr<SetPIDValues> self = shared_from_this();
1383 
1384         // todo(james): might make sense to do a mapper call here if this
1385         // interface gets more traction
1386         crow::connections::systemBus->async_method_call(
1387             [self](const boost::system::error_code ec,
1388                    const dbus::utility::ManagedObjectType& mObj) {
1389                 if (ec)
1390                 {
1391                     BMCWEB_LOG_ERROR << "Error communicating to Entity Manager";
1392                     messages::internalError(self->asyncResp->res);
1393                     return;
1394                 }
1395                 const std::array<const char*, 3> configurations = {
1396                     pidConfigurationIface, pidZoneConfigurationIface,
1397                     stepwiseConfigurationIface};
1398 
1399                 for (const auto& [path, object] : mObj)
1400                 {
1401                     for (const auto& [interface, _] : object)
1402                     {
1403                         if (std::find(configurations.begin(),
1404                                       configurations.end(),
1405                                       interface) != configurations.end())
1406                         {
1407                             self->objectCount++;
1408                             break;
1409                         }
1410                     }
1411                 }
1412                 self->managedObj = mObj;
1413             },
1414             "xyz.openbmc_project.EntityManager", "/", objectManagerIface,
1415             "GetManagedObjects");
1416 
1417         // at the same time get the profile information
1418         crow::connections::systemBus->async_method_call(
1419             [self](const boost::system::error_code ec,
1420                    const crow::openbmc_mapper::GetSubTreeType& subtree) {
1421                 if (ec || subtree.empty())
1422                 {
1423                     return;
1424                 }
1425                 if (subtree[0].second.empty())
1426                 {
1427                     // invalid mapper response, should never happen
1428                     BMCWEB_LOG_ERROR << "SetPIDValues: Mapper Error";
1429                     messages::internalError(self->asyncResp->res);
1430                     return;
1431                 }
1432 
1433                 const std::string& path = subtree[0].first;
1434                 const std::string& owner = subtree[0].second[0].first;
1435                 crow::connections::systemBus->async_method_call(
1436                     [self, path, owner](
1437                         const boost::system::error_code ec2,
1438                         const boost::container::flat_map<
1439                             std::string, dbus::utility::DbusVariantType>& r) {
1440                         if (ec2)
1441                         {
1442                             BMCWEB_LOG_ERROR
1443                                 << "SetPIDValues: Can't get thermalModeIface "
1444                                 << path;
1445                             messages::internalError(self->asyncResp->res);
1446                             return;
1447                         }
1448                         const std::string* current = nullptr;
1449                         const std::vector<std::string>* supported = nullptr;
1450                         for (const auto& [key, value] : r)
1451                         {
1452                             if (key == "Current")
1453                             {
1454                                 current = std::get_if<std::string>(&value);
1455                                 if (current == nullptr)
1456                                 {
1457                                     BMCWEB_LOG_ERROR
1458                                         << "SetPIDValues: thermal mode iface invalid "
1459                                         << path;
1460                                     messages::internalError(
1461                                         self->asyncResp->res);
1462                                     return;
1463                                 }
1464                             }
1465                             if (key == "Supported")
1466                             {
1467                                 supported =
1468                                     std::get_if<std::vector<std::string>>(
1469                                         &value);
1470                                 if (supported == nullptr)
1471                                 {
1472                                     BMCWEB_LOG_ERROR
1473                                         << "SetPIDValues: thermal mode iface invalid"
1474                                         << path;
1475                                     messages::internalError(
1476                                         self->asyncResp->res);
1477                                     return;
1478                                 }
1479                             }
1480                         }
1481                         if (current == nullptr || supported == nullptr)
1482                         {
1483                             BMCWEB_LOG_ERROR
1484                                 << "SetPIDValues: thermal mode iface invalid "
1485                                 << path;
1486                             messages::internalError(self->asyncResp->res);
1487                             return;
1488                         }
1489                         self->currentProfile = *current;
1490                         self->supportedProfiles = *supported;
1491                         self->profileConnection = owner;
1492                         self->profilePath = path;
1493                     },
1494                     owner, path, "org.freedesktop.DBus.Properties", "GetAll",
1495                     thermalModeIface);
1496             },
1497             "xyz.openbmc_project.ObjectMapper",
1498             "/xyz/openbmc_project/object_mapper",
1499             "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0,
1500             std::array<const char*, 1>{thermalModeIface});
1501     }
1502     void pidSetDone()
1503     {
1504         if (asyncResp->res.result() != boost::beast::http::status::ok)
1505         {
1506             return;
1507         }
1508         std::shared_ptr<bmcweb::AsyncResp> response = asyncResp;
1509         if (profile)
1510         {
1511             if (std::find(supportedProfiles.begin(), supportedProfiles.end(),
1512                           *profile) == supportedProfiles.end())
1513             {
1514                 messages::actionParameterUnknown(response->res, "Profile",
1515                                                  *profile);
1516                 return;
1517             }
1518             currentProfile = *profile;
1519             crow::connections::systemBus->async_method_call(
1520                 [response](const boost::system::error_code ec) {
1521                     if (ec)
1522                     {
1523                         BMCWEB_LOG_ERROR << "Error patching profile" << ec;
1524                         messages::internalError(response->res);
1525                     }
1526                 },
1527                 profileConnection, profilePath,
1528                 "org.freedesktop.DBus.Properties", "Set", thermalModeIface,
1529                 "Current", dbus::utility::DbusVariantType(*profile));
1530         }
1531 
1532         for (auto& containerPair : configuration)
1533         {
1534             auto& container = containerPair.second;
1535             if (!container)
1536             {
1537                 continue;
1538             }
1539             BMCWEB_LOG_DEBUG << *container;
1540 
1541             std::string& type = containerPair.first;
1542 
1543             for (nlohmann::json::iterator it = container->begin();
1544                  it != container->end(); ++it)
1545             {
1546                 const auto& name = it.key();
1547                 BMCWEB_LOG_DEBUG << "looking for " << name;
1548 
1549                 auto pathItr =
1550                     std::find_if(managedObj.begin(), managedObj.end(),
1551                                  [&name](const auto& obj) {
1552                                      return boost::algorithm::ends_with(
1553                                          obj.first.str, "/" + name);
1554                                  });
1555                 boost::container::flat_map<std::string,
1556                                            dbus::utility::DbusVariantType>
1557                     output;
1558 
1559                 output.reserve(16); // The pid interface length
1560 
1561                 // determines if we're patching entity-manager or
1562                 // creating a new object
1563                 bool createNewObject = (pathItr == managedObj.end());
1564                 BMCWEB_LOG_DEBUG << "Found = " << !createNewObject;
1565 
1566                 std::string iface;
1567                 /*
1568                 if (type == "PidControllers" || type == "FanControllers")
1569                 {
1570                     iface = pidConfigurationIface;
1571                     if (!createNewObject &&
1572                         pathItr->second.find(pidConfigurationIface) ==
1573                             pathItr->second.end())
1574                     {
1575                         createNewObject = true;
1576                     }
1577                 }
1578                 else if (type == "FanZones")
1579                 {
1580                     iface = pidZoneConfigurationIface;
1581                     if (!createNewObject &&
1582                         pathItr->second.find(pidZoneConfigurationIface) ==
1583                             pathItr->second.end())
1584                     {
1585 
1586                         createNewObject = true;
1587                     }
1588                 }
1589                 else if (type == "StepwiseControllers")
1590                 {
1591                     iface = stepwiseConfigurationIface;
1592                     if (!createNewObject &&
1593                         pathItr->second.find(stepwiseConfigurationIface) ==
1594                             pathItr->second.end())
1595                     {
1596                         createNewObject = true;
1597                     }
1598                 }*/
1599 
1600                 if (createNewObject && it.value() == nullptr)
1601                 {
1602                     // can't delete a non-existent object
1603                     messages::propertyValueNotInList(response->res,
1604                                                      it.value().dump(), name);
1605                     continue;
1606                 }
1607 
1608                 std::string path;
1609                 if (pathItr != managedObj.end())
1610                 {
1611                     path = pathItr->first.str;
1612                 }
1613 
1614                 BMCWEB_LOG_DEBUG << "Create new = " << createNewObject << "\n";
1615 
1616                 // arbitrary limit to avoid attacks
1617                 constexpr const size_t controllerLimit = 500;
1618                 if (createNewObject && objectCount >= controllerLimit)
1619                 {
1620                     messages::resourceExhaustion(response->res, type);
1621                     continue;
1622                 }
1623 
1624                 output["Name"] = boost::replace_all_copy(name, "_", " ");
1625 
1626                 std::string chassis;
1627                 CreatePIDRet ret = createPidInterface(
1628                     response, type, it, path, managedObj, createNewObject,
1629                     output, chassis, currentProfile);
1630                 if (ret == CreatePIDRet::fail)
1631                 {
1632                     return;
1633                 }
1634                 if (ret == CreatePIDRet::del)
1635                 {
1636                     continue;
1637                 }
1638 
1639                 if (!createNewObject)
1640                 {
1641                     for (const auto& property : output)
1642                     {
1643                         crow::connections::systemBus->async_method_call(
1644                             [response,
1645                              propertyName{std::string(property.first)}](
1646                                 const boost::system::error_code ec) {
1647                                 if (ec)
1648                                 {
1649                                     BMCWEB_LOG_ERROR << "Error patching "
1650                                                      << propertyName << ": "
1651                                                      << ec;
1652                                     messages::internalError(response->res);
1653                                     return;
1654                                 }
1655                                 messages::success(response->res);
1656                             },
1657                             "xyz.openbmc_project.EntityManager", path,
1658                             "org.freedesktop.DBus.Properties", "Set", iface,
1659                             property.first, property.second);
1660                     }
1661                 }
1662                 else
1663                 {
1664                     if (chassis.empty())
1665                     {
1666                         BMCWEB_LOG_ERROR << "Failed to get chassis from config";
1667                         messages::internalError(response->res);
1668                         return;
1669                     }
1670 
1671                     bool foundChassis = false;
1672                     for (const auto& obj : managedObj)
1673                     {
1674                         if (boost::algorithm::ends_with(obj.first.str, chassis))
1675                         {
1676                             chassis = obj.first.str;
1677                             foundChassis = true;
1678                             break;
1679                         }
1680                     }
1681                     if (!foundChassis)
1682                     {
1683                         BMCWEB_LOG_ERROR << "Failed to find chassis on dbus";
1684                         messages::resourceMissingAtURI(
1685                             response->res,
1686                             crow::utility::urlFromPieces("redfish", "v1",
1687                                                          "Chassis", chassis));
1688                         return;
1689                     }
1690 
1691                     crow::connections::systemBus->async_method_call(
1692                         [response](const boost::system::error_code ec) {
1693                             if (ec)
1694                             {
1695                                 BMCWEB_LOG_ERROR << "Error Adding Pid Object "
1696                                                  << ec;
1697                                 messages::internalError(response->res);
1698                                 return;
1699                             }
1700                             messages::success(response->res);
1701                         },
1702                         "xyz.openbmc_project.EntityManager", chassis,
1703                         "xyz.openbmc_project.AddObject", "AddObject", output);
1704                 }
1705             }
1706         }
1707     }
1708 
1709     ~SetPIDValues()
1710     {
1711         try
1712         {
1713             pidSetDone();
1714         }
1715         catch (...)
1716         {
1717             BMCWEB_LOG_CRITICAL << "pidSetDone threw exception";
1718         }
1719     }
1720 
1721     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1722     std::vector<std::pair<std::string, std::optional<nlohmann::json>>>
1723         configuration;
1724     std::optional<std::string> profile;
1725     dbus::utility::ManagedObjectType managedObj;
1726     std::vector<std::string> supportedProfiles;
1727     std::string currentProfile;
1728     std::string profileConnection;
1729     std::string profilePath;
1730     size_t objectCount = 0;
1731 };
1732 
1733 /**
1734  * @brief Retrieves BMC manager location data over DBus
1735  *
1736  * @param[in] aResp Shared pointer for completing asynchronous calls
1737  * @param[in] connectionName - service name
1738  * @param[in] path - object path
1739  * @return none
1740  */
1741 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1742                         const std::string& connectionName,
1743                         const std::string& path)
1744 {
1745     BMCWEB_LOG_DEBUG << "Get BMC manager Location data.";
1746 
1747     sdbusplus::asio::getProperty<std::string>(
1748         *crow::connections::systemBus, connectionName, path,
1749         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
1750         [aResp](const boost::system::error_code ec,
1751                 const std::string& property) {
1752             if (ec)
1753             {
1754                 BMCWEB_LOG_DEBUG << "DBUS response error for "
1755                                     "Location";
1756                 messages::internalError(aResp->res);
1757                 return;
1758             }
1759 
1760             aResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
1761                 property;
1762         });
1763 }
1764 // avoid name collision systems.hpp
1765 inline void
1766     managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
1767 {
1768     BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time";
1769 
1770     sdbusplus::asio::getProperty<uint64_t>(
1771         *crow::connections::systemBus, "xyz.openbmc_project.State.BMC",
1772         "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC",
1773         "LastRebootTime",
1774         [aResp](const boost::system::error_code ec,
1775                 const uint64_t lastResetTime) {
1776             if (ec)
1777             {
1778                 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec;
1779                 return;
1780             }
1781 
1782             // LastRebootTime is epoch time, in milliseconds
1783             // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
1784             uint64_t lastResetTimeStamp = lastResetTime / 1000;
1785 
1786             // Convert to ISO 8601 standard
1787             aResp->res.jsonValue["LastResetTime"] =
1788                 crow::utility::getDateTimeUint(lastResetTimeStamp);
1789         });
1790 }
1791 
1792 /**
1793  * @brief Set the running firmware image
1794  *
1795  * @param[i,o] aResp - Async response object
1796  * @param[i] runningFirmwareTarget - Image to make the running image
1797  *
1798  * @return void
1799  */
1800 inline void
1801     setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
1802                            const std::string& runningFirmwareTarget)
1803 {
1804     // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id>
1805     std::string::size_type idPos = runningFirmwareTarget.rfind('/');
1806     if (idPos == std::string::npos)
1807     {
1808         messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1809                                          "@odata.id");
1810         BMCWEB_LOG_DEBUG << "Can't parse firmware ID!";
1811         return;
1812     }
1813     idPos++;
1814     if (idPos >= runningFirmwareTarget.size())
1815     {
1816         messages::propertyValueNotInList(aResp->res, runningFirmwareTarget,
1817                                          "@odata.id");
1818         BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1819         return;
1820     }
1821     std::string firmwareId = runningFirmwareTarget.substr(idPos);
1822 
1823     // Make sure the image is valid before setting priority
1824     crow::connections::systemBus->async_method_call(
1825         [aResp, firmwareId,
1826          runningFirmwareTarget](const boost::system::error_code ec,
1827                                 dbus::utility::ManagedObjectType& subtree) {
1828             if (ec)
1829             {
1830                 BMCWEB_LOG_DEBUG << "D-Bus response error getting objects.";
1831                 messages::internalError(aResp->res);
1832                 return;
1833             }
1834 
1835             if (subtree.empty())
1836             {
1837                 BMCWEB_LOG_DEBUG << "Can't find image!";
1838                 messages::internalError(aResp->res);
1839                 return;
1840             }
1841 
1842             bool foundImage = false;
1843             for (auto& object : subtree)
1844             {
1845                 const std::string& path =
1846                     static_cast<const std::string&>(object.first);
1847                 std::size_t idPos2 = path.rfind('/');
1848 
1849                 if (idPos2 == std::string::npos)
1850                 {
1851                     continue;
1852                 }
1853 
1854                 idPos2++;
1855                 if (idPos2 >= path.size())
1856                 {
1857                     continue;
1858                 }
1859 
1860                 if (path.substr(idPos2) == firmwareId)
1861                 {
1862                     foundImage = true;
1863                     break;
1864                 }
1865             }
1866 
1867             if (!foundImage)
1868             {
1869                 messages::propertyValueNotInList(
1870                     aResp->res, runningFirmwareTarget, "@odata.id");
1871                 BMCWEB_LOG_DEBUG << "Invalid firmware ID.";
1872                 return;
1873             }
1874 
1875             BMCWEB_LOG_DEBUG
1876                 << "Setting firmware version " + firmwareId + " to priority 0.";
1877 
1878             // Only support Immediate
1879             // An addition could be a Redfish Setting like
1880             // ActiveSoftwareImageApplyTime and support OnReset
1881             crow::connections::systemBus->async_method_call(
1882                 [aResp](const boost::system::error_code ec) {
1883                     if (ec)
1884                     {
1885                         BMCWEB_LOG_DEBUG << "D-Bus response error setting.";
1886                         messages::internalError(aResp->res);
1887                         return;
1888                     }
1889                     doBMCGracefulRestart(aResp);
1890                 },
1891 
1892                 "xyz.openbmc_project.Software.BMC.Updater",
1893                 "/xyz/openbmc_project/software/" + firmwareId,
1894                 "org.freedesktop.DBus.Properties", "Set",
1895                 "xyz.openbmc_project.Software.RedundancyPriority", "Priority",
1896                 dbus::utility::DbusVariantType(static_cast<uint8_t>(0)));
1897         },
1898         "xyz.openbmc_project.Software.BMC.Updater",
1899         "/xyz/openbmc_project/software", "org.freedesktop.DBus.ObjectManager",
1900         "GetManagedObjects");
1901 }
1902 
1903 inline void setDateTime(std::shared_ptr<bmcweb::AsyncResp> aResp,
1904                         std::string datetime)
1905 {
1906     BMCWEB_LOG_DEBUG << "Set date time: " << datetime;
1907 
1908     std::stringstream stream(datetime);
1909     // Convert from ISO 8601 to boost local_time
1910     // (BMC only has time in UTC)
1911     boost::posix_time::ptime posixTime;
1912     boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
1913     // Facet gets deleted with the stringsteam
1914     auto ifc = std::make_unique<boost::local_time::local_time_input_facet>(
1915         "%Y-%m-%d %H:%M:%S%F %ZP");
1916     stream.imbue(std::locale(stream.getloc(), ifc.release()));
1917 
1918     boost::local_time::local_date_time ldt(boost::local_time::not_a_date_time);
1919 
1920     if (stream >> ldt)
1921     {
1922         posixTime = ldt.utc_time();
1923         boost::posix_time::time_duration dur = posixTime - epoch;
1924         uint64_t durMicroSecs = static_cast<uint64_t>(dur.total_microseconds());
1925         crow::connections::systemBus->async_method_call(
1926             [aResp{std::move(aResp)}, datetime{std::move(datetime)}](
1927                 const boost::system::error_code ec) {
1928                 if (ec)
1929                 {
1930                     BMCWEB_LOG_DEBUG << "Failed to set elapsed time. "
1931                                         "DBUS response error "
1932                                      << ec;
1933                     messages::internalError(aResp->res);
1934                     return;
1935                 }
1936                 aResp->res.jsonValue["DateTime"] = datetime;
1937             },
1938             "xyz.openbmc_project.Time.Manager", "/xyz/openbmc_project/time/bmc",
1939             "org.freedesktop.DBus.Properties", "Set",
1940             "xyz.openbmc_project.Time.EpochTime", "Elapsed",
1941             dbus::utility::DbusVariantType(durMicroSecs));
1942     }
1943     else
1944     {
1945         messages::propertyValueFormatError(aResp->res, datetime, "DateTime");
1946         return;
1947     }
1948 }
1949 
1950 inline void requestRoutesManager(App& app)
1951 {
1952     std::string uuid = persistent_data::getConfig().systemUuid;
1953 
1954     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
1955         .privileges(redfish::privileges::getManager)
1956         .methods(boost::beast::http::verb::get)([uuid](const crow::Request&,
1957                                                        const std::shared_ptr<
1958                                                            bmcweb::AsyncResp>&
1959                                                            asyncResp) {
1960             asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers/bmc";
1961             asyncResp->res.jsonValue["@odata.type"] =
1962                 "#Manager.v1_11_0.Manager";
1963             asyncResp->res.jsonValue["Id"] = "bmc";
1964             asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
1965             asyncResp->res.jsonValue["Description"] =
1966                 "Baseboard Management Controller";
1967             asyncResp->res.jsonValue["PowerState"] = "On";
1968             asyncResp->res.jsonValue["Status"] = {{"State", "Enabled"},
1969                                                   {"Health", "OK"}};
1970             asyncResp->res.jsonValue["ManagerType"] = "BMC";
1971             asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
1972             asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
1973             asyncResp->res.jsonValue["Model"] =
1974                 "OpenBmc"; // TODO(ed), get model
1975 
1976             asyncResp->res.jsonValue["LogServices"] = {
1977                 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices"}};
1978 
1979             asyncResp->res.jsonValue["NetworkProtocol"] = {
1980                 {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol"}};
1981 
1982             asyncResp->res.jsonValue["EthernetInterfaces"] = {
1983                 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}};
1984 
1985 #ifdef BMCWEB_ENABLE_VM_NBDPROXY
1986             asyncResp->res.jsonValue["VirtualMedia"] = {
1987                 {"@odata.id", "/redfish/v1/Managers/bmc/VirtualMedia"}};
1988 #endif // BMCWEB_ENABLE_VM_NBDPROXY
1989 
1990             // default oem data
1991             nlohmann::json& oem = asyncResp->res.jsonValue["Oem"];
1992             nlohmann::json& oemOpenbmc = oem["OpenBmc"];
1993             oem["@odata.type"] = "#OemManager.Oem";
1994             oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem";
1995             oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc";
1996             oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc";
1997             oemOpenbmc["Certificates"] = {
1998                 {"@odata.id",
1999                  "/redfish/v1/Managers/bmc/Truststore/Certificates"}};
2000 
2001             // Manager.Reset (an action) can be many values, OpenBMC only
2002             // supports BMC reboot.
2003             nlohmann::json& managerReset =
2004                 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
2005             managerReset["target"] =
2006                 "/redfish/v1/Managers/bmc/Actions/Manager.Reset";
2007             managerReset["@Redfish.ActionInfo"] =
2008                 "/redfish/v1/Managers/bmc/ResetActionInfo";
2009 
2010             // ResetToDefaults (Factory Reset) has values like
2011             // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
2012             // on OpenBMC
2013             nlohmann::json& resetToDefaults =
2014                 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
2015             resetToDefaults["target"] =
2016                 "/redfish/v1/Managers/bmc/Actions/Manager.ResetToDefaults";
2017             resetToDefaults["ResetType@Redfish.AllowableValues"] = {"ResetAll"};
2018 
2019             std::pair<std::string, std::string> redfishDateTimeOffset =
2020                 crow::utility::getDateTimeOffsetNow();
2021 
2022             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2023             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2024                 redfishDateTimeOffset.second;
2025 
2026             // TODO (Gunnar): Remove these one day since moved to ComputerSystem
2027             // Still used by OCP profiles
2028             // https://github.com/opencomputeproject/OCP-Profiles/issues/23
2029             // Fill in SerialConsole info
2030             asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true;
2031             asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] =
2032                 15;
2033             asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] =
2034                 {"IPMI", "SSH"};
2035 #ifdef BMCWEB_ENABLE_KVM
2036             // Fill in GraphicalConsole info
2037             asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] =
2038                 true;
2039             asyncResp->res
2040                 .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4;
2041             asyncResp->res.jsonValue["GraphicalConsole"]
2042                                     ["ConnectTypesSupported"] = {"KVMIP"};
2043 #endif // BMCWEB_ENABLE_KVM
2044 
2045             asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] =
2046                 1;
2047             asyncResp->res.jsonValue["Links"]["ManagerForServers"] = {
2048                 {{"@odata.id", "/redfish/v1/Systems/system"}}};
2049 
2050             auto health = std::make_shared<HealthPopulate>(asyncResp);
2051             health->isManagersHealth = true;
2052             health->populate();
2053 
2054             fw_util::populateFirmwareInformation(asyncResp, fw_util::bmcPurpose,
2055                                                  "FirmwareVersion", true);
2056 
2057             managerGetLastResetTime(asyncResp);
2058 
2059             auto pids = std::make_shared<GetPIDValues>(asyncResp);
2060             pids->run();
2061 
2062             getMainChassisId(
2063                 asyncResp, [](const std::string& chassisId,
2064                               const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
2065                     aRsp->res
2066                         .jsonValue["Links"]["ManagerForChassis@odata.count"] =
2067                         1;
2068                     aRsp->res.jsonValue["Links"]["ManagerForChassis"] = {
2069                         {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
2070                     aRsp->res.jsonValue["Links"]["ManagerInChassis"] = {
2071                         {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
2072                 });
2073 
2074             static bool started = false;
2075 
2076             if (!started)
2077             {
2078                 sdbusplus::asio::getProperty<double>(
2079                     *crow::connections::systemBus, "org.freedesktop.systemd1",
2080                     "/org/freedesktop/systemd1",
2081                     "org.freedesktop.systemd1.Manager", "Progress",
2082                     [asyncResp](const boost::system::error_code ec,
2083                                 const double& val) {
2084                         if (ec)
2085                         {
2086                             BMCWEB_LOG_ERROR << "Error while getting progress";
2087                             messages::internalError(asyncResp->res);
2088                             return;
2089                         }
2090                         if (val < 1.0)
2091                         {
2092                             asyncResp->res.jsonValue["Status"]["State"] =
2093                                 "Starting";
2094                             started = true;
2095                         }
2096                     });
2097             }
2098 
2099             crow::connections::systemBus->async_method_call(
2100                 [asyncResp](
2101                     const boost::system::error_code ec,
2102                     const std::vector<
2103                         std::pair<std::string,
2104                                   std::vector<std::pair<
2105                                       std::string, std::vector<std::string>>>>>&
2106                         subtree) {
2107                     if (ec)
2108                     {
2109                         BMCWEB_LOG_DEBUG
2110                             << "D-Bus response error on GetSubTree " << ec;
2111                         return;
2112                     }
2113                     if (subtree.empty())
2114                     {
2115                         BMCWEB_LOG_DEBUG << "Can't find bmc D-Bus object!";
2116                         return;
2117                     }
2118                     // Assume only 1 bmc D-Bus object
2119                     // Throw an error if there is more than 1
2120                     if (subtree.size() > 1)
2121                     {
2122                         BMCWEB_LOG_DEBUG
2123                             << "Found more than 1 bmc D-Bus object!";
2124                         messages::internalError(asyncResp->res);
2125                         return;
2126                     }
2127 
2128                     if (subtree[0].first.empty() ||
2129                         subtree[0].second.size() != 1)
2130                     {
2131                         BMCWEB_LOG_DEBUG << "Error getting bmc D-Bus object!";
2132                         messages::internalError(asyncResp->res);
2133                         return;
2134                     }
2135 
2136                     const std::string& path = subtree[0].first;
2137                     const std::string& connectionName =
2138                         subtree[0].second[0].first;
2139 
2140                     for (const auto& interfaceName :
2141                          subtree[0].second[0].second)
2142                     {
2143                         if (interfaceName ==
2144                             "xyz.openbmc_project.Inventory.Decorator.Asset")
2145                         {
2146                             crow::connections::systemBus->async_method_call(
2147                                 [asyncResp](
2148                                     const boost::system::error_code ec,
2149                                     const std::vector<std::pair<
2150                                         std::string,
2151                                         dbus::utility::DbusVariantType>>&
2152                                         propertiesList) {
2153                                     if (ec)
2154                                     {
2155                                         BMCWEB_LOG_DEBUG
2156                                             << "Can't get bmc asset!";
2157                                         return;
2158                                     }
2159                                     for (const std::pair<
2160                                              std::string,
2161                                              dbus::utility::DbusVariantType>&
2162                                              property : propertiesList)
2163                                     {
2164                                         const std::string& propertyName =
2165                                             property.first;
2166 
2167                                         if ((propertyName == "PartNumber") ||
2168                                             (propertyName == "SerialNumber") ||
2169                                             (propertyName == "Manufacturer") ||
2170                                             (propertyName == "Model") ||
2171                                             (propertyName == "SparePartNumber"))
2172                                         {
2173                                             const std::string* value =
2174                                                 std::get_if<std::string>(
2175                                                     &property.second);
2176                                             if (value == nullptr)
2177                                             {
2178                                                 // illegal property
2179                                                 messages::internalError(
2180                                                     asyncResp->res);
2181                                                 return;
2182                                             }
2183                                             asyncResp->res
2184                                                 .jsonValue[propertyName] =
2185                                                 *value;
2186                                         }
2187                                     }
2188                                 },
2189                                 connectionName, path,
2190                                 "org.freedesktop.DBus.Properties", "GetAll",
2191                                 "xyz.openbmc_project.Inventory.Decorator.Asset");
2192                         }
2193                         else if (
2194                             interfaceName ==
2195                             "xyz.openbmc_project.Inventory.Decorator.LocationCode")
2196                         {
2197                             getLocation(asyncResp, connectionName, path);
2198                         }
2199                     }
2200                 },
2201                 "xyz.openbmc_project.ObjectMapper",
2202                 "/xyz/openbmc_project/object_mapper",
2203                 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
2204                 "/xyz/openbmc_project/inventory", int32_t(0),
2205                 std::array<const char*, 1>{
2206                     "xyz.openbmc_project.Inventory.Item.Bmc"});
2207         });
2208 
2209     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/")
2210         .privileges(redfish::privileges::patchManager)
2211         .methods(
2212             boost::beast::http::verb::
2213                 patch)([](const crow::Request& req,
2214                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2215             std::optional<nlohmann::json> oem;
2216             std::optional<nlohmann::json> links;
2217             std::optional<std::string> datetime;
2218 
2219             if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem,
2220                                           "DateTime", datetime, "Links", links))
2221             {
2222                 return;
2223             }
2224 
2225             if (oem)
2226             {
2227                 std::optional<nlohmann::json> openbmc;
2228                 if (!redfish::json_util::readJson(*oem, asyncResp->res,
2229                                                   "OpenBmc", openbmc))
2230                 {
2231                     BMCWEB_LOG_ERROR
2232                         << "Illegal Property "
2233                         << oem->dump(2, ' ', true,
2234                                      nlohmann::json::error_handler_t::replace);
2235                     return;
2236                 }
2237                 if (openbmc)
2238                 {
2239                     std::optional<nlohmann::json> fan;
2240                     if (!redfish::json_util::readJson(*openbmc, asyncResp->res,
2241                                                       "Fan", fan))
2242                     {
2243                         BMCWEB_LOG_ERROR
2244                             << "Illegal Property "
2245                             << openbmc->dump(
2246                                    2, ' ', true,
2247                                    nlohmann::json::error_handler_t::replace);
2248                         return;
2249                     }
2250                     if (fan)
2251                     {
2252                         auto pid =
2253                             std::make_shared<SetPIDValues>(asyncResp, *fan);
2254                         pid->run();
2255                     }
2256                 }
2257             }
2258             if (links)
2259             {
2260                 std::optional<nlohmann::json> activeSoftwareImage;
2261                 if (!redfish::json_util::readJson(*links, asyncResp->res,
2262                                                   "ActiveSoftwareImage",
2263                                                   activeSoftwareImage))
2264                 {
2265                     return;
2266                 }
2267                 if (activeSoftwareImage)
2268                 {
2269                     std::optional<std::string> odataId;
2270                     if (!json_util::readJson(*activeSoftwareImage,
2271                                              asyncResp->res, "@odata.id",
2272                                              odataId))
2273                     {
2274                         return;
2275                     }
2276 
2277                     if (odataId)
2278                     {
2279                         setActiveFirmwareImage(asyncResp, *odataId);
2280                     }
2281                 }
2282             }
2283             if (datetime)
2284             {
2285                 setDateTime(asyncResp, std::move(*datetime));
2286             }
2287         });
2288 }
2289 
2290 inline void requestRoutesManagerCollection(App& app)
2291 {
2292     BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
2293         .privileges(redfish::privileges::getManagerCollection)
2294         .methods(boost::beast::http::verb::get)(
2295             [](const crow::Request&,
2296                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2297                 // Collections don't include the static data added by SubRoute
2298                 // because it has a duplicate entry for members
2299                 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
2300                 asyncResp->res.jsonValue["@odata.type"] =
2301                     "#ManagerCollection.ManagerCollection";
2302                 asyncResp->res.jsonValue["Name"] = "Manager Collection";
2303                 asyncResp->res.jsonValue["Members@odata.count"] = 1;
2304                 asyncResp->res.jsonValue["Members"] = {
2305                     {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
2306             });
2307 }
2308 } // namespace redfish
2309