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