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