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