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