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