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