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