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