xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 88ada3bc)
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_ERROR << "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_ERROR << "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_ERROR << "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             BMCWEB_LOG_ERROR << "DBUS response error " << ec;
237             messages::internalError(asyncResp->res);
238             return;
239         }
240         // Iterate over all retrieved ObjectPaths.
241         for (const std::pair<
242                  std::string,
243                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
244                  object : subtree)
245         {
246             const std::string& path = object.first;
247             const std::vector<std::pair<std::string, std::vector<std::string>>>&
248                 connectionNames = object.second;
249 
250             sdbusplus::message::object_path objPath(path);
251             if (objPath.filename() != chassisId)
252             {
253                 continue;
254             }
255 
256             auto health = std::make_shared<HealthPopulate>(asyncResp);
257 
258             if constexpr (bmcwebEnableHealthPopulate)
259             {
260                 dbus::utility::getAssociationEndPoints(
261                     path + "/all_sensors",
262                     [health](const boost::system::error_code& ec2,
263                              const dbus::utility::MapperEndPoints& resp) {
264                     if (ec2)
265                     {
266                         return; // no sensors = no failures
267                     }
268                     health->inventory = resp;
269                     });
270 
271                 health->populate();
272             }
273 
274             if (connectionNames.empty())
275             {
276                 BMCWEB_LOG_ERROR << "Got 0 Connection names";
277                 continue;
278             }
279 
280             asyncResp->res.jsonValue["@odata.type"] =
281                 "#Chassis.v1_22_0.Chassis";
282             asyncResp->res.jsonValue["@odata.id"] =
283                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
284             asyncResp->res.jsonValue["Name"] = "Chassis Collection";
285             asyncResp->res.jsonValue["ChassisType"] = "RackMount";
286             asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
287                 boost::urls::format(
288                     "/redfish/v1/Chassis/{}/Actions/Chassis.Reset", chassisId);
289             asyncResp->res
290                 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] =
291                 boost::urls::format("/redfish/v1/Chassis/{}/ResetActionInfo",
292                                     chassisId);
293             asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] =
294                 "/redfish/v1/Systems/system/PCIeDevices";
295 
296             dbus::utility::getAssociationEndPoints(
297                 path + "/drive",
298                 [asyncResp,
299                  chassisId](const boost::system::error_code& ec3,
300                             const dbus::utility::MapperEndPoints& resp) {
301                 if (ec3 || resp.empty())
302                 {
303                     return; // no drives = no failures
304                 }
305 
306                 nlohmann::json reference;
307                 reference["@odata.id"] = boost::urls::format(
308                     "/redfish/v1/Chassis/{}/Drives", chassisId);
309                 asyncResp->res.jsonValue["Drives"] = std::move(reference);
310                 });
311 
312             const std::string& connectionName = connectionNames[0].first;
313 
314             const std::vector<std::string>& interfaces2 =
315                 connectionNames[0].second;
316             const std::array<const char*, 2> hasIndicatorLed = {
317                 "xyz.openbmc_project.Inventory.Item.Panel",
318                 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
319 
320             const std::string assetTagInterface =
321                 "xyz.openbmc_project.Inventory.Decorator.AssetTag";
322             const std::string replaceableInterface =
323                 "xyz.openbmc_project.Inventory.Decorator.Replaceable";
324             for (const auto& interface : interfaces2)
325             {
326                 if (interface == assetTagInterface)
327                 {
328                     sdbusplus::asio::getProperty<std::string>(
329                         *crow::connections::systemBus, connectionName, path,
330                         assetTagInterface, "AssetTag",
331                         [asyncResp,
332                          chassisId](const boost::system::error_code& ec2,
333                                     const std::string& property) {
334                         if (ec2)
335                         {
336                             BMCWEB_LOG_ERROR
337                                 << "DBus response error for AssetTag: " << ec2;
338                             messages::internalError(asyncResp->res);
339                             return;
340                         }
341                         asyncResp->res.jsonValue["AssetTag"] = property;
342                         });
343                 }
344                 else if (interface == replaceableInterface)
345                 {
346                     sdbusplus::asio::getProperty<bool>(
347                         *crow::connections::systemBus, connectionName, path,
348                         replaceableInterface, "HotPluggable",
349                         [asyncResp,
350                          chassisId](const boost::system::error_code& ec2,
351                                     const bool property) {
352                         if (ec2)
353                         {
354                             BMCWEB_LOG_ERROR
355                                 << "DBus response error for HotPluggable: "
356                                 << ec2;
357                             messages::internalError(asyncResp->res);
358                             return;
359                         }
360                         asyncResp->res.jsonValue["HotPluggable"] = property;
361                         });
362                 }
363             }
364 
365             for (const char* interface : hasIndicatorLed)
366             {
367                 if (std::find(interfaces2.begin(), interfaces2.end(),
368                               interface) != interfaces2.end())
369                 {
370                     getIndicatorLedState(asyncResp);
371                     getLocationIndicatorActive(asyncResp);
372                     break;
373                 }
374             }
375 
376             sdbusplus::asio::getAllProperties(
377                 *crow::connections::systemBus, connectionName, path,
378                 "xyz.openbmc_project.Inventory.Decorator.Asset",
379                 [asyncResp, chassisId(std::string(chassisId))](
380                     const boost::system::error_code& /*ec2*/,
381                     const dbus::utility::DBusPropertiesMap& propertiesList) {
382                 const std::string* partNumber = nullptr;
383                 const std::string* serialNumber = nullptr;
384                 const std::string* manufacturer = nullptr;
385                 const std::string* model = nullptr;
386                 const std::string* sparePartNumber = nullptr;
387 
388                 const bool success = sdbusplus::unpackPropertiesNoThrow(
389                     dbus_utils::UnpackErrorPrinter(), propertiesList,
390                     "PartNumber", partNumber, "SerialNumber", serialNumber,
391                     "Manufacturer", manufacturer, "Model", model,
392                     "SparePartNumber", sparePartNumber);
393 
394                 if (!success)
395                 {
396                     messages::internalError(asyncResp->res);
397                     return;
398                 }
399 
400                 if (partNumber != nullptr)
401                 {
402                     asyncResp->res.jsonValue["PartNumber"] = *partNumber;
403                 }
404 
405                 if (serialNumber != nullptr)
406                 {
407                     asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
408                 }
409 
410                 if (manufacturer != nullptr)
411                 {
412                     asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
413                 }
414 
415                 if (model != nullptr)
416                 {
417                     asyncResp->res.jsonValue["Model"] = *model;
418                 }
419 
420                 // SparePartNumber is optional on D-Bus
421                 // so skip if it is empty
422                 if (sparePartNumber != nullptr && !sparePartNumber->empty())
423                 {
424                     asyncResp->res.jsonValue["SparePartNumber"] =
425                         *sparePartNumber;
426                 }
427 
428                 asyncResp->res.jsonValue["Name"] = chassisId;
429                 asyncResp->res.jsonValue["Id"] = chassisId;
430 #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL
431                 asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
432                     boost::urls::format("/redfish/v1/Chassis/{}/Thermal",
433                                         chassisId);
434                 // Power object
435                 asyncResp->res.jsonValue["Power"]["@odata.id"] =
436                     boost::urls::format("/redfish/v1/Chassis/{}/Power",
437                                         chassisId);
438 #endif
439 #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM
440                 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] =
441                     boost::urls::format(
442                         "/redfish/v1/Chassis/{}/ThermalSubsystem", chassisId);
443                 asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] =
444                     boost::urls::format("/redfish/v1/Chassis/{}/PowerSubsystem",
445                                         chassisId);
446                 asyncResp->res.jsonValue["EnvironmentMetrics"]["@odata.id"] =
447                     boost::urls::format(
448                         "/redfish/v1/Chassis/{}/EnvironmentMetrics", chassisId);
449 #endif
450                 // SensorCollection
451                 asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
452                     boost::urls::format("/redfish/v1/Chassis/{}/Sensors",
453                                         chassisId);
454                 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
455 
456                 nlohmann::json::array_t computerSystems;
457                 nlohmann::json::object_t system;
458                 system["@odata.id"] = "/redfish/v1/Systems/system";
459                 computerSystems.emplace_back(std::move(system));
460                 asyncResp->res.jsonValue["Links"]["ComputerSystems"] =
461                     std::move(computerSystems);
462 
463                 nlohmann::json::array_t managedBy;
464                 nlohmann::json::object_t manager;
465                 manager["@odata.id"] = "/redfish/v1/Managers/bmc";
466                 managedBy.emplace_back(std::move(manager));
467                 asyncResp->res.jsonValue["Links"]["ManagedBy"] =
468                     std::move(managedBy);
469                 getChassisState(asyncResp);
470                 });
471 
472             for (const auto& interface : interfaces2)
473             {
474                 if (interface == "xyz.openbmc_project.Common.UUID")
475                 {
476                     getChassisUUID(asyncResp, connectionName, path);
477                 }
478                 else if (interface ==
479                          "xyz.openbmc_project.Inventory.Decorator.LocationCode")
480                 {
481                     getChassisLocationCode(asyncResp, connectionName, path);
482                 }
483             }
484 
485             return;
486         }
487 
488         // Couldn't find an object with that name.  return an error
489         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
490         });
491 
492     getPhysicalSecurityData(asyncResp);
493 }
494 
495 inline void
496     handleChassisPatch(App& app, const crow::Request& req,
497                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
498                        const std::string& param)
499 {
500     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
501     {
502         return;
503     }
504     std::optional<bool> locationIndicatorActive;
505     std::optional<std::string> indicatorLed;
506 
507     if (param.empty())
508     {
509         return;
510     }
511 
512     if (!json_util::readJsonPatch(
513             req, asyncResp->res, "LocationIndicatorActive",
514             locationIndicatorActive, "IndicatorLED", indicatorLed))
515     {
516         return;
517     }
518 
519     // TODO (Gunnar): Remove IndicatorLED after enough time has passed
520     if (!locationIndicatorActive && !indicatorLed)
521     {
522         return; // delete this when we support more patch properties
523     }
524     if (indicatorLed)
525     {
526         asyncResp->res.addHeader(
527             boost::beast::http::field::warning,
528             "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
529     }
530 
531     constexpr std::array<std::string_view, 2> interfaces = {
532         "xyz.openbmc_project.Inventory.Item.Board",
533         "xyz.openbmc_project.Inventory.Item.Chassis"};
534 
535     const std::string& chassisId = param;
536 
537     dbus::utility::getSubTree(
538         "/xyz/openbmc_project/inventory", 0, interfaces,
539         [asyncResp, chassisId, locationIndicatorActive,
540          indicatorLed](const boost::system::error_code& ec,
541                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
542         if (ec)
543         {
544             BMCWEB_LOG_ERROR << "DBUS response error " << ec;
545             messages::internalError(asyncResp->res);
546             return;
547         }
548 
549         // Iterate over all retrieved ObjectPaths.
550         for (const std::pair<
551                  std::string,
552                  std::vector<std::pair<std::string, std::vector<std::string>>>>&
553                  object : subtree)
554         {
555             const std::string& path = object.first;
556             const std::vector<std::pair<std::string, std::vector<std::string>>>&
557                 connectionNames = object.second;
558 
559             sdbusplus::message::object_path objPath(path);
560             if (objPath.filename() != chassisId)
561             {
562                 continue;
563             }
564 
565             if (connectionNames.empty())
566             {
567                 BMCWEB_LOG_ERROR << "Got 0 Connection names";
568                 continue;
569             }
570 
571             const std::vector<std::string>& interfaces3 =
572                 connectionNames[0].second;
573 
574             const std::array<const char*, 2> hasIndicatorLed = {
575                 "xyz.openbmc_project.Inventory.Item.Panel",
576                 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
577             bool indicatorChassis = false;
578             for (const char* interface : hasIndicatorLed)
579             {
580                 if (std::find(interfaces3.begin(), interfaces3.end(),
581                               interface) != interfaces3.end())
582                 {
583                     indicatorChassis = true;
584                     break;
585                 }
586             }
587             if (locationIndicatorActive)
588             {
589                 if (indicatorChassis)
590                 {
591                     setLocationIndicatorActive(asyncResp,
592                                                *locationIndicatorActive);
593                 }
594                 else
595                 {
596                     messages::propertyUnknown(asyncResp->res,
597                                               "LocationIndicatorActive");
598                 }
599             }
600             if (indicatorLed)
601             {
602                 if (indicatorChassis)
603                 {
604                     setIndicatorLedState(asyncResp, *indicatorLed);
605                 }
606                 else
607                 {
608                     messages::propertyUnknown(asyncResp->res, "IndicatorLED");
609                 }
610             }
611             return;
612         }
613 
614         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
615         });
616 }
617 
618 /**
619  * Chassis override class for delivering Chassis Schema
620  * Functions triggers appropriate requests on DBus
621  */
622 inline void requestRoutesChassis(App& app)
623 {
624     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
625         .privileges(redfish::privileges::getChassis)
626         .methods(boost::beast::http::verb::get)(
627             std::bind_front(handleChassisGet, std::ref(app)));
628 
629     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
630         .privileges(redfish::privileges::patchChassis)
631         .methods(boost::beast::http::verb::patch)(
632             std::bind_front(handleChassisPatch, std::ref(app)));
633 }
634 
635 inline void
636     doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
637 {
638     constexpr std::array<std::string_view, 1> interfaces = {
639         "xyz.openbmc_project.State.Chassis"};
640 
641     // Use mapper to get subtree paths.
642     dbus::utility::getSubTreePaths(
643         "/", 0, interfaces,
644         [asyncResp](
645             const boost::system::error_code& ec,
646             const dbus::utility::MapperGetSubTreePathsResponse& chassisList) {
647         if (ec)
648         {
649             BMCWEB_LOG_ERROR << "[mapper] Bad D-Bus request error: " << ec;
650             messages::internalError(asyncResp->res);
651             return;
652         }
653 
654         const char* processName = "xyz.openbmc_project.State.Chassis";
655         const char* interfaceName = "xyz.openbmc_project.State.Chassis";
656         const char* destProperty = "RequestedPowerTransition";
657         const std::string propertyValue =
658             "xyz.openbmc_project.State.Chassis.Transition.PowerCycle";
659         std::string objectPath = "/xyz/openbmc_project/state/chassis_system0";
660 
661         /* Look for system reset chassis path */
662         if ((std::find(chassisList.begin(), chassisList.end(), objectPath)) ==
663             chassisList.end())
664         {
665             /* We prefer to reset the full chassis_system, but if it doesn't
666              * exist on some platforms, fall back to a host-only power reset
667              */
668             objectPath = "/xyz/openbmc_project/state/chassis0";
669         }
670 
671         crow::connections::systemBus->async_method_call(
672             [asyncResp](const boost::system::error_code& ec2) {
673             // Use "Set" method to set the property value.
674             if (ec2)
675             {
676                 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec2;
677                 messages::internalError(asyncResp->res);
678                 return;
679             }
680 
681             messages::success(asyncResp->res);
682             },
683             processName, objectPath, "org.freedesktop.DBus.Properties", "Set",
684             interfaceName, destProperty,
685             dbus::utility::DbusVariantType{propertyValue});
686         });
687 }
688 
689 inline void handleChassisResetActionInfoPost(
690     App& app, const crow::Request& req,
691     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
692     const std::string& /*chassisId*/)
693 {
694     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
695     {
696         return;
697     }
698     BMCWEB_LOG_DEBUG << "Post Chassis Reset.";
699 
700     std::string resetType;
701 
702     if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
703     {
704         return;
705     }
706 
707     if (resetType != "PowerCycle")
708     {
709         BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
710                          << resetType;
711         messages::actionParameterNotSupported(asyncResp->res, resetType,
712                                               "ResetType");
713 
714         return;
715     }
716     doChassisPowerCycle(asyncResp);
717 }
718 
719 /**
720  * ChassisResetAction class supports the POST method for the Reset
721  * action.
722  * Function handles POST method request.
723  * Analyzes POST body before sending Reset request data to D-Bus.
724  */
725 
726 inline void requestRoutesChassisResetAction(App& app)
727 {
728     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/")
729         .privileges(redfish::privileges::postChassis)
730         .methods(boost::beast::http::verb::post)(
731             std::bind_front(handleChassisResetActionInfoPost, std::ref(app)));
732 }
733 
734 inline void handleChassisResetActionInfoGet(
735     App& app, const crow::Request& req,
736     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
737     const std::string& chassisId)
738 {
739     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
740     {
741         return;
742     }
743     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
744     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
745         "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId);
746     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
747 
748     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
749     nlohmann::json::array_t parameters;
750     nlohmann::json::object_t parameter;
751     parameter["Name"] = "ResetType";
752     parameter["Required"] = true;
753     parameter["DataType"] = "String";
754     nlohmann::json::array_t allowed;
755     allowed.emplace_back("PowerCycle");
756     parameter["AllowableValues"] = std::move(allowed);
757     parameters.emplace_back(std::move(parameter));
758 
759     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
760 }
761 
762 /**
763  * ChassisResetActionInfo derived class for delivering Chassis
764  * ResetType AllowableValues using ResetInfo schema.
765  */
766 inline void requestRoutesChassisResetActionInfo(App& app)
767 {
768     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/")
769         .privileges(redfish::privileges::getActionInfo)
770         .methods(boost::beast::http::verb::get)(
771             std::bind_front(handleChassisResetActionInfoGet, std::ref(app)));
772 }
773 
774 } // namespace redfish
775