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