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