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