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