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