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