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