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