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