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