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