xref: /openbmc/bmcweb/redfish-core/lib/chassis.hpp (revision 83237dd618c38a54aaa91b9fe4968eeb454a5dfa)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "bmcweb_config.h"
7 
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_singleton.hpp"
11 #include "dbus_utility.hpp"
12 #include "error_messages.hpp"
13 #include "generated/enums/action_info.hpp"
14 #include "generated/enums/chassis.hpp"
15 #include "generated/enums/resource.hpp"
16 #include "http_request.hpp"
17 #include "led.hpp"
18 #include "logging.hpp"
19 #include "query.hpp"
20 #include "registries/privilege_registry.hpp"
21 #include "utils/asset_utils.hpp"
22 #include "utils/chassis_utils.hpp"
23 #include "utils/collection.hpp"
24 #include "utils/dbus_utils.hpp"
25 #include "utils/json_utils.hpp"
26 
27 #include <asm-generic/errno.h>
28 
29 #include <boost/beast/http/field.hpp>
30 #include <boost/beast/http/verb.hpp>
31 #include <boost/system/error_code.hpp>
32 #include <boost/url/format.hpp>
33 #include <boost/url/url.hpp>
34 #include <nlohmann/json.hpp>
35 #include <sdbusplus/message/native_types.hpp>
36 #include <sdbusplus/unpack_properties.hpp>
37 
38 #include <algorithm>
39 #include <array>
40 #include <format>
41 #include <functional>
42 #include <memory>
43 #include <optional>
44 #include <ranges>
45 #include <string>
46 #include <string_view>
47 #include <utility>
48 #include <vector>
49 
50 namespace redfish
51 {
52 
translateChassisTypeToRedfish(const std::string_view & chassisType)53 inline chassis::ChassisType translateChassisTypeToRedfish(
54     const std::string_view& chassisType)
55 {
56     if (chassisType ==
57         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Blade")
58     {
59         return chassis::ChassisType::Blade;
60     }
61     if (chassisType ==
62         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component")
63     {
64         return chassis::ChassisType::Component;
65     }
66     if (chassisType ==
67         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure")
68     {
69         return chassis::ChassisType::Enclosure;
70     }
71     if (chassisType ==
72         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module")
73     {
74         return chassis::ChassisType::Module;
75     }
76     if (chassisType ==
77         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount")
78     {
79         return chassis::ChassisType::RackMount;
80     }
81     if (chassisType ==
82         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone")
83     {
84         return chassis::ChassisType::StandAlone;
85     }
86     if (chassisType ==
87         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StorageEnclosure")
88     {
89         return chassis::ChassisType::StorageEnclosure;
90     }
91     if (chassisType ==
92         "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone")
93     {
94         return chassis::ChassisType::Zone;
95     }
96     return chassis::ChassisType::Invalid;
97 }
98 
99 /**
100  * @brief Retrieves resources over dbus to link to the chassis
101  *
102  * @param[in] asyncResp  - Shared pointer for completing asynchronous
103  * calls
104  * @param[in] path       - Chassis dbus path to look for the storage.
105  *
106  * Calls the Association endpoints on the path + "/storage" and add the link of
107  * json["Links"]["Storage@odata.count"] =
108  *    {"@odata.id", "/redfish/v1/Storage/" + resourceId}
109  *
110  * @return None.
111  */
getStorageLink(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const sdbusplus::message::object_path & path)112 inline void getStorageLink(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
113                            const sdbusplus::message::object_path& path)
114 {
115     dbus::utility::getProperty<std::vector<std::string>>(
116         "xyz.openbmc_project.ObjectMapper", (path / "storage").str,
117         "xyz.openbmc_project.Association", "endpoints",
118         [asyncResp](const boost::system::error_code& ec,
119                     const std::vector<std::string>& storageList) {
120             if (ec)
121             {
122                 BMCWEB_LOG_DEBUG("getStorageLink got DBUS response error");
123                 return;
124             }
125 
126             nlohmann::json::array_t storages;
127             for (const std::string& storagePath : storageList)
128             {
129                 std::string id =
130                     sdbusplus::message::object_path(storagePath).filename();
131                 if (id.empty())
132                 {
133                     continue;
134                 }
135 
136                 nlohmann::json::object_t storage;
137                 storage["@odata.id"] =
138                     boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
139                                         BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
140                 storages.emplace_back(std::move(storage));
141             }
142             asyncResp->res.jsonValue["Links"]["Storage@odata.count"] =
143                 storages.size();
144             asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages);
145         });
146 }
147 
148 /**
149  * @brief Retrieves chassis state properties over dbus
150  *
151  * @param[in] asyncResp - Shared pointer for completing asynchronous calls.
152  *
153  * @return None.
154  */
getChassisState(std::shared_ptr<bmcweb::AsyncResp> asyncResp)155 inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> asyncResp)
156 {
157     dbus::utility::getProperty<std::string>(
158         "xyz.openbmc_project.State.Chassis",
159         "/xyz/openbmc_project/state/chassis0",
160         "xyz.openbmc_project.State.Chassis", "CurrentPowerState",
161         [asyncResp{std::move(asyncResp)}](const boost::system::error_code& ec,
162                                           const std::string& chassisState) {
163             if (ec)
164             {
165                 if (ec == boost::system::errc::host_unreachable)
166                 {
167                     // Service not available, no error, just don't return
168                     // chassis state info
169                     BMCWEB_LOG_DEBUG("Service not available {}", ec);
170                     return;
171                 }
172                 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
173                 messages::internalError(asyncResp->res);
174                 return;
175             }
176 
177             BMCWEB_LOG_DEBUG("Chassis state: {}", chassisState);
178             // Verify Chassis State
179             if (chassisState ==
180                 "xyz.openbmc_project.State.Chassis.PowerState.On")
181             {
182                 asyncResp->res.jsonValue["PowerState"] =
183                     resource::PowerState::On;
184                 asyncResp->res.jsonValue["Status"]["State"] =
185                     resource::State::Enabled;
186             }
187             else if (chassisState ==
188                      "xyz.openbmc_project.State.Chassis.PowerState.Off")
189             {
190                 asyncResp->res.jsonValue["PowerState"] =
191                     resource::PowerState::Off;
192                 asyncResp->res.jsonValue["Status"]["State"] =
193                     resource::State::StandbyOffline;
194             }
195         });
196 }
197 
198 /**
199  * Retrieves physical security properties over dbus
200  */
handlePhysicalSecurityGetSubTree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)201 inline void handlePhysicalSecurityGetSubTree(
202     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
203     const boost::system::error_code& ec,
204     const dbus::utility::MapperGetSubTreeResponse& subtree)
205 {
206     if (ec)
207     {
208         // do not add err msg in redfish response, because this is not
209         //     mandatory property
210         BMCWEB_LOG_INFO("DBUS error: no matched iface {}", ec);
211         return;
212     }
213     // Iterate over all retrieved ObjectPaths.
214     for (const auto& object : subtree)
215     {
216         if (!object.second.empty())
217         {
218             const auto& service = object.second.front();
219 
220             BMCWEB_LOG_DEBUG("Get intrusion status by service ");
221 
222             dbus::utility::getProperty<std::string>(
223                 service.first, object.first,
224                 "xyz.openbmc_project.Chassis.Intrusion", "Status",
225                 [asyncResp](const boost::system::error_code& ec1,
226                             const std::string& value) {
227                     if (ec1)
228                     {
229                         // do not add err msg in redfish response, because this
230                         // is not
231                         //     mandatory property
232                         BMCWEB_LOG_ERROR("DBUS response error {}", ec1);
233                         return;
234                     }
235                     asyncResp->res.jsonValue["PhysicalSecurity"]
236                                             ["IntrusionSensorNumber"] = 1;
237                     asyncResp->res
238                         .jsonValue["PhysicalSecurity"]["IntrusionSensor"] =
239                         value;
240                 });
241 
242             return;
243         }
244     }
245 }
246 
handleChassisCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)247 inline void handleChassisCollectionGet(
248     App& app, const crow::Request& req,
249     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
250 {
251     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
252     {
253         return;
254     }
255     asyncResp->res.jsonValue["@odata.type"] =
256         "#ChassisCollection.ChassisCollection";
257     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
258     asyncResp->res.jsonValue["Name"] = "Chassis Collection";
259 
260     collection_util::getCollectionMembers(
261         asyncResp, boost::urls::url("/redfish/v1/Chassis"), chassisInterfaces,
262         "/xyz/openbmc_project/inventory");
263 }
264 
getChassisContainedBy(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & upstreamChassisPaths)265 inline void getChassisContainedBy(
266     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
267     const std::string& chassisId, const boost::system::error_code& ec,
268     const dbus::utility::MapperGetSubTreePathsResponse& upstreamChassisPaths)
269 {
270     if (ec)
271     {
272         if (ec.value() != EBADR)
273         {
274             BMCWEB_LOG_ERROR("DBUS response error {}", ec);
275             messages::internalError(asyncResp->res);
276         }
277         return;
278     }
279     if (upstreamChassisPaths.empty())
280     {
281         return;
282     }
283     if (upstreamChassisPaths.size() > 1)
284     {
285         BMCWEB_LOG_ERROR("{} is contained by multiple chassis", chassisId);
286         messages::internalError(asyncResp->res);
287         return;
288     }
289 
290     sdbusplus::message::object_path upstreamChassisPath(
291         upstreamChassisPaths[0]);
292     std::string upstreamChassis = upstreamChassisPath.filename();
293     if (upstreamChassis.empty())
294     {
295         BMCWEB_LOG_WARNING("Malformed upstream Chassis path {} on {}",
296                            upstreamChassisPath.str, chassisId);
297         return;
298     }
299 
300     asyncResp->res.jsonValue["Links"]["ContainedBy"]["@odata.id"] =
301         boost::urls::format("/redfish/v1/Chassis/{}", upstreamChassis);
302 }
303 
getChassisContains(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & downstreamChassisPaths)304 inline void getChassisContains(
305     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
306     const std::string& chassisId, const boost::system::error_code& ec,
307     const dbus::utility::MapperGetSubTreePathsResponse& downstreamChassisPaths)
308 {
309     if (ec)
310     {
311         if (ec.value() != EBADR)
312         {
313             BMCWEB_LOG_ERROR("DBUS response error {}", ec);
314             messages::internalError(asyncResp->res);
315         }
316         return;
317     }
318     if (downstreamChassisPaths.empty())
319     {
320         return;
321     }
322     nlohmann::json& jValue = asyncResp->res.jsonValue["Links"]["Contains"];
323     if (!jValue.is_array())
324     {
325         // Create the array if it was empty
326         jValue = nlohmann::json::array();
327     }
328     for (const auto& p : downstreamChassisPaths)
329     {
330         sdbusplus::message::object_path downstreamChassisPath(p);
331         std::string downstreamChassis = downstreamChassisPath.filename();
332         if (downstreamChassis.empty())
333         {
334             BMCWEB_LOG_WARNING("Malformed downstream Chassis path {} on {}",
335                                downstreamChassisPath.str, chassisId);
336             continue;
337         }
338         nlohmann::json link;
339         link["@odata.id"] =
340             boost::urls::format("/redfish/v1/Chassis/{}", downstreamChassis);
341         jValue.push_back(std::move(link));
342     }
343     asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = jValue.size();
344 }
345 
getChassisConnectivity(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & chassisPath)346 inline void getChassisConnectivity(
347     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
348     const std::string& chassisId, const std::string& chassisPath)
349 {
350     BMCWEB_LOG_DEBUG("Get chassis connectivity");
351 
352     dbus::utility::getAssociatedSubTreePaths(
353         chassisPath + "/contained_by",
354         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
355         chassisInterfaces,
356         std::bind_front(getChassisContainedBy, asyncResp, chassisId));
357 
358     dbus::utility::getAssociatedSubTreePaths(
359         chassisPath + "/containing",
360         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
361         chassisInterfaces,
362         std::bind_front(getChassisContains, asyncResp, chassisId));
363 }
364 
365 /**
366  * ChassisCollection derived class for delivering Chassis Collection Schema
367  *  Functions triggers appropriate requests on DBus
368  */
requestRoutesChassisCollection(App & app)369 inline void requestRoutesChassisCollection(App& app)
370 {
371     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/")
372         .privileges(redfish::privileges::getChassisCollection)
373         .methods(boost::beast::http::verb::get)(
374             std::bind_front(handleChassisCollectionGet, std::ref(app)));
375 }
376 
getChassisLocationCode(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)377 inline void getChassisLocationCode(
378     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
379     const std::string& connectionName, const std::string& path)
380 {
381     dbus::utility::getProperty<std::string>(
382         connectionName, path,
383         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
384         [asyncResp](const boost::system::error_code& ec,
385                     const std::string& property) {
386             if (ec)
387             {
388                 BMCWEB_LOG_ERROR("DBUS response error for Location");
389                 messages::internalError(asyncResp->res);
390                 return;
391             }
392 
393             asyncResp->res
394                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
395                 property;
396         });
397 }
398 
getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)399 inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
400                            const std::string& connectionName,
401                            const std::string& path)
402 {
403     dbus::utility::getProperty<std::string>(
404         connectionName, path, "xyz.openbmc_project.Common.UUID", "UUID",
405         [asyncResp](const boost::system::error_code& ec,
406                     const std::string& chassisUUID) {
407             if (ec)
408             {
409                 BMCWEB_LOG_ERROR("DBUS response error for UUID");
410                 messages::internalError(asyncResp->res);
411                 return;
412             }
413             asyncResp->res.jsonValue["UUID"] = chassisUUID;
414         });
415 }
416 
handleDecoratorAssetProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & path,const dbus::utility::DBusPropertiesMap & propertiesList)417 inline void handleDecoratorAssetProperties(
418     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
419     const std::string& chassisId, const std::string& path,
420     const dbus::utility::DBusPropertiesMap& propertiesList)
421 {
422     asset_utils::extractAssetInfo(asyncResp, ""_json_pointer, propertiesList,
423                                   true);
424 
425     asyncResp->res.jsonValue["Name"] = chassisId;
426     asyncResp->res.jsonValue["Id"] = chassisId;
427 
428     if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_POWER_THERMAL)
429     {
430         asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
431             boost::urls::format("/redfish/v1/Chassis/{}/Thermal", chassisId);
432         // Power object
433         asyncResp->res.jsonValue["Power"]["@odata.id"] =
434             boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId);
435     }
436 
437     if constexpr (BMCWEB_REDFISH_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM)
438     {
439         asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] =
440             boost::urls::format("/redfish/v1/Chassis/{}/ThermalSubsystem",
441                                 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("/redfish/v1/Chassis/{}/EnvironmentMetrics",
447                                 chassisId);
448     }
449 
450     asyncResp->res.jsonValue["Assembly"]["@odata.id"] =
451         boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisId);
452 
453     // SensorCollection
454     asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
455         boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId);
456     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
457 
458     nlohmann::json::array_t computerSystems;
459     nlohmann::json::object_t system;
460     system["@odata.id"] =
461         std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
462     computerSystems.emplace_back(std::move(system));
463     asyncResp->res.jsonValue["Links"]["ComputerSystems"] =
464         std::move(computerSystems);
465 
466     nlohmann::json::array_t managedBy;
467     nlohmann::json::object_t manager;
468     manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}",
469                                                BMCWEB_REDFISH_MANAGER_URI_NAME);
470     managedBy.emplace_back(std::move(manager));
471     asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy);
472     getChassisState(asyncResp);
473     getStorageLink(asyncResp, path);
474 }
475 
handleChassisProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & propertiesList)476 inline void handleChassisProperties(
477     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
478     const dbus::utility::DBusPropertiesMap& propertiesList)
479 {
480     const std::string* type = nullptr;
481 
482     const bool success = sdbusplus::unpackPropertiesNoThrow(
483         dbus_utils::UnpackErrorPrinter(), propertiesList, "Type", type);
484 
485     if (!success)
486     {
487         messages::internalError(asyncResp->res);
488         return;
489     }
490 
491     // Chassis Type is a required property in Redfish
492     // If there is an error or some enum we don't support just sit it to Rack
493     // Mount
494     asyncResp->res.jsonValue["ChassisType"] = chassis::ChassisType::RackMount;
495 
496     if (type != nullptr)
497     {
498         auto chassisType = translateChassisTypeToRedfish(*type);
499         if (chassisType != chassis::ChassisType::Invalid)
500         {
501             asyncResp->res.jsonValue["ChassisType"] = chassisType;
502         }
503     }
504 }
505 
handleChassisGetSubTree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)506 inline void handleChassisGetSubTree(
507     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
508     const std::string& chassisId, const boost::system::error_code& ec,
509     const dbus::utility::MapperGetSubTreeResponse& subtree)
510 {
511     if (ec)
512     {
513         BMCWEB_LOG_ERROR("DBUS response error {}", ec);
514         messages::internalError(asyncResp->res);
515         return;
516     }
517     // Iterate over all retrieved ObjectPaths.
518     for (const std::pair<
519              std::string,
520              std::vector<std::pair<std::string, std::vector<std::string>>>>&
521              object : subtree)
522     {
523         const std::string& path = object.first;
524         const std::vector<std::pair<std::string, std::vector<std::string>>>&
525             connectionNames = object.second;
526 
527         sdbusplus::message::object_path objPath(path);
528         if (objPath.filename() != chassisId)
529         {
530             continue;
531         }
532 
533         getChassisConnectivity(asyncResp, chassisId, path);
534 
535         if (connectionNames.empty())
536         {
537             BMCWEB_LOG_ERROR("Got 0 Connection names");
538             continue;
539         }
540 
541         asyncResp->res.jsonValue["@odata.type"] = "#Chassis.v1_22_0.Chassis";
542         asyncResp->res.jsonValue["@odata.id"] =
543             boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
544         asyncResp->res.jsonValue["Name"] = "Chassis Collection";
545         asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
546             boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset",
547                                 chassisId);
548         asyncResp->res
549             .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] =
550             boost::urls::format("/redfish/v1/Chassis/{}/ResetActionInfo",
551                                 chassisId);
552         dbus::utility::getAssociationEndPoints(
553             path + "/drive",
554             [asyncResp, chassisId](const boost::system::error_code& ec3,
555                                    const dbus::utility::MapperEndPoints& resp) {
556                 if (ec3 || resp.empty())
557                 {
558                     return; // no drives = no failures
559                 }
560 
561                 nlohmann::json reference;
562                 reference["@odata.id"] = boost::urls::format(
563                     "/redfish/v1/Chassis/{}/Drives", chassisId);
564                 asyncResp->res.jsonValue["Drives"] = std::move(reference);
565             });
566 
567         const std::string& connectionName = connectionNames[0].first;
568 
569         const std::vector<std::string>& interfaces2 = connectionNames[0].second;
570         const std::array<const char*, 3> hasIndicatorLed = {
571             "xyz.openbmc_project.Inventory.Item.Chassis",
572             "xyz.openbmc_project.Inventory.Item.Panel",
573             "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
574 
575         const std::string assetTagInterface =
576             "xyz.openbmc_project.Inventory.Decorator.AssetTag";
577         const std::string replaceableInterface =
578             "xyz.openbmc_project.Inventory.Decorator.Replaceable";
579         const std::string revisionInterface =
580             "xyz.openbmc_project.Inventory.Decorator.Revision";
581         for (const auto& interface : interfaces2)
582         {
583             if (interface == assetTagInterface)
584             {
585                 dbus::utility::getProperty<std::string>(
586                     connectionName, path, assetTagInterface, "AssetTag",
587                     [asyncResp, chassisId](const boost::system::error_code& ec2,
588                                            const std::string& property) {
589                         if (ec2)
590                         {
591                             BMCWEB_LOG_ERROR(
592                                 "DBus response error for AssetTag: {}", ec2);
593                             messages::internalError(asyncResp->res);
594                             return;
595                         }
596                         asyncResp->res.jsonValue["AssetTag"] = property;
597                     });
598             }
599             else if (interface == replaceableInterface)
600             {
601                 dbus::utility::getProperty<bool>(
602                     connectionName, path, replaceableInterface, "HotPluggable",
603                     [asyncResp, chassisId](const boost::system::error_code& ec2,
604                                            const bool property) {
605                         if (ec2)
606                         {
607                             BMCWEB_LOG_ERROR(
608                                 "DBus response error for HotPluggable: {}",
609                                 ec2);
610                             messages::internalError(asyncResp->res);
611                             return;
612                         }
613                         asyncResp->res.jsonValue["HotPluggable"] = property;
614                     });
615             }
616             else if (interface == revisionInterface)
617             {
618                 dbus::utility::getProperty<std::string>(
619                     connectionName, path, revisionInterface, "Version",
620                     [asyncResp, chassisId](const boost::system::error_code& ec2,
621                                            const std::string& property) {
622                         if (ec2)
623                         {
624                             BMCWEB_LOG_ERROR(
625                                 "DBus response error for Version: {}", ec2);
626                             messages::internalError(asyncResp->res);
627                             return;
628                         }
629                         asyncResp->res.jsonValue["Version"] = property;
630                     });
631             }
632         }
633 
634         for (const char* interface : hasIndicatorLed)
635         {
636             if (std::ranges::find(interfaces2, interface) != interfaces2.end())
637             {
638                 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED)
639                 {
640                     getIndicatorLedState(asyncResp);
641                 }
642                 getLocationIndicatorActive(asyncResp, objPath);
643                 break;
644             }
645         }
646 
647         dbus::utility::getAllProperties(
648             *crow::connections::systemBus, connectionName, path,
649             "xyz.openbmc_project.Inventory.Decorator.Asset",
650             [asyncResp, chassisId,
651              path](const boost::system::error_code&,
652                    const dbus::utility::DBusPropertiesMap& propertiesList) {
653                 handleDecoratorAssetProperties(asyncResp, chassisId, path,
654                                                propertiesList);
655             });
656 
657         dbus::utility::getAllProperties(
658             *crow::connections::systemBus, connectionName, path,
659             "xyz.openbmc_project.Inventory.Item.Chassis",
660             [asyncResp](
661                 const boost::system::error_code&,
662                 const dbus::utility::DBusPropertiesMap& propertiesList) {
663                 handleChassisProperties(asyncResp, propertiesList);
664             });
665 
666         for (const auto& interface : interfaces2)
667         {
668             if (interface == "xyz.openbmc_project.Common.UUID")
669             {
670                 getChassisUUID(asyncResp, connectionName, path);
671             }
672             else if (interface ==
673                      "xyz.openbmc_project.Inventory.Decorator.LocationCode")
674             {
675                 getChassisLocationCode(asyncResp, connectionName, path);
676             }
677         }
678 
679         return;
680     }
681 
682     // Couldn't find an object with that name.  return an error
683     messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
684 }
685 
handleChassisGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)686 inline void handleChassisGet(
687     App& app, const crow::Request& req,
688     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
689     const std::string& chassisId)
690 {
691     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
692     {
693         return;
694     }
695 
696     dbus::utility::getSubTree(
697         "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
698         std::bind_front(handleChassisGetSubTree, asyncResp, chassisId));
699 
700     constexpr std::array<std::string_view, 1> interfaces2 = {
701         "xyz.openbmc_project.Chassis.Intrusion"};
702 
703     dbus::utility::getSubTree(
704         "/xyz/openbmc_project", 0, interfaces2,
705         std::bind_front(handlePhysicalSecurityGetSubTree, asyncResp));
706 }
707 
handleChassisPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param)708 inline void handleChassisPatch(
709     App& app, const crow::Request& req,
710     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
711     const std::string& param)
712 {
713     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
714     {
715         return;
716     }
717     std::optional<bool> locationIndicatorActive;
718     std::optional<std::string> indicatorLed;
719 
720     if (param.empty())
721     {
722         return;
723     }
724 
725     if (!json_util::readJsonPatch(                             //
726             req, asyncResp->res,                               //
727             "IndicatorLED", indicatorLed,                      //
728             "LocationIndicatorActive", locationIndicatorActive //
729             ))
730     {
731         return;
732     }
733 
734     if (!locationIndicatorActive && !indicatorLed)
735     {
736         return; // delete this when we support more patch properties
737     }
738     if (indicatorLed)
739     {
740         if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED)
741         {
742             asyncResp->res.addHeader(
743                 boost::beast::http::field::warning,
744                 "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
745         }
746         else
747         {
748             messages::propertyUnknown(asyncResp->res, "IndicatorLED");
749             return;
750         }
751     }
752 
753     const std::string& chassisId = param;
754 
755     dbus::utility::getSubTree(
756         "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
757         [asyncResp, chassisId, locationIndicatorActive,
758          indicatorLed](const boost::system::error_code& ec,
759                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
760             if (ec)
761             {
762                 BMCWEB_LOG_ERROR("DBUS response error {}", ec);
763                 messages::internalError(asyncResp->res);
764                 return;
765             }
766 
767             // Iterate over all retrieved ObjectPaths.
768             for (const std::pair<std::string,
769                                  std::vector<std::pair<
770                                      std::string, std::vector<std::string>>>>&
771                      object : subtree)
772             {
773                 const std::string& path = object.first;
774                 const std::vector<
775                     std::pair<std::string, std::vector<std::string>>>&
776                     connectionNames = object.second;
777 
778                 sdbusplus::message::object_path objPath(path);
779                 if (objPath.filename() != chassisId)
780                 {
781                     continue;
782                 }
783 
784                 if (connectionNames.empty())
785                 {
786                     BMCWEB_LOG_ERROR("Got 0 Connection names");
787                     continue;
788                 }
789 
790                 const std::vector<std::string>& interfaces3 =
791                     connectionNames[0].second;
792 
793                 const std::array<const char*, 3> hasIndicatorLed = {
794                     "xyz.openbmc_project.Inventory.Item.Chassis",
795                     "xyz.openbmc_project.Inventory.Item.Panel",
796                     "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
797                 bool indicatorChassis = false;
798                 for (const char* interface : hasIndicatorLed)
799                 {
800                     if (std::ranges::find(interfaces3, interface) !=
801                         interfaces3.end())
802                     {
803                         indicatorChassis = true;
804                         break;
805                     }
806                 }
807                 if (locationIndicatorActive)
808                 {
809                     if (indicatorChassis)
810                     {
811                         setLocationIndicatorActive(asyncResp, path,
812                                                    *locationIndicatorActive);
813                     }
814                     else
815                     {
816                         messages::propertyUnknown(asyncResp->res,
817                                                   "LocationIndicatorActive");
818                     }
819                 }
820                 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED)
821                 {
822                     if (indicatorLed)
823                     {
824                         if (indicatorChassis)
825                         {
826                             setIndicatorLedState(asyncResp, *indicatorLed);
827                         }
828                         else
829                         {
830                             messages::propertyUnknown(asyncResp->res,
831                                                       "IndicatorLED");
832                         }
833                     }
834                 }
835                 return;
836             }
837 
838             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
839         });
840 }
841 
842 /**
843  * Chassis override class for delivering Chassis Schema
844  * Functions triggers appropriate requests on DBus
845  */
requestRoutesChassis(App & app)846 inline void requestRoutesChassis(App& app)
847 {
848     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
849         .privileges(redfish::privileges::getChassis)
850         .methods(boost::beast::http::verb::get)(
851             std::bind_front(handleChassisGet, std::ref(app)));
852 
853     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
854         .privileges(redfish::privileges::patchChassis)
855         .methods(boost::beast::http::verb::patch)(
856             std::bind_front(handleChassisPatch, std::ref(app)));
857 }
858 
doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)859 inline void doChassisPowerCycle(
860     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
861 {
862     constexpr std::array<std::string_view, 1> interfaces = {
863         "xyz.openbmc_project.State.Chassis"};
864 
865     // Use mapper to get subtree paths.
866     dbus::utility::getSubTreePaths(
867         "/", 0, interfaces,
868         [asyncResp](
869             const boost::system::error_code& ec,
870             const dbus::utility::MapperGetSubTreePathsResponse& chassisList) {
871             if (ec)
872             {
873                 BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec);
874                 messages::internalError(asyncResp->res);
875                 return;
876             }
877 
878             const char* processName = "xyz.openbmc_project.State.Chassis";
879             const char* interfaceName = "xyz.openbmc_project.State.Chassis";
880             const char* destProperty = "RequestedPowerTransition";
881             const std::string propertyValue =
882                 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle";
883             std::string objectPath =
884                 "/xyz/openbmc_project/state/chassis_system0";
885 
886             /* Look for system reset chassis path */
887             if ((std::ranges::find(chassisList, objectPath)) ==
888                 chassisList.end())
889             {
890                 /* We prefer to reset the full chassis_system, but if it doesn't
891                  * exist on some platforms, fall back to a host-only power reset
892                  */
893                 objectPath = "/xyz/openbmc_project/state/chassis0";
894             }
895 
896             setDbusProperty(asyncResp, "ResetType", processName, objectPath,
897                             interfaceName, destProperty, propertyValue);
898         });
899 }
900 
handleChassisResetActionInfoPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)901 inline void handleChassisResetActionInfoPost(
902     App& app, const crow::Request& req,
903     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
904     const std::string& /*chassisId*/)
905 {
906     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
907     {
908         return;
909     }
910     BMCWEB_LOG_DEBUG("Post Chassis Reset.");
911 
912     std::string resetType;
913 
914     if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
915     {
916         return;
917     }
918 
919     if (resetType != "PowerCycle")
920     {
921         BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType);
922         messages::actionParameterNotSupported(asyncResp->res, resetType,
923                                               "ResetType");
924 
925         return;
926     }
927     doChassisPowerCycle(asyncResp);
928 }
929 
930 /**
931  * ChassisResetAction class supports the POST method for the Reset
932  * action.
933  * Function handles POST method request.
934  * Analyzes POST body before sending Reset request data to D-Bus.
935  */
936 
requestRoutesChassisResetAction(App & app)937 inline void requestRoutesChassisResetAction(App& app)
938 {
939     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/")
940         .privileges(redfish::privileges::postChassis)
941         .methods(boost::beast::http::verb::post)(
942             std::bind_front(handleChassisResetActionInfoPost, std::ref(app)));
943 }
944 
handleChassisResetActionInfoGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)945 inline void handleChassisResetActionInfoGet(
946     App& app, const crow::Request& req,
947     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
948     const std::string& chassisId)
949 {
950     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
951     {
952         return;
953     }
954     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
955     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
956         "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId);
957     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
958 
959     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
960     nlohmann::json::array_t parameters;
961     nlohmann::json::object_t parameter;
962     parameter["Name"] = "ResetType";
963     parameter["Required"] = true;
964     parameter["DataType"] = action_info::ParameterTypes::String;
965     nlohmann::json::array_t allowed;
966     allowed.emplace_back("PowerCycle");
967     parameter["AllowableValues"] = std::move(allowed);
968     parameters.emplace_back(std::move(parameter));
969 
970     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
971 }
972 
973 /**
974  * ChassisResetActionInfo derived class for delivering Chassis
975  * ResetType AllowableValues using ResetInfo schema.
976  */
requestRoutesChassisResetActionInfo(App & app)977 inline void requestRoutesChassisResetActionInfo(App& app)
978 {
979     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/")
980         .privileges(redfish::privileges::getActionInfo)
981         .methods(boost::beast::http::verb::get)(
982             std::bind_front(handleChassisResetActionInfoGet, std::ref(app)));
983 }
984 
985 } // namespace redfish
986