xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision ed76121b)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "health.hpp"
19 #include "led.hpp"
20 #include "utils/json_utils.hpp"
21 
22 #include <app.hpp>
23 #include <dbus_utility.hpp>
24 #include <query.hpp>
25 #include <registries/privilege_registry.hpp>
26 #include <sdbusplus/asio/property.hpp>
27 #include <sdbusplus/unpack_properties.hpp>
28 #include <utils/collection.hpp>
29 #include <utils/dbus_utils.hpp>
30 
31 namespace redfish
32 {
33 
34 /**
35  * @brief Retrieves chassis state properties over dbus
36  *
37  * @param[in] aResp - Shared pointer for completing asynchronous calls.
38  *
39  * @return None.
40  */
41 inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> aResp)
42 {
43     // crow::connections::systemBus->async_method_call(
44     sdbusplus::asio::getProperty<std::string>(
45         *crow::connections::systemBus, "xyz.openbmc_project.State.Chassis",
46         "/xyz/openbmc_project/state/chassis0",
47         "xyz.openbmc_project.State.Chassis", "CurrentPowerState",
48         [aResp{std::move(aResp)}](const boost::system::error_code ec,
49                                   const std::string& chassisState) {
50         if (ec)
51         {
52             if (ec == boost::system::errc::host_unreachable)
53             {
54                 // Service not available, no error, just don't return
55                 // chassis state info
56                 BMCWEB_LOG_DEBUG << "Service not available " << ec;
57                 return;
58             }
59             BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
60             messages::internalError(aResp->res);
61             return;
62         }
63 
64         BMCWEB_LOG_DEBUG << "Chassis state: " << chassisState;
65         // Verify Chassis State
66         if (chassisState == "xyz.openbmc_project.State.Chassis.PowerState.On")
67         {
68             aResp->res.jsonValue["PowerState"] = "On";
69             aResp->res.jsonValue["Status"]["State"] = "Enabled";
70         }
71         else if (chassisState ==
72                  "xyz.openbmc_project.State.Chassis.PowerState.Off")
73         {
74             aResp->res.jsonValue["PowerState"] = "Off";
75             aResp->res.jsonValue["Status"]["State"] = "StandbyOffline";
76         }
77         });
78 }
79 
80 inline void getIntrusionByService(std::shared_ptr<bmcweb::AsyncResp> aResp,
81                                   const std::string& service,
82                                   const std::string& objPath)
83 {
84     BMCWEB_LOG_DEBUG << "Get intrusion status by service \n";
85 
86     sdbusplus::asio::getProperty<std::string>(
87         *crow::connections::systemBus, service, objPath,
88         "xyz.openbmc_project.Chassis.Intrusion", "Status",
89         [aResp{std::move(aResp)}](const boost::system::error_code ec,
90                                   const std::string& value) {
91         if (ec)
92         {
93             // do not add err msg in redfish response, because this is not
94             //     mandatory property
95             BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n";
96             return;
97         }
98 
99         aResp->res.jsonValue["PhysicalSecurity"]["IntrusionSensorNumber"] = 1;
100         aResp->res.jsonValue["PhysicalSecurity"]["IntrusionSensor"] = value;
101         });
102 }
103 
104 /**
105  * Retrieves physical security properties over dbus
106  */
107 inline void getPhysicalSecurityData(std::shared_ptr<bmcweb::AsyncResp> aResp)
108 {
109     crow::connections::systemBus->async_method_call(
110         [aResp{std::move(aResp)}](
111             const boost::system::error_code ec,
112             const dbus::utility::MapperGetSubTreeResponse& subtree) {
113         if (ec)
114         {
115             // do not add err msg in redfish response, because this is not
116             //     mandatory property
117             BMCWEB_LOG_INFO << "DBUS error: no matched iface " << ec << "\n";
118             return;
119         }
120         // Iterate over all retrieved ObjectPaths.
121         for (const auto& object : subtree)
122         {
123             for (const auto& service : object.second)
124             {
125                 getIntrusionByService(aResp, service.first, object.first);
126                 return;
127             }
128         }
129         },
130         "xyz.openbmc_project.ObjectMapper",
131         "/xyz/openbmc_project/object_mapper",
132         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
133         "/xyz/openbmc_project/Intrusion", 1,
134         std::array<const char*, 1>{"xyz.openbmc_project.Chassis.Intrusion"});
135 }
136 
137 inline void handleChassisCollectionGet(
138     App& app, const crow::Request& req,
139     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
140 {
141     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
142     {
143         return;
144     }
145     asyncResp->res.jsonValue["@odata.type"] =
146         "#ChassisCollection.ChassisCollection";
147     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
148     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
149 
150     collection_util::getCollectionMembers(
151         asyncResp, "/redfish/v1/Chassis",
152         {"xyz.openbmc_project.Inventory.Item.Board",
153          "xyz.openbmc_project.Inventory.Item.Chassis"});
154 }
155 
156 /**
157  * ChassisCollection derived class for delivering Chassis Collection Schema
158  *  Functions triggers appropriate requests on DBus
159  */
160 inline void requestRoutesChassisCollection(App& app)
161 {
162     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/")
163         .privileges(redfish::privileges::getChassisCollection)
164         .methods(boost::beast::http::verb::get)(
165             std::bind_front(handleChassisCollectionGet, std::ref(app)));
166 }
167 
168 inline void
169     getChassisLocationCode(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
170                            const std::string& connectionName,
171                            const std::string& path)
172 {
173     sdbusplus::asio::getProperty<std::string>(
174         *crow::connections::systemBus, connectionName, path,
175         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
176         [asyncResp](const boost::system::error_code ec,
177                     const std::string& property) {
178         if (ec)
179         {
180             BMCWEB_LOG_DEBUG << "DBUS response error for Location";
181             messages::internalError(asyncResp->res);
182             return;
183         }
184 
185         asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
186             property;
187         });
188 }
189 
190 inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
191                            const std::string& connectionName,
192                            const std::string& path)
193 {
194     sdbusplus::asio::getProperty<std::string>(
195         *crow::connections::systemBus, connectionName, path,
196         "xyz.openbmc_project.Common.UUID", "UUID",
197         [asyncResp](const boost::system::error_code ec,
198                     const std::string& chassisUUID) {
199         if (ec)
200         {
201             BMCWEB_LOG_DEBUG << "DBUS response error for UUID";
202             messages::internalError(asyncResp->res);
203             return;
204         }
205         asyncResp->res.jsonValue["UUID"] = chassisUUID;
206         });
207 }
208 
209 inline void
210     handleChassisGet(App& app, const crow::Request& req,
211                      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
212                      const std::string& chassisId)
213 {
214     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
215     {
216         return;
217     }
218     const std::array<const char*, 2> interfaces = {
219         "xyz.openbmc_project.Inventory.Item.Board",
220         "xyz.openbmc_project.Inventory.Item.Chassis"};
221 
222     crow::connections::systemBus->async_method_call(
223         [asyncResp, chassisId(std::string(chassisId))](
224             const boost::system::error_code ec,
225             const dbus::utility::MapperGetSubTreeResponse& subtree) {
226         if (ec)
227         {
228             messages::internalError(asyncResp->res);
229             return;
230         }
231         // Iterate over all retrieved ObjectPaths.
232         for (const std::pair<
233                  std::string,
234                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
235                  object : subtree)
236         {
237             const std::string& path = object.first;
238             const std::vector<std::pair<std::string, std::vector<std::string>>>&
239                 connectionNames = object.second;
240 
241             sdbusplus::message::object_path objPath(path);
242             if (objPath.filename() != chassisId)
243             {
244                 continue;
245             }
246 
247             auto health = std::make_shared<HealthPopulate>(asyncResp);
248 
249             sdbusplus::asio::getProperty<std::vector<std::string>>(
250                 *crow::connections::systemBus,
251                 "xyz.openbmc_project.ObjectMapper", path + "/all_sensors",
252                 "xyz.openbmc_project.Association", "endpoints",
253                 [health](const boost::system::error_code ec2,
254                          const std::vector<std::string>& resp) {
255                 if (ec2)
256                 {
257                     return; // no sensors = no failures
258                 }
259                 health->inventory = resp;
260                 });
261 
262             health->populate();
263 
264             if (connectionNames.empty())
265             {
266                 BMCWEB_LOG_ERROR << "Got 0 Connection names";
267                 continue;
268             }
269 
270             asyncResp->res.jsonValue["@odata.type"] =
271                 "#Chassis.v1_16_0.Chassis";
272             asyncResp->res.jsonValue["@odata.id"] =
273                 "/redfish/v1/Chassis/" + chassisId;
274             asyncResp->res.jsonValue["Name"] = "Chassis Collection";
275             asyncResp->res.jsonValue["ChassisType"] = "RackMount";
276             asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
277                 "/redfish/v1/Chassis/" + chassisId + "/Actions/Chassis.Reset";
278             asyncResp->res
279                 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] =
280                 "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
281             asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] =
282                 "/redfish/v1/Systems/system/PCIeDevices";
283 
284             sdbusplus::asio::getProperty<std::vector<std::string>>(
285                 *crow::connections::systemBus,
286                 "xyz.openbmc_project.ObjectMapper", path + "/drive",
287                 "xyz.openbmc_project.Association", "endpoints",
288                 [asyncResp, chassisId](const boost::system::error_code ec3,
289                                        const std::vector<std::string>& resp) {
290                 if (ec3 || resp.empty())
291                 {
292                     return; // no drives = no failures
293                 }
294 
295                 nlohmann::json reference;
296                 reference["@odata.id"] = crow::utility::urlFromPieces(
297                     "redfish", "v1", "Chassis", chassisId, "Drives");
298                 asyncResp->res.jsonValue["Drives"] = std::move(reference);
299                 });
300 
301             const std::string& connectionName = connectionNames[0].first;
302 
303             const std::vector<std::string>& interfaces2 =
304                 connectionNames[0].second;
305             const std::array<const char*, 2> hasIndicatorLed = {
306                 "xyz.openbmc_project.Inventory.Item.Panel",
307                 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
308 
309             const std::string assetTagInterface =
310                 "xyz.openbmc_project.Inventory.Decorator.AssetTag";
311             if (std::find(interfaces2.begin(), interfaces2.end(),
312                           assetTagInterface) != interfaces2.end())
313             {
314                 sdbusplus::asio::getProperty<std::string>(
315                     *crow::connections::systemBus, connectionName, path,
316                     assetTagInterface, "AssetTag",
317                     [asyncResp, chassisId(std::string(chassisId))](
318                         const boost::system::error_code ec2,
319                         const std::string& property) {
320                     if (ec2)
321                     {
322                         BMCWEB_LOG_DEBUG << "DBus response error for AssetTag";
323                         messages::internalError(asyncResp->res);
324                         return;
325                     }
326                     asyncResp->res.jsonValue["AssetTag"] = property;
327                     });
328             }
329 
330             for (const char* interface : hasIndicatorLed)
331             {
332                 if (std::find(interfaces2.begin(), interfaces2.end(),
333                               interface) != interfaces2.end())
334                 {
335                     getIndicatorLedState(asyncResp);
336                     getLocationIndicatorActive(asyncResp);
337                     break;
338                 }
339             }
340 
341             sdbusplus::asio::getAllProperties(
342                 *crow::connections::systemBus, connectionName, path,
343                 "xyz.openbmc_project.Inventory.Decorator.Asset",
344                 [asyncResp, chassisId(std::string(chassisId))](
345                     const boost::system::error_code /*ec2*/,
346                     const dbus::utility::DBusPropertiesMap& propertiesList) {
347                 const std::string* partNumber = nullptr;
348                 const std::string* serialNumber = nullptr;
349                 const std::string* manufacturer = nullptr;
350                 const std::string* model = nullptr;
351                 const std::string* sparePartNumber = nullptr;
352 
353                 const bool success = sdbusplus::unpackPropertiesNoThrow(
354                     dbus_utils::UnpackErrorPrinter(), propertiesList,
355                     "PartNumber", partNumber, "SerialNumber", serialNumber,
356                     "Manufacturer", manufacturer, "Model", model,
357                     "SparePartNumber", sparePartNumber);
358 
359                 if (!success)
360                 {
361                     messages::internalError(asyncResp->res);
362                     return;
363                 }
364 
365                 if (partNumber != nullptr)
366                 {
367                     asyncResp->res.jsonValue["PartNumber"] = *partNumber;
368                 }
369 
370                 if (serialNumber != nullptr)
371                 {
372                     asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
373                 }
374 
375                 if (manufacturer != nullptr)
376                 {
377                     asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
378                 }
379 
380                 if (model != nullptr)
381                 {
382                     asyncResp->res.jsonValue["Model"] = *model;
383                 }
384 
385                 // SparePartNumber is optional on D-Bus
386                 // so skip if it is empty
387                 if (sparePartNumber != nullptr && !sparePartNumber->empty())
388                 {
389                     asyncResp->res.jsonValue["SparePartNumber"] =
390                         *sparePartNumber;
391                 }
392 
393                 asyncResp->res.jsonValue["Name"] = chassisId;
394                 asyncResp->res.jsonValue["Id"] = chassisId;
395 #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL
396                 asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
397                     "/redfish/v1/Chassis/" + chassisId + "/Thermal";
398                 // Power object
399                 asyncResp->res.jsonValue["Power"]["@odata.id"] =
400                     "/redfish/v1/Chassis/" + chassisId + "/Power";
401 #endif
402 #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM
403                 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] =
404                     crow::utility::urlFromPieces("redfish", "v1", "Chassis",
405                                                  chassisId, "ThermalSubsystem");
406 #endif
407                 // SensorCollection
408                 asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
409                     "/redfish/v1/Chassis/" + chassisId + "/Sensors";
410                 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
411 
412                 nlohmann::json::array_t computerSystems;
413                 nlohmann::json::object_t system;
414                 system["@odata.id"] = "/redfish/v1/Systems/system";
415                 computerSystems.push_back(std::move(system));
416                 asyncResp->res.jsonValue["Links"]["ComputerSystems"] =
417                     std::move(computerSystems);
418 
419                 nlohmann::json::array_t managedBy;
420                 nlohmann::json::object_t manager;
421                 manager["@odata.id"] = "/redfish/v1/Managers/bmc";
422                 managedBy.push_back(std::move(manager));
423                 asyncResp->res.jsonValue["Links"]["ManagedBy"] =
424                     std::move(managedBy);
425                 getChassisState(asyncResp);
426                 });
427 
428             for (const auto& interface : interfaces2)
429             {
430                 if (interface == "xyz.openbmc_project.Common.UUID")
431                 {
432                     getChassisUUID(asyncResp, connectionName, path);
433                 }
434                 else if (interface ==
435                          "xyz.openbmc_project.Inventory.Decorator.LocationCode")
436                 {
437                     getChassisLocationCode(asyncResp, connectionName, path);
438                 }
439             }
440 
441             return;
442         }
443 
444         // Couldn't find an object with that name.  return an error
445         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
446         },
447         "xyz.openbmc_project.ObjectMapper",
448         "/xyz/openbmc_project/object_mapper",
449         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
450         "/xyz/openbmc_project/inventory", 0, interfaces);
451 
452     getPhysicalSecurityData(asyncResp);
453 }
454 
455 inline void
456     handleChassisPatch(App& app, const crow::Request& req,
457                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
458                        const std::string& param)
459 {
460     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
461     {
462         return;
463     }
464     std::optional<bool> locationIndicatorActive;
465     std::optional<std::string> indicatorLed;
466 
467     if (param.empty())
468     {
469         return;
470     }
471 
472     if (!json_util::readJsonPatch(
473             req, asyncResp->res, "LocationIndicatorActive",
474             locationIndicatorActive, "IndicatorLED", indicatorLed))
475     {
476         return;
477     }
478 
479     // TODO (Gunnar): Remove IndicatorLED after enough time has passed
480     if (!locationIndicatorActive && !indicatorLed)
481     {
482         return; // delete this when we support more patch properties
483     }
484     if (indicatorLed)
485     {
486         asyncResp->res.addHeader(
487             boost::beast::http::field::warning,
488             "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
489     }
490 
491     const std::array<const char*, 2> interfaces = {
492         "xyz.openbmc_project.Inventory.Item.Board",
493         "xyz.openbmc_project.Inventory.Item.Chassis"};
494 
495     const std::string& chassisId = param;
496 
497     crow::connections::systemBus->async_method_call(
498         [asyncResp, chassisId, locationIndicatorActive,
499          indicatorLed](const boost::system::error_code ec,
500                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
501         if (ec)
502         {
503             messages::internalError(asyncResp->res);
504             return;
505         }
506 
507         // Iterate over all retrieved ObjectPaths.
508         for (const std::pair<
509                  std::string,
510                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
511                  object : subtree)
512         {
513             const std::string& path = object.first;
514             const std::vector<std::pair<std::string, std::vector<std::string>>>&
515                 connectionNames = object.second;
516 
517             sdbusplus::message::object_path objPath(path);
518             if (objPath.filename() != chassisId)
519             {
520                 continue;
521             }
522 
523             if (connectionNames.empty())
524             {
525                 BMCWEB_LOG_ERROR << "Got 0 Connection names";
526                 continue;
527             }
528 
529             const std::vector<std::string>& interfaces3 =
530                 connectionNames[0].second;
531 
532             const std::array<const char*, 2> hasIndicatorLed = {
533                 "xyz.openbmc_project.Inventory.Item.Panel",
534                 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
535             bool indicatorChassis = false;
536             for (const char* interface : hasIndicatorLed)
537             {
538                 if (std::find(interfaces3.begin(), interfaces3.end(),
539                               interface) != interfaces3.end())
540                 {
541                     indicatorChassis = true;
542                     break;
543                 }
544             }
545             if (locationIndicatorActive)
546             {
547                 if (indicatorChassis)
548                 {
549                     setLocationIndicatorActive(asyncResp,
550                                                *locationIndicatorActive);
551                 }
552                 else
553                 {
554                     messages::propertyUnknown(asyncResp->res,
555                                               "LocationIndicatorActive");
556                 }
557             }
558             if (indicatorLed)
559             {
560                 if (indicatorChassis)
561                 {
562                     setIndicatorLedState(asyncResp, *indicatorLed);
563                 }
564                 else
565                 {
566                     messages::propertyUnknown(asyncResp->res, "IndicatorLED");
567                 }
568             }
569             return;
570         }
571 
572         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
573         },
574         "xyz.openbmc_project.ObjectMapper",
575         "/xyz/openbmc_project/object_mapper",
576         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
577         "/xyz/openbmc_project/inventory", 0, interfaces);
578 }
579 
580 /**
581  * Chassis override class for delivering Chassis Schema
582  * Functions triggers appropriate requests on DBus
583  */
584 inline void requestRoutesChassis(App& app)
585 {
586     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
587         .privileges(redfish::privileges::getChassis)
588         .methods(boost::beast::http::verb::get)(
589             std::bind_front(handleChassisGet, std::ref(app)));
590 
591     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
592         .privileges(redfish::privileges::patchChassis)
593         .methods(boost::beast::http::verb::patch)(
594             std::bind_front(handleChassisPatch, std::ref(app)));
595 }
596 
597 inline void
598     doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
599 {
600     const char* busName = "xyz.openbmc_project.ObjectMapper";
601     const char* path = "/xyz/openbmc_project/object_mapper";
602     const char* interface = "xyz.openbmc_project.ObjectMapper";
603     const char* method = "GetSubTreePaths";
604 
605     const std::array<const char*, 1> interfaces = {
606         "xyz.openbmc_project.State.Chassis"};
607 
608     // Use mapper to get subtree paths.
609     crow::connections::systemBus->async_method_call(
610         [asyncResp](
611             const boost::system::error_code ec,
612             const dbus::utility::MapperGetSubTreePathsResponse& chassisList) {
613         if (ec)
614         {
615             BMCWEB_LOG_DEBUG << "[mapper] Bad D-Bus request error: " << ec;
616             messages::internalError(asyncResp->res);
617             return;
618         }
619 
620         const char* processName = "xyz.openbmc_project.State.Chassis";
621         const char* interfaceName = "xyz.openbmc_project.State.Chassis";
622         const char* destProperty = "RequestedPowerTransition";
623         const std::string propertyValue =
624             "xyz.openbmc_project.State.Chassis.Transition.PowerCycle";
625         std::string objectPath = "/xyz/openbmc_project/state/chassis_system0";
626 
627         /* Look for system reset chassis path */
628         if ((std::find(chassisList.begin(), chassisList.end(), objectPath)) ==
629             chassisList.end())
630         {
631             /* We prefer to reset the full chassis_system, but if it doesn't
632              * exist on some platforms, fall back to a host-only power reset
633              */
634             objectPath = "/xyz/openbmc_project/state/chassis0";
635         }
636 
637         crow::connections::systemBus->async_method_call(
638             [asyncResp](const boost::system::error_code ec2) {
639             // Use "Set" method to set the property value.
640             if (ec2)
641             {
642                 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec2;
643                 messages::internalError(asyncResp->res);
644                 return;
645             }
646 
647             messages::success(asyncResp->res);
648             },
649             processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
650             interfaceName, destProperty,
651             dbus::utility::DbusVariantType{propertyValue});
652         },
653         busName, path, interface, method, "/", 0, interfaces);
654 }
655 
656 inline void handleChassisResetActionInfoPost(
657     App& app, const crow::Request& req,
658     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
659     const std::string& /*chassisId*/)
660 {
661     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
662     {
663         return;
664     }
665     BMCWEB_LOG_DEBUG << "Post Chassis Reset.";
666 
667     std::string resetType;
668 
669     if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
670     {
671         return;
672     }
673 
674     if (resetType != "PowerCycle")
675     {
676         BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
677                          << resetType;
678         messages::actionParameterNotSupported(asyncResp->res, resetType,
679                                               "ResetType");
680 
681         return;
682     }
683     doChassisPowerCycle(asyncResp);
684 }
685 
686 /**
687  * ChassisResetAction class supports the POST method for the Reset
688  * action.
689  * Function handles POST method request.
690  * Analyzes POST body before sending Reset request data to D-Bus.
691  */
692 
693 inline void requestRoutesChassisResetAction(App& app)
694 {
695     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/")
696         .privileges(redfish::privileges::postChassis)
697         .methods(boost::beast::http::verb::post)(
698             std::bind_front(handleChassisResetActionInfoPost, std::ref(app)));
699 }
700 
701 inline void handleChassisResetActionInfoGet(
702     App& app, const crow::Request& req,
703     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
704     const std::string& chassisId)
705 {
706     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
707     {
708         return;
709     }
710     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
711     asyncResp->res.jsonValue["@odata.id"] =
712         "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
713     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
714 
715     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
716     nlohmann::json::array_t parameters;
717     nlohmann::json::object_t parameter;
718     parameter["Name"] = "ResetType";
719     parameter["Required"] = true;
720     parameter["DataType"] = "String";
721     nlohmann::json::array_t allowed;
722     allowed.push_back("PowerCycle");
723     parameter["AllowableValues"] = std::move(allowed);
724     parameters.push_back(std::move(parameter));
725 
726     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
727 }
728 
729 /**
730  * ChassisResetActionInfo derived class for delivering Chassis
731  * ResetType AllowableValues using ResetInfo schema.
732  */
733 inline void requestRoutesChassisResetActionInfo(App& app)
734 {
735     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/")
736         .privileges(redfish::privileges::getActionInfo)
737         .methods(boost::beast::http::verb::get)(
738             std::bind_front(handleChassisResetActionInfoGet, std::ref(app)));
739 }
740 
741 } // namespace redfish
742