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