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