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