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