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