xref: /openbmc/bmcweb/redfish-core/lib/openbmc/openbmc_managers.hpp (revision 5571acd2359e4420d749e14069e3eefe1f5b125b)
1 #pragma once
2 
3 #include "bmcweb_config.h"
4 
5 #include "async_resp.hpp"
6 #include "dbus_singleton.hpp"
7 #include "dbus_utility.hpp"
8 #include "error_messages.hpp"
9 #include "io_context_singleton.hpp"
10 #include "logging.hpp"
11 #include "redfish.hpp"
12 #include "sub_request.hpp"
13 #include "utils/dbus_utils.hpp"
14 #include "utils/json_utils.hpp"
15 #include "verb.hpp"
16 
17 #include <boost/asio/post.hpp>
18 #include <boost/beast/http/status.hpp>
19 #include <boost/container/flat_map.hpp>
20 #include <boost/container/flat_set.hpp>
21 #include <boost/url/format.hpp>
22 #include <boost/url/url.hpp>
23 #include <nlohmann/json.hpp>
24 #include <sdbusplus/asio/property.hpp>
25 #include <sdbusplus/message/native_types.hpp>
26 #include <sdbusplus/unpack_properties.hpp>
27 
28 #include <algorithm>
29 #include <array>
30 #include <cstddef>
31 #include <functional>
32 #include <map>
33 #include <memory>
34 #include <optional>
35 #include <string>
36 #include <string_view>
37 #include <utility>
38 #include <variant>
39 #include <vector>
40 
41 namespace redfish
42 {
43 
44 static constexpr const char* objectManagerIface =
45     "org.freedesktop.DBus.ObjectManager";
46 static constexpr const char* pidConfigurationIface =
47     "xyz.openbmc_project.Configuration.Pid";
48 static constexpr const char* pidZoneConfigurationIface =
49     "xyz.openbmc_project.Configuration.Pid.Zone";
50 static constexpr const char* stepwiseConfigurationIface =
51     "xyz.openbmc_project.Configuration.Stepwise";
52 static constexpr const char* thermalModeIface =
53     "xyz.openbmc_project.Control.ThermalMode";
54 
asyncPopulatePid(const std::string & connection,const std::string & path,const std::string & currentProfile,const std::vector<std::string> & supportedProfiles,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)55 inline void asyncPopulatePid(
56     const std::string& connection, const std::string& path,
57     const std::string& currentProfile,
58     const std::vector<std::string>& supportedProfiles,
59     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
60 {
61     sdbusplus::message::object_path objPath(path);
62     dbus::utility::getManagedObjects(
63         connection, objPath,
64         [asyncResp, currentProfile, supportedProfiles](
65             const boost::system::error_code& ec,
66             const dbus::utility::ManagedObjectType& managedObj) {
67             if (ec)
68             {
69                 BMCWEB_LOG_ERROR("{}", ec);
70                 messages::internalError(asyncResp->res);
71                 return;
72             }
73             nlohmann::json& configRoot = asyncResp->res.jsonValue["Fan"];
74             nlohmann::json& fans = configRoot["FanControllers"];
75             fans["@odata.type"] =
76                 "#OpenBMCManager.v1_0_0.Manager.FanControllers";
77             fans["@odata.id"] = boost::urls::format(
78                 "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanControllers",
79                 BMCWEB_REDFISH_MANAGER_URI_NAME);
80 
81             nlohmann::json& pids = configRoot["PidControllers"];
82             pids["@odata.type"] =
83                 "#OpenBMCManager.v1_0_0.Manager.PidControllers";
84             pids["@odata.id"] = boost::urls::format(
85                 "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/PidControllers",
86                 BMCWEB_REDFISH_MANAGER_URI_NAME);
87 
88             nlohmann::json& stepwise = configRoot["StepwiseControllers"];
89             stepwise["@odata.type"] =
90                 "#OpenBMCManager.v1_0_0.Manager.StepwiseControllers";
91             stepwise["@odata.id"] = boost::urls::format(
92                 "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/StepwiseControllers",
93                 BMCWEB_REDFISH_MANAGER_URI_NAME);
94 
95             nlohmann::json& zones = configRoot["FanZones"];
96             zones["@odata.id"] = boost::urls::format(
97                 "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanZones",
98                 BMCWEB_REDFISH_MANAGER_URI_NAME);
99             zones["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager.FanZones";
100             configRoot["@odata.id"] =
101                 boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan",
102                                     BMCWEB_REDFISH_MANAGER_URI_NAME);
103             configRoot["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager.Fan";
104             configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles;
105 
106             if (!currentProfile.empty())
107             {
108                 configRoot["Profile"] = currentProfile;
109             }
110             BMCWEB_LOG_DEBUG("profile = {} !", currentProfile);
111 
112             for (const auto& pathPair : managedObj)
113             {
114                 for (const auto& intfPair : pathPair.second)
115                 {
116                     if (intfPair.first != pidConfigurationIface &&
117                         intfPair.first != pidZoneConfigurationIface &&
118                         intfPair.first != stepwiseConfigurationIface)
119                     {
120                         continue;
121                     }
122 
123                     std::string name;
124 
125                     for (const std::pair<std::string,
126                                          dbus::utility::DbusVariantType>&
127                              propPair : intfPair.second)
128                     {
129                         if (propPair.first == "Name")
130                         {
131                             const std::string* namePtr =
132                                 std::get_if<std::string>(&propPair.second);
133                             if (namePtr == nullptr)
134                             {
135                                 BMCWEB_LOG_ERROR("Pid Name Field illegal");
136                                 messages::internalError(asyncResp->res);
137                                 return;
138                             }
139                             name = *namePtr;
140                             dbus::utility::escapePathForDbus(name);
141                         }
142                         else if (propPair.first == "Profiles")
143                         {
144                             const std::vector<std::string>* profiles =
145                                 std::get_if<std::vector<std::string>>(
146                                     &propPair.second);
147                             if (profiles == nullptr)
148                             {
149                                 BMCWEB_LOG_ERROR("Pid Profiles Field illegal");
150                                 messages::internalError(asyncResp->res);
151                                 return;
152                             }
153                             if (std::find(profiles->begin(), profiles->end(),
154                                           currentProfile) == profiles->end())
155                             {
156                                 BMCWEB_LOG_INFO(
157                                     "{} not supported in current profile",
158                                     name);
159                                 continue;
160                             }
161                         }
162                     }
163                     nlohmann::json* config = nullptr;
164                     const std::string* classPtr = nullptr;
165 
166                     for (const std::pair<std::string,
167                                          dbus::utility::DbusVariantType>&
168                              propPair : intfPair.second)
169                     {
170                         if (propPair.first == "Class")
171                         {
172                             classPtr =
173                                 std::get_if<std::string>(&propPair.second);
174                         }
175                     }
176 
177                     boost::urls::url url(
178                         boost::urls::format("/redfish/v1/Managers/{}",
179                                             BMCWEB_REDFISH_MANAGER_URI_NAME));
180                     if (intfPair.first == pidZoneConfigurationIface)
181                     {
182                         std::string chassis;
183                         if (!dbus::utility::getNthStringFromPath(
184                                 pathPair.first.str, 5, chassis))
185                         {
186                             chassis = "#IllegalValue";
187                         }
188                         nlohmann::json& zone = zones[name];
189                         zone["Chassis"]["@odata.id"] = boost::urls::format(
190                             "/redfish/v1/Chassis/{}", chassis);
191                         url.set_fragment(
192                             ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name)
193                                 .to_string());
194                         zone["@odata.id"] = std::move(url);
195                         zone["@odata.type"] =
196                             "#OpenBMCManager.v1_0_0.Manager.FanZone";
197                         config = &zone;
198                     }
199 
200                     else if (intfPair.first == stepwiseConfigurationIface)
201                     {
202                         if (classPtr == nullptr)
203                         {
204                             BMCWEB_LOG_ERROR("Pid Class Field illegal");
205                             messages::internalError(asyncResp->res);
206                             return;
207                         }
208 
209                         nlohmann::json& controller = stepwise[name];
210                         config = &controller;
211                         url.set_fragment(
212                             ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer /
213                              name)
214                                 .to_string());
215                         controller["@odata.id"] = std::move(url);
216                         controller["@odata.type"] =
217                             "#OpenBMCManager.v1_0_0.Manager.StepwiseController";
218 
219                         controller["Direction"] = *classPtr;
220                     }
221 
222                     // pid and fans are off the same configuration
223                     else if (intfPair.first == pidConfigurationIface)
224                     {
225                         if (classPtr == nullptr)
226                         {
227                             BMCWEB_LOG_ERROR("Pid Class Field illegal");
228                             messages::internalError(asyncResp->res);
229                             return;
230                         }
231                         bool isFan = *classPtr == "fan";
232                         nlohmann::json& element =
233                             isFan ? fans[name] : pids[name];
234                         config = &element;
235                         if (isFan)
236                         {
237                             url.set_fragment(
238                                 ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer /
239                                  name)
240                                     .to_string());
241                             element["@odata.id"] = std::move(url);
242                             element["@odata.type"] =
243                                 "#OpenBMCManager.v1_0_0.Manager.FanController";
244                         }
245                         else
246                         {
247                             url.set_fragment(
248                                 ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer /
249                                  name)
250                                     .to_string());
251                             element["@odata.id"] = std::move(url);
252                             element["@odata.type"] =
253                                 "#OpenBMCManager.v1_0_0.Manager.PidController";
254                         }
255                     }
256                     else
257                     {
258                         BMCWEB_LOG_ERROR("Unexpected configuration");
259                         messages::internalError(asyncResp->res);
260                         return;
261                     }
262 
263                     // used for making maps out of 2 vectors
264                     const std::vector<double>* keys = nullptr;
265                     const std::vector<double>* values = nullptr;
266 
267                     for (const auto& propertyPair : intfPair.second)
268                     {
269                         if (propertyPair.first == "Type" ||
270                             propertyPair.first == "Class" ||
271                             propertyPair.first == "Name" ||
272                             propertyPair.first == "AccumulateSetPoint")
273                         {
274                             continue;
275                         }
276 
277                         // zones
278                         if (intfPair.first == pidZoneConfigurationIface)
279                         {
280                             const double* ptr =
281                                 std::get_if<double>(&propertyPair.second);
282                             if (ptr == nullptr)
283                             {
284                                 BMCWEB_LOG_ERROR("Field Illegal {}",
285                                                  propertyPair.first);
286                                 messages::internalError(asyncResp->res);
287                                 return;
288                             }
289                             (*config)[propertyPair.first] = *ptr;
290                         }
291 
292                         if (intfPair.first == stepwiseConfigurationIface)
293                         {
294                             if (propertyPair.first == "Reading" ||
295                                 propertyPair.first == "Output")
296                             {
297                                 const std::vector<double>* ptr =
298                                     std::get_if<std::vector<double>>(
299                                         &propertyPair.second);
300 
301                                 if (ptr == nullptr)
302                                 {
303                                     BMCWEB_LOG_ERROR("Field Illegal {}",
304                                                      propertyPair.first);
305                                     messages::internalError(asyncResp->res);
306                                     return;
307                                 }
308 
309                                 if (propertyPair.first == "Reading")
310                                 {
311                                     keys = ptr;
312                                 }
313                                 else
314                                 {
315                                     values = ptr;
316                                 }
317                                 if (keys != nullptr && values != nullptr)
318                                 {
319                                     if (keys->size() != values->size())
320                                     {
321                                         BMCWEB_LOG_ERROR(
322                                             "Reading and Output size don't match ");
323                                         messages::internalError(asyncResp->res);
324                                         return;
325                                     }
326                                     nlohmann::json& steps = (*config)["Steps"];
327                                     steps = nlohmann::json::array();
328                                     for (size_t ii = 0; ii < keys->size(); ii++)
329                                     {
330                                         nlohmann::json::object_t step;
331                                         step["Target"] = (*keys)[ii];
332                                         step["Output"] = (*values)[ii];
333                                         steps.emplace_back(std::move(step));
334                                     }
335                                 }
336                             }
337                             if (propertyPair.first == "NegativeHysteresis" ||
338                                 propertyPair.first == "PositiveHysteresis")
339                             {
340                                 const double* ptr =
341                                     std::get_if<double>(&propertyPair.second);
342                                 if (ptr == nullptr)
343                                 {
344                                     BMCWEB_LOG_ERROR("Field Illegal {}",
345                                                      propertyPair.first);
346                                     messages::internalError(asyncResp->res);
347                                     return;
348                                 }
349                                 (*config)[propertyPair.first] = *ptr;
350                             }
351                         }
352 
353                         // pid and fans are off the same configuration
354                         if (intfPair.first == pidConfigurationIface ||
355                             intfPair.first == stepwiseConfigurationIface)
356                         {
357                             if (propertyPair.first == "Zones")
358                             {
359                                 const std::vector<std::string>* inputs =
360                                     std::get_if<std::vector<std::string>>(
361                                         &propertyPair.second);
362 
363                                 if (inputs == nullptr)
364                                 {
365                                     BMCWEB_LOG_ERROR("Zones Pid Field Illegal");
366                                     messages::internalError(asyncResp->res);
367                                     return;
368                                 }
369                                 auto& data = (*config)[propertyPair.first];
370                                 data = nlohmann::json::array();
371                                 for (std::string itemCopy : *inputs)
372                                 {
373                                     dbus::utility::escapePathForDbus(itemCopy);
374                                     nlohmann::json::object_t input;
375                                     boost::urls::url managerUrl =
376                                         boost::urls::format(
377                                             "/redfish/v1/Managers/{}#{}",
378                                             BMCWEB_REDFISH_MANAGER_URI_NAME,
379                                             ("/Oem/OpenBmc/Fan/FanZones"_json_pointer /
380                                              itemCopy)
381                                                 .to_string());
382                                     input["@odata.id"] = std::move(managerUrl);
383                                     data.emplace_back(std::move(input));
384                                 }
385                             }
386                             // todo(james): may never happen, but this
387                             // assumes configuration data referenced in the
388                             // PID config is provided by the same daemon, we
389                             // could add another loop to cover all cases,
390                             // but I'm okay kicking this can down the road a
391                             // bit
392 
393                             else if (propertyPair.first == "Inputs" ||
394                                      propertyPair.first == "Outputs")
395                             {
396                                 auto& data = (*config)[propertyPair.first];
397                                 const std::vector<std::string>* inputs =
398                                     std::get_if<std::vector<std::string>>(
399                                         &propertyPair.second);
400 
401                                 if (inputs == nullptr)
402                                 {
403                                     BMCWEB_LOG_ERROR("Field Illegal {}",
404                                                      propertyPair.first);
405                                     messages::internalError(asyncResp->res);
406                                     return;
407                                 }
408                                 data = *inputs;
409                             }
410                             else if (propertyPair.first == "SetPointOffset")
411                             {
412                                 const std::string* ptr =
413                                     std::get_if<std::string>(
414                                         &propertyPair.second);
415 
416                                 if (ptr == nullptr)
417                                 {
418                                     BMCWEB_LOG_ERROR("Field Illegal {}",
419                                                      propertyPair.first);
420                                     messages::internalError(asyncResp->res);
421                                     return;
422                                 }
423                                 // translate from dbus to redfish
424                                 if (*ptr == "WarningHigh")
425                                 {
426                                     (*config)["SetPointOffset"] =
427                                         "UpperThresholdNonCritical";
428                                 }
429                                 else if (*ptr == "WarningLow")
430                                 {
431                                     (*config)["SetPointOffset"] =
432                                         "LowerThresholdNonCritical";
433                                 }
434                                 else if (*ptr == "CriticalHigh")
435                                 {
436                                     (*config)["SetPointOffset"] =
437                                         "UpperThresholdCritical";
438                                 }
439                                 else if (*ptr == "CriticalLow")
440                                 {
441                                     (*config)["SetPointOffset"] =
442                                         "LowerThresholdCritical";
443                                 }
444                                 else
445                                 {
446                                     BMCWEB_LOG_ERROR("Value Illegal {}", *ptr);
447                                     messages::internalError(asyncResp->res);
448                                     return;
449                                 }
450                             }
451                             // doubles
452                             else if (propertyPair.first ==
453                                          "FFGainCoefficient" ||
454                                      propertyPair.first == "FFOffCoefficient" ||
455                                      propertyPair.first == "ICoefficient" ||
456                                      propertyPair.first == "ILimitMax" ||
457                                      propertyPair.first == "ILimitMin" ||
458                                      propertyPair.first ==
459                                          "PositiveHysteresis" ||
460                                      propertyPair.first ==
461                                          "NegativeHysteresis" ||
462                                      propertyPair.first == "OutLimitMax" ||
463                                      propertyPair.first == "OutLimitMin" ||
464                                      propertyPair.first == "PCoefficient" ||
465                                      propertyPair.first == "SetPoint" ||
466                                      propertyPair.first == "SlewNeg" ||
467                                      propertyPair.first == "SlewPos")
468                             {
469                                 const double* ptr =
470                                     std::get_if<double>(&propertyPair.second);
471                                 if (ptr == nullptr)
472                                 {
473                                     BMCWEB_LOG_ERROR("Field Illegal {}",
474                                                      propertyPair.first);
475                                     messages::internalError(asyncResp->res);
476                                     return;
477                                 }
478                                 (*config)[propertyPair.first] = *ptr;
479                             }
480                         }
481                     }
482                 }
483             }
484         });
485 }
486 
487 enum class CreatePIDRet
488 {
489     fail,
490     del,
491     patch
492 };
493 
findChassis(const dbus::utility::ManagedObjectType & managedObj,std::string_view value,std::string & chassis)494 inline const dbus::utility::ManagedObjectType::value_type* findChassis(
495     const dbus::utility::ManagedObjectType& managedObj, std::string_view value,
496     std::string& chassis)
497 {
498     BMCWEB_LOG_DEBUG("Find Chassis: {}", value);
499 
500     std::string escaped(value);
501     std::replace(escaped.begin(), escaped.end(), ' ', '_');
502     escaped = "/" + escaped;
503     auto it = std::ranges::find_if(managedObj, [&escaped](const auto& obj) {
504         if (obj.first.str.ends_with(escaped))
505         {
506             BMCWEB_LOG_DEBUG("Matched {}", obj.first.str);
507             return true;
508         }
509         return false;
510     });
511 
512     if (it == managedObj.end())
513     {
514         return nullptr;
515     }
516     // 5 comes from <chassis-name> being the 5th element
517     // /xyz/openbmc_project/inventory/system/chassis/<chassis-name>
518     if (dbus::utility::getNthStringFromPath(it->first.str, 5, chassis))
519     {
520         return &(*it);
521     }
522 
523     return nullptr;
524 }
525 
getZonesFromJsonReq(const std::shared_ptr<bmcweb::AsyncResp> & response,std::vector<nlohmann::json::object_t> & config,std::vector<std::string> & zones)526 inline bool getZonesFromJsonReq(
527     const std::shared_ptr<bmcweb::AsyncResp>& response,
528     std::vector<nlohmann::json::object_t>& config,
529     std::vector<std::string>& zones)
530 {
531     if (config.empty())
532     {
533         BMCWEB_LOG_ERROR("Empty Zones");
534         messages::propertyValueFormatError(response->res, config, "Zones");
535         return false;
536     }
537     for (auto& odata : config)
538     {
539         std::string path;
540         if (!redfish::json_util::readJsonObject(odata, response->res,
541                                                 "@odata.id", path))
542         {
543             return false;
544         }
545         std::string input;
546 
547         // 8 below comes from
548         // /redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones/Left
549         //     0    1     2      3    4    5      6     7      8
550         if (!dbus::utility::getNthStringFromPath(path, 8, input))
551         {
552             BMCWEB_LOG_ERROR("Got invalid path {}", path);
553             BMCWEB_LOG_ERROR("Illegal Type Zones");
554             messages::propertyValueFormatError(response->res, odata, "Zones");
555             return false;
556         }
557         std::replace(input.begin(), input.end(), '_', ' ');
558         zones.emplace_back(std::move(input));
559     }
560     return true;
561 }
562 
createPidInterface(const std::shared_ptr<bmcweb::AsyncResp> & response,const std::string & type,std::string_view name,nlohmann::json & jsonValue,const std::string & path,const dbus::utility::ManagedObjectType & managedObj,bool createNewObject,dbus::utility::DBusPropertiesMap & output,std::string & chassis,const std::string & profile)563 inline CreatePIDRet createPidInterface(
564     const std::shared_ptr<bmcweb::AsyncResp>& response, const std::string& type,
565     std::string_view name, nlohmann::json& jsonValue, const std::string& path,
566     const dbus::utility::ManagedObjectType& managedObj, bool createNewObject,
567     dbus::utility::DBusPropertiesMap& output, std::string& chassis,
568     const std::string& profile)
569 {
570     // common deleter
571     if (jsonValue == nullptr)
572     {
573         std::string iface;
574         if (type == "PidControllers" || type == "FanControllers")
575         {
576             iface = pidConfigurationIface;
577         }
578         else if (type == "FanZones")
579         {
580             iface = pidZoneConfigurationIface;
581         }
582         else if (type == "StepwiseControllers")
583         {
584             iface = stepwiseConfigurationIface;
585         }
586         else
587         {
588             BMCWEB_LOG_ERROR("Illegal Type {}", type);
589             messages::propertyUnknown(response->res, type);
590             return CreatePIDRet::fail;
591         }
592 
593         BMCWEB_LOG_DEBUG("del {} {}", path, iface);
594         // delete interface
595         dbus::utility::async_method_call(
596             response,
597             [response, path](const boost::system::error_code& ec) {
598                 if (ec)
599                 {
600                     BMCWEB_LOG_ERROR("Error patching {}: {}", path, ec);
601                     messages::internalError(response->res);
602                     return;
603                 }
604                 messages::success(response->res);
605             },
606             "xyz.openbmc_project.EntityManager", path, iface, "Delete");
607         return CreatePIDRet::del;
608     }
609 
610     const dbus::utility::ManagedObjectType::value_type* managedItem = nullptr;
611     if (!createNewObject)
612     {
613         // if we aren't creating a new object, we should be able to find it on
614         // d-bus
615         managedItem = findChassis(managedObj, name, chassis);
616         if (managedItem == nullptr)
617         {
618             BMCWEB_LOG_ERROR("Failed to get chassis from config patch");
619             messages::invalidObject(
620                 response->res,
621                 boost::urls::format("/redfish/v1/Chassis/{}", chassis));
622             return CreatePIDRet::fail;
623         }
624     }
625 
626     if (!profile.empty() &&
627         (type == "PidControllers" || type == "FanControllers" ||
628          type == "StepwiseControllers"))
629     {
630         if (managedItem == nullptr)
631         {
632             output.emplace_back("Profiles", std::vector<std::string>{profile});
633         }
634         else
635         {
636             std::string interface;
637             if (type == "StepwiseControllers")
638             {
639                 interface = stepwiseConfigurationIface;
640             }
641             else
642             {
643                 interface = pidConfigurationIface;
644             }
645             bool ifaceFound = false;
646             for (const auto& iface : managedItem->second)
647             {
648                 if (iface.first == interface)
649                 {
650                     ifaceFound = true;
651                     for (const auto& prop : iface.second)
652                     {
653                         if (prop.first == "Profiles")
654                         {
655                             const std::vector<std::string>* curProfiles =
656                                 std::get_if<std::vector<std::string>>(
657                                     &(prop.second));
658                             if (curProfiles == nullptr)
659                             {
660                                 BMCWEB_LOG_ERROR(
661                                     "Illegal profiles in managed object");
662                                 messages::internalError(response->res);
663                                 return CreatePIDRet::fail;
664                             }
665                             if (std::find(curProfiles->begin(),
666                                           curProfiles->end(), profile) ==
667                                 curProfiles->end())
668                             {
669                                 std::vector<std::string> newProfiles =
670                                     *curProfiles;
671                                 newProfiles.push_back(profile);
672                                 output.emplace_back("Profiles", newProfiles);
673                             }
674                         }
675                     }
676                 }
677             }
678 
679             if (!ifaceFound)
680             {
681                 BMCWEB_LOG_ERROR("Failed to find interface in managed object");
682                 messages::internalError(response->res);
683                 return CreatePIDRet::fail;
684             }
685         }
686     }
687 
688     if (type == "PidControllers" || type == "FanControllers")
689     {
690         if (createNewObject)
691         {
692             output.emplace_back("Class",
693                                 type == "PidControllers" ? "temp" : "fan");
694             output.emplace_back("Type", "Pid");
695         }
696 
697         std::optional<std::vector<nlohmann::json::object_t>> zones;
698         std::optional<std::vector<std::string>> inputs;
699         std::optional<std::vector<std::string>> outputs;
700         std::map<std::string, std::optional<double>> doubles;
701         std::optional<std::string> setpointOffset;
702         if (!redfish::json_util::readJson(                           //
703                 jsonValue, response->res,                            //
704                 "FFGainCoefficient", doubles["FFGainCoefficient"],   //
705                 "FFOffCoefficient", doubles["FFOffCoefficient"],     //
706                 "ICoefficient", doubles["ICoefficient"],             //
707                 "ILimitMax", doubles["ILimitMax"],                   //
708                 "ILimitMin", doubles["ILimitMin"],                   //
709                 "Inputs", inputs,                                    //
710                 "NegativeHysteresis", doubles["NegativeHysteresis"], //
711                 "OutLimitMax", doubles["OutLimitMax"],               //
712                 "OutLimitMin", doubles["OutLimitMin"],               //
713                 "Outputs", outputs,                                  //
714                 "PCoefficient", doubles["PCoefficient"],             //
715                 "PositiveHysteresis", doubles["PositiveHysteresis"], //
716                 "SetPoint", doubles["SetPoint"],                     //
717                 "SetPointOffset", setpointOffset,                    //
718                 "SlewNeg", doubles["SlewNeg"],                       //
719                 "SlewPos", doubles["SlewPos"],                       //
720                 "Zones", zones                                       //
721                 ))
722         {
723             return CreatePIDRet::fail;
724         }
725 
726         if (zones)
727         {
728             std::vector<std::string> zonesStr;
729             if (!getZonesFromJsonReq(response, *zones, zonesStr))
730             {
731                 BMCWEB_LOG_ERROR("Illegal Zones");
732                 return CreatePIDRet::fail;
733             }
734             if (chassis.empty() &&
735                 findChassis(managedObj, zonesStr[0], chassis) == nullptr)
736             {
737                 BMCWEB_LOG_ERROR("Failed to get chassis from config patch");
738                 messages::invalidObject(
739                     response->res,
740                     boost::urls::format("/redfish/v1/Chassis/{}", chassis));
741                 return CreatePIDRet::fail;
742             }
743             output.emplace_back("Zones", std::move(zonesStr));
744         }
745 
746         if (inputs)
747         {
748             for (std::string& value : *inputs)
749             {
750                 std::replace(value.begin(), value.end(), '_', ' ');
751             }
752             output.emplace_back("Inputs", *inputs);
753         }
754 
755         if (outputs)
756         {
757             for (std::string& value : *outputs)
758             {
759                 std::replace(value.begin(), value.end(), '_', ' ');
760             }
761             output.emplace_back("Outputs", *outputs);
762         }
763 
764         if (setpointOffset)
765         {
766             // translate between redfish and dbus names
767             if (*setpointOffset == "UpperThresholdNonCritical")
768             {
769                 output.emplace_back("SetPointOffset", "WarningLow");
770             }
771             else if (*setpointOffset == "LowerThresholdNonCritical")
772             {
773                 output.emplace_back("SetPointOffset", "WarningHigh");
774             }
775             else if (*setpointOffset == "LowerThresholdCritical")
776             {
777                 output.emplace_back("SetPointOffset", "CriticalLow");
778             }
779             else if (*setpointOffset == "UpperThresholdCritical")
780             {
781                 output.emplace_back("SetPointOffset", "CriticalHigh");
782             }
783             else
784             {
785                 BMCWEB_LOG_ERROR("Invalid setpointoffset {}", *setpointOffset);
786                 messages::propertyValueNotInList(response->res, name,
787                                                  "SetPointOffset");
788                 return CreatePIDRet::fail;
789             }
790         }
791 
792         // doubles
793         for (const auto& pairs : doubles)
794         {
795             if (!pairs.second)
796             {
797                 continue;
798             }
799             BMCWEB_LOG_DEBUG("{} = {}", pairs.first, *pairs.second);
800             output.emplace_back(pairs.first, *pairs.second);
801         }
802     }
803 
804     else if (type == "FanZones")
805     {
806         output.emplace_back("Type", "Pid.Zone");
807 
808         std::optional<std::string> chassisId;
809         std::optional<double> failSafePercent;
810         std::optional<double> minThermalOutput;
811         if (!redfish::json_util::readJson(          //
812                 jsonValue, response->res,           //
813                 "Chassis/@odata.id", chassisId,     //
814                 "FailSafePercent", failSafePercent, //
815                 "MinThermalOutput", minThermalOutput))
816         {
817             return CreatePIDRet::fail;
818         }
819 
820         if (chassisId)
821         {
822             // /redfish/v1/chassis/chassis_name/
823             if (!dbus::utility::getNthStringFromPath(*chassisId, 3, chassis))
824             {
825                 BMCWEB_LOG_ERROR("Got invalid path {}", *chassisId);
826                 messages::invalidObject(
827                     response->res,
828                     boost::urls::format("/redfish/v1/Chassis/{}", *chassisId));
829                 return CreatePIDRet::fail;
830             }
831         }
832         if (minThermalOutput)
833         {
834             output.emplace_back("MinThermalOutput", *minThermalOutput);
835         }
836         if (failSafePercent)
837         {
838             output.emplace_back("FailSafePercent", *failSafePercent);
839         }
840     }
841     else if (type == "StepwiseControllers")
842     {
843         output.emplace_back("Type", "Stepwise");
844 
845         std::optional<std::vector<nlohmann::json::object_t>> zones;
846         std::optional<std::vector<nlohmann::json::object_t>> steps;
847         std::optional<std::vector<std::string>> inputs;
848         std::optional<double> positiveHysteresis;
849         std::optional<double> negativeHysteresis;
850         std::optional<std::string> direction; // upper clipping curve vs lower
851         if (!redfish::json_util::readJson(    //
852                 jsonValue, response->res,     //
853                 "Direction", direction,       //
854                 "Inputs", inputs,             //
855                 "NegativeHysteresis", negativeHysteresis, //
856                 "PositiveHysteresis", positiveHysteresis, //
857                 "Steps", steps,                           //
858                 "Zones", zones                            //
859                 ))
860         {
861             return CreatePIDRet::fail;
862         }
863 
864         if (zones)
865         {
866             std::vector<std::string> zonesStrs;
867             if (!getZonesFromJsonReq(response, *zones, zonesStrs))
868             {
869                 BMCWEB_LOG_ERROR("Illegal Zones");
870                 return CreatePIDRet::fail;
871             }
872             if (chassis.empty() &&
873                 findChassis(managedObj, zonesStrs[0], chassis) == nullptr)
874             {
875                 BMCWEB_LOG_ERROR("Failed to get chassis from config patch");
876                 messages::invalidObject(
877                     response->res,
878                     boost::urls::format("/redfish/v1/Chassis/{}", chassis));
879                 return CreatePIDRet::fail;
880             }
881             output.emplace_back("Zones", std::move(zonesStrs));
882         }
883         if (steps)
884         {
885             std::vector<double> readings;
886             std::vector<double> outputs;
887             for (auto& step : *steps)
888             {
889                 double target = 0.0;
890                 double out = 0.0;
891 
892                 if (!redfish::json_util::readJsonObject( //
893                         step, response->res,             //
894                         "Output", out,                   //
895                         "Target", target                 //
896                         ))
897                 {
898                     return CreatePIDRet::fail;
899                 }
900                 readings.emplace_back(target);
901                 outputs.emplace_back(out);
902             }
903             output.emplace_back("Reading", std::move(readings));
904             output.emplace_back("Output", std::move(outputs));
905         }
906         if (inputs)
907         {
908             for (std::string& value : *inputs)
909             {
910                 std::replace(value.begin(), value.end(), '_', ' ');
911             }
912             output.emplace_back("Inputs", std::move(*inputs));
913         }
914         if (negativeHysteresis)
915         {
916             output.emplace_back("NegativeHysteresis", *negativeHysteresis);
917         }
918         if (positiveHysteresis)
919         {
920             output.emplace_back("PositiveHysteresis", *positiveHysteresis);
921         }
922         if (direction)
923         {
924             constexpr const std::array<const char*, 2> allowedDirections = {
925                 "Ceiling", "Floor"};
926             if (std::ranges::find(allowedDirections, *direction) ==
927                 allowedDirections.end())
928             {
929                 messages::propertyValueTypeError(response->res, "Direction",
930                                                  *direction);
931                 return CreatePIDRet::fail;
932             }
933             output.emplace_back("Class", *direction);
934         }
935     }
936     else
937     {
938         BMCWEB_LOG_ERROR("Illegal Type {}", type);
939         messages::propertyUnknown(response->res, type);
940         return CreatePIDRet::fail;
941     }
942     return CreatePIDRet::patch;
943 }
944 
945 struct GetPIDValues : std::enable_shared_from_this<GetPIDValues>
946 {
947     struct CompletionValues
948     {
949         std::vector<std::string> supportedProfiles;
950         std::string currentProfile;
951         dbus::utility::MapperGetSubTreeResponse subtree;
952     };
953 
GetPIDValuesredfish::GetPIDValues954     explicit GetPIDValues(
955         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
956         asyncResp(asyncRespIn)
957 
958     {}
959 
runredfish::GetPIDValues960     void run()
961     {
962         std::shared_ptr<GetPIDValues> self = shared_from_this();
963 
964         // get all configurations
965         constexpr std::array<std::string_view, 4> interfaces = {
966             pidConfigurationIface, pidZoneConfigurationIface,
967             objectManagerIface, stepwiseConfigurationIface};
968         dbus::utility::getSubTree(
969             "/", 0, interfaces,
970             [self](
971                 const boost::system::error_code& ec,
972                 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) {
973                 if (ec)
974                 {
975                     BMCWEB_LOG_ERROR("{}", ec);
976                     messages::internalError(self->asyncResp->res);
977                     return;
978                 }
979                 self->complete.subtree = subtreeLocal;
980             });
981 
982         // at the same time get the selected profile
983         constexpr std::array<std::string_view, 1> thermalModeIfaces = {
984             thermalModeIface};
985         dbus::utility::getSubTree(
986             "/", 0, thermalModeIfaces,
987             [self](
988                 const boost::system::error_code& ec,
989                 const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) {
990                 if (ec || subtreeLocal.empty())
991                 {
992                     return;
993                 }
994                 if (subtreeLocal[0].second.size() != 1)
995                 {
996                     // invalid mapper response, should never happen
997                     BMCWEB_LOG_ERROR("GetPIDValues: Mapper Error");
998                     messages::internalError(self->asyncResp->res);
999                     return;
1000                 }
1001 
1002                 const std::string& path = subtreeLocal[0].first;
1003                 const std::string& owner = subtreeLocal[0].second[0].first;
1004 
1005                 dbus::utility::getAllProperties(
1006                     *crow::connections::systemBus, owner, path,
1007                     thermalModeIface,
1008                     [path, owner,
1009                      self](const boost::system::error_code& ec2,
1010                            const dbus::utility::DBusPropertiesMap& resp) {
1011                         if (ec2)
1012                         {
1013                             BMCWEB_LOG_ERROR(
1014                                 "GetPIDValues: Can't get thermalModeIface {}",
1015                                 path);
1016                             messages::internalError(self->asyncResp->res);
1017                             return;
1018                         }
1019 
1020                         const std::string* current = nullptr;
1021                         const std::vector<std::string>* supported = nullptr;
1022 
1023                         const bool success = sdbusplus::unpackPropertiesNoThrow(
1024                             dbus_utils::UnpackErrorPrinter(), resp, "Current",
1025                             current, "Supported", supported);
1026 
1027                         if (!success)
1028                         {
1029                             messages::internalError(self->asyncResp->res);
1030                             return;
1031                         }
1032 
1033                         if (current == nullptr || supported == nullptr)
1034                         {
1035                             BMCWEB_LOG_ERROR(
1036                                 "GetPIDValues: thermal mode iface invalid {}",
1037                                 path);
1038                             messages::internalError(self->asyncResp->res);
1039                             return;
1040                         }
1041                         self->complete.currentProfile = *current;
1042                         self->complete.supportedProfiles = *supported;
1043                     });
1044             });
1045     }
1046 
processingCompleteredfish::GetPIDValues1047     static void processingComplete(
1048         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1049         const CompletionValues& completion)
1050     {
1051         if (asyncResp->res.result() != boost::beast::http::status::ok)
1052         {
1053             return;
1054         }
1055         // create map of <connection, path to objMgr>>
1056         boost::container::flat_map<
1057             std::string, std::string, std::less<>,
1058             std::vector<std::pair<std::string, std::string>>>
1059             objectMgrPaths;
1060         boost::container::flat_set<std::string, std::less<>,
1061                                    std::vector<std::string>>
1062             calledConnections;
1063         for (const auto& pathGroup : completion.subtree)
1064         {
1065             for (const auto& connectionGroup : pathGroup.second)
1066             {
1067                 auto findConnection =
1068                     calledConnections.find(connectionGroup.first);
1069                 if (findConnection != calledConnections.end())
1070                 {
1071                     break;
1072                 }
1073                 for (const std::string& interface : connectionGroup.second)
1074                 {
1075                     if (interface == objectManagerIface)
1076                     {
1077                         objectMgrPaths[connectionGroup.first] = pathGroup.first;
1078                     }
1079                     // this list is alphabetical, so we
1080                     // should have found the objMgr by now
1081                     if (interface == pidConfigurationIface ||
1082                         interface == pidZoneConfigurationIface ||
1083                         interface == stepwiseConfigurationIface)
1084                     {
1085                         auto findObjMgr =
1086                             objectMgrPaths.find(connectionGroup.first);
1087                         if (findObjMgr == objectMgrPaths.end())
1088                         {
1089                             BMCWEB_LOG_DEBUG("{}Has no Object Manager",
1090                                              connectionGroup.first);
1091                             continue;
1092                         }
1093 
1094                         calledConnections.insert(connectionGroup.first);
1095 
1096                         asyncPopulatePid(findObjMgr->first, findObjMgr->second,
1097                                          completion.currentProfile,
1098                                          completion.supportedProfiles,
1099                                          asyncResp);
1100                         break;
1101                     }
1102                 }
1103             }
1104         }
1105     }
1106 
~GetPIDValuesredfish::GetPIDValues1107     ~GetPIDValues()
1108     {
1109         boost::asio::post(getIoContext(),
1110                           std::bind_front(&processingComplete, asyncResp,
1111                                           std::move(complete)));
1112     }
1113 
1114     GetPIDValues(const GetPIDValues&) = delete;
1115     GetPIDValues(GetPIDValues&&) = delete;
1116     GetPIDValues& operator=(const GetPIDValues&) = delete;
1117     GetPIDValues& operator=(GetPIDValues&&) = delete;
1118 
1119     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1120     CompletionValues complete;
1121 };
1122 
1123 struct SetPIDValues : std::enable_shared_from_this<SetPIDValues>
1124 {
SetPIDValuesredfish::SetPIDValues1125     SetPIDValues(
1126         const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
1127         std::vector<
1128             std::pair<std::string, std::optional<nlohmann::json::object_t>>>&&
1129             configurationsIn,
1130         std::optional<std::string>& profileIn) :
1131         asyncResp(asyncRespIn), configuration(std::move(configurationsIn)),
1132         profile(std::move(profileIn))
1133     {}
1134 
1135     SetPIDValues(const SetPIDValues&) = delete;
1136     SetPIDValues(SetPIDValues&&) = delete;
1137     SetPIDValues& operator=(const SetPIDValues&) = delete;
1138     SetPIDValues& operator=(SetPIDValues&&) = delete;
1139 
runredfish::SetPIDValues1140     void run()
1141     {
1142         if (asyncResp->res.result() != boost::beast::http::status::ok)
1143         {
1144             return;
1145         }
1146 
1147         std::shared_ptr<SetPIDValues> self = shared_from_this();
1148 
1149         // todo(james): might make sense to do a mapper call here if this
1150         // interface gets more traction
1151         sdbusplus::message::object_path objPath(
1152             "/xyz/openbmc_project/inventory");
1153         dbus::utility::getManagedObjects(
1154             "xyz.openbmc_project.EntityManager", objPath,
1155             [self](const boost::system::error_code& ec,
1156                    const dbus::utility::ManagedObjectType& mObj) {
1157                 if (ec)
1158                 {
1159                     BMCWEB_LOG_ERROR("Error communicating to Entity Manager");
1160                     messages::internalError(self->asyncResp->res);
1161                     return;
1162                 }
1163                 const std::array<const char*, 3> configurations = {
1164                     pidConfigurationIface, pidZoneConfigurationIface,
1165                     stepwiseConfigurationIface};
1166 
1167                 for (const auto& [path, object] : mObj)
1168                 {
1169                     for (const auto& [interface, _] : object)
1170                     {
1171                         if (std::ranges::find(configurations, interface) !=
1172                             configurations.end())
1173                         {
1174                             self->objectCount++;
1175                             break;
1176                         }
1177                     }
1178                 }
1179                 self->managedObj = mObj;
1180             });
1181 
1182         // at the same time get the profile information
1183         constexpr std::array<std::string_view, 1> thermalModeIfaces = {
1184             thermalModeIface};
1185         dbus::utility::getSubTree(
1186             "/", 0, thermalModeIfaces,
1187             [self](const boost::system::error_code& ec,
1188                    const dbus::utility::MapperGetSubTreeResponse& subtree) {
1189                 if (ec || subtree.empty())
1190                 {
1191                     return;
1192                 }
1193                 if (subtree[0].second.empty())
1194                 {
1195                     // invalid mapper response, should never happen
1196                     BMCWEB_LOG_ERROR("SetPIDValues: Mapper Error");
1197                     messages::internalError(self->asyncResp->res);
1198                     return;
1199                 }
1200 
1201                 const std::string& path = subtree[0].first;
1202                 const std::string& owner = subtree[0].second[0].first;
1203                 dbus::utility::getAllProperties(
1204                     *crow::connections::systemBus, owner, path,
1205                     thermalModeIface,
1206                     [self, path,
1207                      owner](const boost::system::error_code& ec2,
1208                             const dbus::utility::DBusPropertiesMap& r) {
1209                         if (ec2)
1210                         {
1211                             BMCWEB_LOG_ERROR(
1212                                 "SetPIDValues: Can't get thermalModeIface {}",
1213                                 path);
1214                             messages::internalError(self->asyncResp->res);
1215                             return;
1216                         }
1217                         const std::string* current = nullptr;
1218                         const std::vector<std::string>* supported = nullptr;
1219 
1220                         const bool success = sdbusplus::unpackPropertiesNoThrow(
1221                             dbus_utils::UnpackErrorPrinter(), r, "Current",
1222                             current, "Supported", supported);
1223 
1224                         if (!success)
1225                         {
1226                             messages::internalError(self->asyncResp->res);
1227                             return;
1228                         }
1229 
1230                         if (current == nullptr || supported == nullptr)
1231                         {
1232                             BMCWEB_LOG_ERROR(
1233                                 "SetPIDValues: thermal mode iface invalid {}",
1234                                 path);
1235                             messages::internalError(self->asyncResp->res);
1236                             return;
1237                         }
1238                         self->currentProfile = *current;
1239                         self->supportedProfiles = *supported;
1240                         self->profileConnection = owner;
1241                         self->profilePath = path;
1242                     });
1243             });
1244     }
pidSetDoneredfish::SetPIDValues1245     void pidSetDone()
1246     {
1247         if (asyncResp->res.result() != boost::beast::http::status::ok)
1248         {
1249             return;
1250         }
1251         std::shared_ptr<bmcweb::AsyncResp> response = asyncResp;
1252         if (profile)
1253         {
1254             if (std::ranges::find(supportedProfiles, *profile) ==
1255                 supportedProfiles.end())
1256             {
1257                 messages::actionParameterUnknown(response->res, "Profile",
1258                                                  *profile);
1259                 return;
1260             }
1261             currentProfile = *profile;
1262             sdbusplus::asio::setProperty(
1263                 *crow::connections::systemBus, profileConnection, profilePath,
1264                 thermalModeIface, "Current", *profile,
1265                 [response](const boost::system::error_code& ec) {
1266                     if (ec)
1267                     {
1268                         BMCWEB_LOG_ERROR("Error patching profile{}", ec);
1269                         messages::internalError(response->res);
1270                     }
1271                 });
1272         }
1273 
1274         for (auto& containerPair : configuration)
1275         {
1276             auto& container = containerPair.second;
1277             if (!container)
1278             {
1279                 continue;
1280             }
1281 
1282             const std::string& type = containerPair.first;
1283 
1284             for (auto& [name, value] : *container)
1285             {
1286                 std::string dbusObjName = name;
1287                 std::replace(dbusObjName.begin(), dbusObjName.end(), ' ', '_');
1288                 BMCWEB_LOG_DEBUG("looking for {}", name);
1289 
1290                 auto pathItr = std::ranges::find_if(
1291                     managedObj, [&dbusObjName](const auto& obj) {
1292                         return obj.first.filename() == dbusObjName;
1293                     });
1294                 dbus::utility::DBusPropertiesMap output;
1295 
1296                 output.reserve(16); // The pid interface length
1297 
1298                 // determines if we're patching entity-manager or
1299                 // creating a new object
1300                 bool createNewObject = (pathItr == managedObj.end());
1301                 BMCWEB_LOG_DEBUG("Found = {}", !createNewObject);
1302 
1303                 std::string iface;
1304                 if (!createNewObject)
1305                 {
1306                     bool findInterface = false;
1307                     for (const auto& interface : pathItr->second)
1308                     {
1309                         if (interface.first == pidConfigurationIface)
1310                         {
1311                             if (type == "PidControllers" ||
1312                                 type == "FanControllers")
1313                             {
1314                                 iface = pidConfigurationIface;
1315                                 findInterface = true;
1316                                 break;
1317                             }
1318                         }
1319                         else if (interface.first == pidZoneConfigurationIface)
1320                         {
1321                             if (type == "FanZones")
1322                             {
1323                                 iface = pidZoneConfigurationIface;
1324                                 findInterface = true;
1325                                 break;
1326                             }
1327                         }
1328                         else if (interface.first == stepwiseConfigurationIface)
1329                         {
1330                             if (type == "StepwiseControllers")
1331                             {
1332                                 iface = stepwiseConfigurationIface;
1333                                 findInterface = true;
1334                                 break;
1335                             }
1336                         }
1337                     }
1338 
1339                     // create new object if interface not found
1340                     if (!findInterface)
1341                     {
1342                         createNewObject = true;
1343                     }
1344                 }
1345 
1346                 if (createNewObject && value == nullptr)
1347                 {
1348                     // can't delete a non-existent object
1349                     messages::propertyValueNotInList(response->res, value,
1350                                                      name);
1351                     continue;
1352                 }
1353 
1354                 std::string path;
1355                 if (pathItr != managedObj.end())
1356                 {
1357                     path = pathItr->first.str;
1358                 }
1359 
1360                 BMCWEB_LOG_DEBUG("Create new = {}", createNewObject);
1361 
1362                 // arbitrary limit to avoid attacks
1363                 constexpr const size_t controllerLimit = 500;
1364                 if (createNewObject && objectCount >= controllerLimit)
1365                 {
1366                     messages::resourceExhaustion(response->res, type);
1367                     continue;
1368                 }
1369                 std::string escaped = name;
1370                 std::replace(escaped.begin(), escaped.end(), '_', ' ');
1371                 output.emplace_back("Name", escaped);
1372 
1373                 std::string chassis;
1374                 CreatePIDRet ret = createPidInterface(
1375                     response, type, name, value, path, managedObj,
1376                     createNewObject, output, chassis, currentProfile);
1377                 if (ret == CreatePIDRet::fail)
1378                 {
1379                     return;
1380                 }
1381                 if (ret == CreatePIDRet::del)
1382                 {
1383                     continue;
1384                 }
1385 
1386                 if (!createNewObject)
1387                 {
1388                     for (const auto& property : output)
1389                     {
1390                         dbus::utility::async_method_call(
1391                             asyncResp,
1392                             [response,
1393                              propertyName{std::string(property.first)}](
1394                                 const boost::system::error_code& ec) {
1395                                 if (ec)
1396                                 {
1397                                     BMCWEB_LOG_ERROR("Error patching {}: {}",
1398                                                      propertyName, ec);
1399                                     messages::internalError(response->res);
1400                                     return;
1401                                 }
1402                                 messages::success(response->res);
1403                             },
1404                             "xyz.openbmc_project.EntityManager", path,
1405                             "org.freedesktop.DBus.Properties", "Set", iface,
1406                             property.first, property.second);
1407                     }
1408                 }
1409                 else
1410                 {
1411                     if (chassis.empty())
1412                     {
1413                         BMCWEB_LOG_ERROR("Failed to get chassis from config");
1414                         messages::internalError(response->res);
1415                         return;
1416                     }
1417 
1418                     bool foundChassis = false;
1419                     for (const auto& obj : managedObj)
1420                     {
1421                         if (obj.first.filename() == chassis)
1422                         {
1423                             chassis = obj.first.str;
1424                             foundChassis = true;
1425                             break;
1426                         }
1427                     }
1428                     if (!foundChassis)
1429                     {
1430                         BMCWEB_LOG_ERROR("Failed to find chassis on dbus");
1431                         messages::resourceMissingAtURI(
1432                             response->res,
1433                             boost::urls::format("/redfish/v1/Chassis/{}",
1434                                                 chassis));
1435                         return;
1436                     }
1437 
1438                     dbus::utility::async_method_call(
1439                         asyncResp,
1440                         [response](const boost::system::error_code& ec) {
1441                             if (ec)
1442                             {
1443                                 BMCWEB_LOG_ERROR("Error Adding Pid Object {}",
1444                                                  ec);
1445                                 messages::internalError(response->res);
1446                                 return;
1447                             }
1448                             messages::success(response->res);
1449                         },
1450                         "xyz.openbmc_project.EntityManager", chassis,
1451                         "xyz.openbmc_project.AddObject", "AddObject", output);
1452                 }
1453             }
1454         }
1455     }
1456 
~SetPIDValuesredfish::SetPIDValues1457     ~SetPIDValues()
1458     {
1459         try
1460         {
1461             pidSetDone();
1462         }
1463         catch (...)
1464         {
1465             BMCWEB_LOG_CRITICAL("pidSetDone threw exception");
1466         }
1467     }
1468 
1469     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1470     std::vector<std::pair<std::string, std::optional<nlohmann::json::object_t>>>
1471         configuration;
1472     std::optional<std::string> profile;
1473     dbus::utility::ManagedObjectType managedObj;
1474     std::vector<std::string> supportedProfiles;
1475     std::string currentProfile;
1476     std::string profileConnection;
1477     std::string profilePath;
1478     size_t objectCount = 0;
1479 };
1480 
handleGetManagerOpenBmc(const SubRequest &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1481 inline void handleGetManagerOpenBmc(
1482     const SubRequest& /*req*/,
1483     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1484     const std::string& /*managerId*/)
1485 {
1486     // Default OEM data
1487     nlohmann::json& oemOpenbmc = asyncResp->res.jsonValue;
1488     oemOpenbmc["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager";
1489     oemOpenbmc["@odata.id"] =
1490         boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc",
1491                             BMCWEB_REDFISH_MANAGER_URI_NAME);
1492 
1493     nlohmann::json::object_t certificates;
1494     certificates["@odata.id"] =
1495         boost::urls::format("/redfish/v1/Managers/{}/Truststore/Certificates",
1496                             BMCWEB_REDFISH_MANAGER_URI_NAME);
1497     oemOpenbmc["Certificates"] = std::move(certificates);
1498 
1499     if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA)
1500     {
1501         auto pids = std::make_shared<GetPIDValues>(asyncResp);
1502         pids->run();
1503     }
1504 }
1505 
handlePatchManagerOpenBmc(const SubRequest & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1506 inline void handlePatchManagerOpenBmc(
1507     const SubRequest& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1508     const std::string& /*managerId*/)
1509 {
1510     nlohmann::json::object_t payload = req.payload();
1511 
1512     std::optional<nlohmann::json::object_t> pidControllers;
1513     std::optional<nlohmann::json::object_t> fanControllers;
1514     std::optional<nlohmann::json::object_t> fanZones;
1515     std::optional<nlohmann::json::object_t> stepwiseControllers;
1516     std::optional<std::string> profile;
1517 
1518     if (!json_util::readJsonObject(
1519             payload, asyncResp->res, "OpenBmc/Fan/PidControllers",
1520             pidControllers, "OpenBmc/Fan/FanControllers", fanControllers,
1521             "OpenBmc/Fan/FanZones", fanZones, "OpenBmc/Fan/StepwiseControllers",
1522             stepwiseControllers, "OpenBmc/Fan/Profile", profile))
1523     {
1524         return;
1525     }
1526 
1527     if (pidControllers || fanControllers || fanZones || stepwiseControllers ||
1528         profile)
1529     {
1530         if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA)
1531         {
1532             std::vector<
1533                 std::pair<std::string, std::optional<nlohmann::json::object_t>>>
1534                 configuration;
1535 
1536             if (pidControllers)
1537             {
1538                 configuration.emplace_back("PidControllers",
1539                                            std::move(pidControllers));
1540             }
1541             if (fanControllers)
1542             {
1543                 configuration.emplace_back("FanControllers",
1544                                            std::move(fanControllers));
1545             }
1546             if (fanZones)
1547             {
1548                 configuration.emplace_back("FanZones", std::move(fanZones));
1549             }
1550             if (stepwiseControllers)
1551             {
1552                 configuration.emplace_back("StepwiseControllers",
1553                                            std::move(stepwiseControllers));
1554             }
1555 
1556             auto pid = std::make_shared<SetPIDValues>(
1557                 asyncResp, std::move(configuration), profile);
1558             pid->run();
1559         }
1560         else
1561         {
1562             messages::propertyUnknown(asyncResp->res, "Oem");
1563             return;
1564         }
1565     }
1566 }
1567 
requestRoutesOpenBmcManager(RedfishService & service)1568 inline void requestRoutesOpenBmcManager(RedfishService& service)
1569 {
1570     REDFISH_SUB_ROUTE<"/redfish/v1/Managers/<str>/#/Oem/OpenBmc">(
1571         service, HttpVerb::Get)(handleGetManagerOpenBmc);
1572 
1573     REDFISH_SUB_ROUTE<"/redfish/v1/Managers/<str>/#/Oem/OpenBmc">(
1574         service, HttpVerb::Patch)(handlePatchManagerOpenBmc);
1575 }
1576 
1577 } // namespace redfish
1578