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