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