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