xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 74ab83c5)
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.v1_16_0.Chassis",
446                                    chassisId);
447         },
448         "xyz.openbmc_project.ObjectMapper",
449         "/xyz/openbmc_project/object_mapper",
450         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
451         "/xyz/openbmc_project/inventory", 0, interfaces);
452 
453     getPhysicalSecurityData(asyncResp);
454 }
455 
456 inline void
457     handleChassisPatch(App& app, const crow::Request& req,
458                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
459                        const std::string& param)
460 {
461     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
462     {
463         return;
464     }
465     std::optional<bool> locationIndicatorActive;
466     std::optional<std::string> indicatorLed;
467 
468     if (param.empty())
469     {
470         return;
471     }
472 
473     if (!json_util::readJsonPatch(
474             req, asyncResp->res, "LocationIndicatorActive",
475             locationIndicatorActive, "IndicatorLED", indicatorLed))
476     {
477         return;
478     }
479 
480     // TODO (Gunnar): Remove IndicatorLED after enough time has passed
481     if (!locationIndicatorActive && !indicatorLed)
482     {
483         return; // delete this when we support more patch properties
484     }
485     if (indicatorLed)
486     {
487         asyncResp->res.addHeader(
488             boost::beast::http::field::warning,
489             "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
490     }
491 
492     const std::array<const char*, 2> interfaces = {
493         "xyz.openbmc_project.Inventory.Item.Board",
494         "xyz.openbmc_project.Inventory.Item.Chassis"};
495 
496     const std::string& chassisId = param;
497 
498     crow::connections::systemBus->async_method_call(
499         [asyncResp, chassisId, locationIndicatorActive,
500          indicatorLed](const boost::system::error_code ec,
501                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
502         if (ec)
503         {
504             messages::internalError(asyncResp->res);
505             return;
506         }
507 
508         // Iterate over all retrieved ObjectPaths.
509         for (const std::pair<
510                  std::string,
511                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
512                  object : subtree)
513         {
514             const std::string& path = object.first;
515             const std::vector<std::pair<std::string, std::vector<std::string>>>&
516                 connectionNames = object.second;
517 
518             sdbusplus::message::object_path objPath(path);
519             if (objPath.filename() != chassisId)
520             {
521                 continue;
522             }
523 
524             if (connectionNames.empty())
525             {
526                 BMCWEB_LOG_ERROR << "Got 0 Connection names";
527                 continue;
528             }
529 
530             const std::vector<std::string>& interfaces3 =
531                 connectionNames[0].second;
532 
533             const std::array<const char*, 2> hasIndicatorLed = {
534                 "xyz.openbmc_project.Inventory.Item.Panel",
535                 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
536             bool indicatorChassis = false;
537             for (const char* interface : hasIndicatorLed)
538             {
539                 if (std::find(interfaces3.begin(), interfaces3.end(),
540                               interface) != interfaces3.end())
541                 {
542                     indicatorChassis = true;
543                     break;
544                 }
545             }
546             if (locationIndicatorActive)
547             {
548                 if (indicatorChassis)
549                 {
550                     setLocationIndicatorActive(asyncResp,
551                                                *locationIndicatorActive);
552                 }
553                 else
554                 {
555                     messages::propertyUnknown(asyncResp->res,
556                                               "LocationIndicatorActive");
557                 }
558             }
559             if (indicatorLed)
560             {
561                 if (indicatorChassis)
562                 {
563                     setIndicatorLedState(asyncResp, *indicatorLed);
564                 }
565                 else
566                 {
567                     messages::propertyUnknown(asyncResp->res, "IndicatorLED");
568                 }
569             }
570             return;
571         }
572 
573         messages::resourceNotFound(asyncResp->res, "#Chassis.v1_14_0.Chassis",
574                                    chassisId);
575         },
576         "xyz.openbmc_project.ObjectMapper",
577         "/xyz/openbmc_project/object_mapper",
578         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
579         "/xyz/openbmc_project/inventory", 0, interfaces);
580 }
581 
582 /**
583  * Chassis override class for delivering Chassis Schema
584  * Functions triggers appropriate requests on DBus
585  */
586 inline void requestRoutesChassis(App& app)
587 {
588     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
589         .privileges(redfish::privileges::getChassis)
590         .methods(boost::beast::http::verb::get)(
591             std::bind_front(handleChassisGet, std::ref(app)));
592 
593     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
594         .privileges(redfish::privileges::patchChassis)
595         .methods(boost::beast::http::verb::patch)(
596             std::bind_front(handleChassisPatch, std::ref(app)));
597 }
598 
599 inline void
600     doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
601 {
602     const char* busName = "xyz.openbmc_project.ObjectMapper";
603     const char* path = "/xyz/openbmc_project/object_mapper";
604     const char* interface = "xyz.openbmc_project.ObjectMapper";
605     const char* method = "GetSubTreePaths";
606 
607     const std::array<const char*, 1> interfaces = {
608         "xyz.openbmc_project.State.Chassis"};
609 
610     // Use mapper to get subtree paths.
611     crow::connections::systemBus->async_method_call(
612         [asyncResp](
613             const boost::system::error_code ec,
614             const dbus::utility::MapperGetSubTreePathsResponse& chassisList) {
615         if (ec)
616         {
617             BMCWEB_LOG_DEBUG << "[mapper] Bad D-Bus request error: " << ec;
618             messages::internalError(asyncResp->res);
619             return;
620         }
621 
622         const char* processName = "xyz.openbmc_project.State.Chassis";
623         const char* interfaceName = "xyz.openbmc_project.State.Chassis";
624         const char* destProperty = "RequestedPowerTransition";
625         const std::string propertyValue =
626             "xyz.openbmc_project.State.Chassis.Transition.PowerCycle";
627         std::string objectPath = "/xyz/openbmc_project/state/chassis_system0";
628 
629         /* Look for system reset chassis path */
630         if ((std::find(chassisList.begin(), chassisList.end(), objectPath)) ==
631             chassisList.end())
632         {
633             /* We prefer to reset the full chassis_system, but if it doesn't
634              * exist on some platforms, fall back to a host-only power reset
635              */
636             objectPath = "/xyz/openbmc_project/state/chassis0";
637         }
638 
639         crow::connections::systemBus->async_method_call(
640             [asyncResp](const boost::system::error_code ec2) {
641             // Use "Set" method to set the property value.
642             if (ec2)
643             {
644                 BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec2;
645                 messages::internalError(asyncResp->res);
646                 return;
647             }
648 
649             messages::success(asyncResp->res);
650             },
651             processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
652             interfaceName, destProperty,
653             dbus::utility::DbusVariantType{propertyValue});
654         },
655         busName, path, interface, method, "/", 0, interfaces);
656 }
657 
658 inline void handleChassisResetActionInfoPost(
659     App& app, const crow::Request& req,
660     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
661     const std::string& /*chassisId*/)
662 {
663     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
664     {
665         return;
666     }
667     BMCWEB_LOG_DEBUG << "Post Chassis Reset.";
668 
669     std::string resetType;
670 
671     if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
672     {
673         return;
674     }
675 
676     if (resetType != "PowerCycle")
677     {
678         BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
679                          << resetType;
680         messages::actionParameterNotSupported(asyncResp->res, resetType,
681                                               "ResetType");
682 
683         return;
684     }
685     doChassisPowerCycle(asyncResp);
686 }
687 
688 /**
689  * ChassisResetAction class supports the POST method for the Reset
690  * action.
691  * Function handles POST method request.
692  * Analyzes POST body before sending Reset request data to D-Bus.
693  */
694 
695 inline void requestRoutesChassisResetAction(App& app)
696 {
697     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/")
698         .privileges(redfish::privileges::postChassis)
699         .methods(boost::beast::http::verb::post)(
700             std::bind_front(handleChassisResetActionInfoPost, std::ref(app)));
701 }
702 
703 inline void handleChassisResetActionInfoGet(
704     App& app, const crow::Request& req,
705     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
706     const std::string& chassisId)
707 {
708     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
709     {
710         return;
711     }
712     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
713     asyncResp->res.jsonValue["@odata.id"] =
714         "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
715     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
716 
717     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
718     nlohmann::json::array_t parameters;
719     nlohmann::json::object_t parameter;
720     parameter["Name"] = "ResetType";
721     parameter["Required"] = true;
722     parameter["DataType"] = "String";
723     nlohmann::json::array_t allowed;
724     allowed.push_back("PowerCycle");
725     parameter["AllowableValues"] = std::move(allowed);
726     parameters.push_back(std::move(parameter));
727 
728     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
729 }
730 
731 /**
732  * ChassisResetActionInfo derived class for delivering Chassis
733  * ResetType AllowableValues using ResetInfo schema.
734  */
735 inline void requestRoutesChassisResetActionInfo(App& app)
736 {
737     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/")
738         .privileges(redfish::privileges::getActionInfo)
739         .methods(boost::beast::http::verb::get)(
740             std::bind_front(handleChassisResetActionInfoGet, std::ref(app)));
741 }
742 
743 } // namespace redfish
744