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