|  | /* | 
|  | // Copyright (c) 2018 Intel Corporation | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | */ | 
|  | #pragma once | 
|  | #include "bmcweb_config.h" | 
|  |  | 
|  | #include "app.hpp" | 
|  | #include "assembly.hpp" | 
|  | #include "dbus_utility.hpp" | 
|  | #include "health.hpp" | 
|  | #include "led.hpp" | 
|  | #include "managed_store.hpp" | 
|  | #include "query.hpp" | 
|  | #include "registries/privilege_registry.hpp" | 
|  | #include "utils/chassis_utils.hpp" | 
|  | #include "utils/collection.hpp" | 
|  | #include "utils/dbus_utils.hpp" | 
|  | #include "utils/json_utils.hpp" | 
|  | #include "utils/storage_utils.hpp" | 
|  | #include "utils/system_utils.hpp" | 
|  |  | 
|  | #include <boost/system/error_code.hpp> | 
|  | #include <sdbusplus/asio/property.hpp> | 
|  | #include <sdbusplus/unpack_properties.hpp> | 
|  | #include <utils/location_utils.hpp> | 
|  |  | 
|  | #include <array> | 
|  | #include <functional> | 
|  | #include <string_view> | 
|  |  | 
|  | #ifdef UNIT_TEST_BUILD | 
|  | #include "test/g3/mock_managed_store.hpp" // NOLINT | 
|  | #endif | 
|  |  | 
|  | namespace redfish | 
|  | { | 
|  |  | 
|  | constexpr int KMAX_RESET_DELAY_SEC = 3600; | 
|  |  | 
|  | /** | 
|  | * @brief Retrieves resources over dbus to link to the chassis | 
|  | * | 
|  | * @param[in] asyncResp  - Shared pointer for completing asynchronous | 
|  | * calls | 
|  | * @param[in] path       - Chassis dbus path to look for the storage. | 
|  | * | 
|  | * Calls the Association endpoints on the path + "/storage" and add the link of | 
|  | * json["Links"]["Storage@odata.count"] = | 
|  | *    {"@odata.id", "/redfish/v1/Storage/" + resourceId} | 
|  | * | 
|  | * @return None. | 
|  | */ | 
|  | inline void getStorageLinkFromChassis( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const sdbusplus::message::object_path& path) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::vector<std::string>>( | 
|  | "xyz.openbmc_project.ObjectMapper", (path / "storage").str, | 
|  | "xyz.openbmc_project.Association", "endpoints", context, | 
|  | [asyncResp](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& storageList) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "getStorageLink got DBUS response error"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | nlohmann::json::array_t storages; | 
|  | asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = | 
|  | storages.size(); | 
|  | asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages); | 
|  |  | 
|  | for (const std::string& storagePath : storageList) | 
|  | { | 
|  | std::string id = | 
|  | sdbusplus::message::object_path(storagePath).filename(); | 
|  | if (id.empty()) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | storage_utils::getSystemPathFromStorage( | 
|  | asyncResp, storagePath, | 
|  | [asyncResp, id](std::optional<std::string_view> systemPath) { | 
|  | std::string systemName = | 
|  | systemPath ? std::filesystem::path(*systemPath).filename() | 
|  | : "system"; | 
|  |  | 
|  | asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = | 
|  | asyncResp->res.jsonValue["Links"]["Storage@odata.count"] | 
|  | .get<std::size_t>() + | 
|  | 1; | 
|  |  | 
|  | nlohmann::json::object_t storage; | 
|  | storage["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Systems", systemName, "Storage", id); | 
|  | asyncResp->res.jsonValue["Links"]["Storage"].emplace_back( | 
|  | std::move(storage)); | 
|  | }); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief Retrieves chassis state properties over dbus | 
|  | * | 
|  | * @param[in] aResp - Shared pointer for completing asynchronous calls. | 
|  | * | 
|  | * @return None. | 
|  | */ | 
|  | inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> aResp) | 
|  | { | 
|  | constexpr std::array<std::string_view, 1> interfaces = { | 
|  | "xyz.openbmc_project.State.Chassis"}; | 
|  |  | 
|  | // Use mapper to get subtree paths. | 
|  | managedStore::ManagedObjectStoreContext requestContext(aResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTreePaths( | 
|  | "/", 0, interfaces, requestContext, | 
|  | [aResp{std::move(aResp)}]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreePathsResponse& chassisList) mutable { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "[mapper] Bad D-Bus request error: " << ec; | 
|  | messages::internalError(aResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::string objectPath = "/xyz/openbmc_project/state/chassis0"; | 
|  |  | 
|  | /* Look for system reset chassis path */ | 
|  | if ((std::find(chassisList.begin(), chassisList.end(), objectPath)) == | 
|  | chassisList.end()) | 
|  | { | 
|  | /* | 
|  | * The default on systems is chassis0 for the power state. | 
|  | * On systems that dont have chassis0, we can use chassis_system0 | 
|  | */ | 
|  | objectPath = "/xyz/openbmc_project/state/chassis_system0"; | 
|  | } | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(aResp); | 
|  | dbus_utils::getProperty<std::string>( | 
|  | "xyz.openbmc_project.State.Chassis", objectPath, | 
|  | "xyz.openbmc_project.State.Chassis", "CurrentPowerState", context, | 
|  | [aResp{std::move(aResp)}](const boost::system::error_code& ec2, | 
|  | const std::string& chassisState) { | 
|  | if (ec2) | 
|  | { | 
|  | if (ec2 == boost::system::errc::host_unreachable) | 
|  | { | 
|  | // Service not available, no error, just don't return | 
|  | // chassis state info | 
|  | BMCWEB_LOG_DEBUG << "Service not available " << ec2; | 
|  | return; | 
|  | } | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | 
|  | messages::internalError(aResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | BMCWEB_LOG_DEBUG << "Chassis state: " << chassisState; | 
|  | // Verify Chassis State | 
|  | if (chassisState == | 
|  | "xyz.openbmc_project.State.Chassis.PowerState.On") | 
|  | { | 
|  | aResp->res.jsonValue["PowerState"] = "On"; | 
|  | aResp->res.jsonValue["Status"]["State"] = "Enabled"; | 
|  | } | 
|  | else if (chassisState == | 
|  | "xyz.openbmc_project.State.Chassis.PowerState.Off") | 
|  | { | 
|  | aResp->res.jsonValue["PowerState"] = "Off"; | 
|  | aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void getIntrusionByService(std::shared_ptr<bmcweb::AsyncResp> aResp, | 
|  | const std::string& service, | 
|  | const std::string& objPath) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Get intrusion status by service \n"; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(aResp); | 
|  | dbus_utils::getProperty<std::string>( | 
|  | service, objPath, "xyz.openbmc_project.Chassis.Intrusion", "Status", | 
|  | context, | 
|  | [aResp{std::move(aResp)}](const boost::system::error_code& ec, | 
|  | const std::string& value) { | 
|  | if (ec) | 
|  | { | 
|  | // do not add err msg in redfish response, because this is not | 
|  | //     mandatory property | 
|  | BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | aResp->res.jsonValue["PhysicalSecurity"]["IntrusionSensorNumber"] = 1; | 
|  | aResp->res.jsonValue["PhysicalSecurity"]["IntrusionSensor"] = value; | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Retrieves physical security properties over dbus | 
|  | */ | 
|  | inline void getPhysicalSecurityData(std::shared_ptr<bmcweb::AsyncResp> aResp) | 
|  | { | 
|  | constexpr std::array<std::string_view, 1> interfaces = { | 
|  | "xyz.openbmc_project.Chassis.Intrusion"}; | 
|  | managedStore::ManagedObjectStoreContext requestContext(aResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTree( | 
|  | "/xyz/openbmc_project/Intrusion", 1, interfaces, requestContext, | 
|  | [aResp{std::move(aResp)}]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreeResponse& subtree) { | 
|  | if (ec) | 
|  | { | 
|  | // do not add err msg in redfish response, because this is not | 
|  | //     mandatory property | 
|  | BMCWEB_LOG_INFO << "DBUS error: no matched iface " << ec << "\n"; | 
|  | return; | 
|  | } | 
|  | // Iterate over all retrieved ObjectPaths. | 
|  | for (const auto& object : subtree) | 
|  | { | 
|  | for (const auto& service : object.second) | 
|  | { | 
|  | getIntrusionByService(aResp, service.first, object.first); | 
|  | return; | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void handleChassisCollectionGet( | 
|  | App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | asyncResp->res.jsonValue["@odata.type"] = | 
|  | "#ChassisCollection.ChassisCollection"; | 
|  | asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; | 
|  | asyncResp->res.jsonValue["Name"] = "Chassis Collection"; | 
|  |  | 
|  | constexpr std::array<std::string_view, 2> interfaces{ | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  | collection_util::getCollectionMembers( | 
|  | asyncResp, boost::urls::url("/redfish/v1/Chassis"), interfaces); | 
|  | } | 
|  |  | 
|  | inline nlohmann::json& | 
|  | getChassisContainsArray(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) | 
|  | { | 
|  | nlohmann::json& jValue = asyncResp->res.jsonValue["Links"]["Contains"]; | 
|  | if (!jValue.is_array()) | 
|  | { | 
|  | jValue = nlohmann::json::array(); | 
|  | asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = | 
|  | jValue.size(); | 
|  | } | 
|  | return jValue; | 
|  | } | 
|  |  | 
|  | // Hack in the Contains relationships | 
|  | inline void addHardcodedRelationships( | 
|  | const std::string& chassisId, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) | 
|  | { | 
|  | if constexpr (bmcwebEnableRdeDevice) | 
|  | { | 
|  | if (chassisId == platform6Chassis0) | 
|  | { | 
|  | nlohmann::json& jValue = getChassisContainsArray(asyncResp); | 
|  | const std::vector<std::string> backplaneTrays = { | 
|  | "2HMMCS_Tray", | 
|  | }; | 
|  | for (const std::string& tray : backplaneTrays) | 
|  | { | 
|  | jValue.emplace_back( | 
|  | nlohmann::json({{"@odata.id", "/redfish/v1/Chassis/" + tray}})); | 
|  | } | 
|  | asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = | 
|  | jValue.size(); | 
|  | } | 
|  | } | 
|  | } | 
|  | // End Hack | 
|  |  | 
|  | inline void getChassisContainedBy( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperEndPoints& upstreamChassisPaths) | 
|  | { | 
|  | if (ec) | 
|  | { | 
|  | if (ec.value() != EBADR) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "DBUS response error " << ec; | 
|  | messages::internalError(asyncResp->res); | 
|  | } | 
|  | return; | 
|  | } | 
|  | if (upstreamChassisPaths.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  | if (upstreamChassisPaths.size() > 1) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << chassisId << " is contained by mutliple chassis"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | sdbusplus::message::object_path upstreamChassisPath( | 
|  | upstreamChassisPaths[0]); | 
|  | std::string upstreamChassis = upstreamChassisPath.filename(); | 
|  | if (upstreamChassis.empty()) | 
|  | { | 
|  | BMCWEB_LOG_WARNING << "Malformed upstream Chassis path " | 
|  | << upstreamChassisPath.str << " on " << chassisId; | 
|  | return; | 
|  | } | 
|  |  | 
|  | asyncResp->res.jsonValue["Links"]["ContainedBy"]["@odata.id"] = | 
|  | "/redfish/v1/Chassis/" + upstreamChassis; | 
|  | } | 
|  |  | 
|  | inline void getChassisContains( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperEndPoints& downstreamChassisPaths) | 
|  | { | 
|  | if (ec) | 
|  | { | 
|  | if (ec.value() != EBADR) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "DBUS response error " << ec; | 
|  | messages::internalError(asyncResp->res); | 
|  | } | 
|  | return; | 
|  | } | 
|  | if (downstreamChassisPaths.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | for (const auto& chassisPath : downstreamChassisPaths) | 
|  | { | 
|  | sdbusplus::message::object_path downstreamChassisPath(chassisPath); | 
|  | const std::string downstreamChassis = downstreamChassisPath.filename(); | 
|  | if (downstreamChassis.empty()) | 
|  | { | 
|  | BMCWEB_LOG_WARNING << "Malformed downstream Chassis path " | 
|  | << downstreamChassisPath.str << " on " | 
|  | << chassisId; | 
|  | continue; | 
|  | } | 
|  | managedStore::GetManagedObjectStore()->getDbusObject( | 
|  | chassisPath, {}, requestContext, | 
|  | [asyncResp, | 
|  | downstreamChassis](const boost::system::error_code& ec2, | 
|  | const dbus::utility::MapperGetObject& object) { | 
|  | if (ec2) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Link must be a Chassis or Board | 
|  | constexpr std::array<std::string_view, 2> chassisInterfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis", | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | }; | 
|  |  | 
|  | if (!redfish::dbus_utils::findInterfacesInServiceMap( | 
|  | object, chassisInterfaces)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | nlohmann::json& jValue = | 
|  | asyncResp->res.jsonValue["Links"]["Contains"]; | 
|  | if (!jValue.is_array()) | 
|  | { | 
|  | // Create the array if it was empty | 
|  | jValue = nlohmann::json::array(); | 
|  | } | 
|  | nlohmann::json link; | 
|  | link["@odata.id"] = "/redfish/v1/Chassis/" + downstreamChassis; | 
|  | jValue.emplace_back(std::move(link)); | 
|  | asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = | 
|  | jValue.size(); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | inline void | 
|  | getChassisConnectivity(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, | 
|  | const std::string& chassisPath) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Get chassis connectivity"; | 
|  |  | 
|  | if constexpr (bmcwebEnableRdeDevice) | 
|  | { | 
|  | // Hack in Chassis links | 
|  | addHardcodedRelationships(chassisId, asyncResp); | 
|  | // End Hack | 
|  | } | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  |  | 
|  | // Chassis Dbus interfaces. They are the same types when we enumerate the | 
|  | // Chassis Collection. | 
|  | constexpr std::array<std::string_view, 2> interfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  |  | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | chassisPath + "/contained_by", {"/xyz/openbmc_project/inventory"}, 0, | 
|  | interfaces, requestContext, | 
|  | std::bind_front(getChassisContainedBy, asyncResp, chassisId)); | 
|  |  | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | chassisPath + "/containing", {"/xyz/openbmc_project/inventory"}, 0, | 
|  | interfaces, requestContext, | 
|  | std::bind_front(getChassisContains, asyncResp, chassisId)); | 
|  | } | 
|  |  | 
|  | inline void getChassisCableAssociation( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisPath) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Get chassis -- cable association"; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::vector<std::string>>( | 
|  | "xyz.openbmc_project.ObjectMapper", chassisPath + "/attached_cables", | 
|  | "xyz.openbmc_project.Association", "endpoints", context, | 
|  | [asyncResp](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& cableList) { | 
|  | if (ec) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (cableList.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  | nlohmann::json& jValue = asyncResp->res.jsonValue["Links"]["Cables"]; | 
|  | jValue = nlohmann::json::array(); | 
|  | for (const std::string& cable : cableList) | 
|  | { | 
|  | sdbusplus::message::object_path cablePath(cable); | 
|  | std::string cableName = cablePath.filename(); | 
|  | if (cable.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "filename() is empty in " << cable; | 
|  | continue; | 
|  | } | 
|  | jValue.emplace_back( | 
|  | nlohmann::json({{"@odata.id", "/redfish/v1/Cables/" + cableName}})); | 
|  | } | 
|  | asyncResp->res.jsonValue["Links"]["Cables@odata.count"] = | 
|  | cableList.size(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void getChassisProcessorAssociation( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisPath, bool multiHost) | 
|  | { | 
|  |  | 
|  | BMCWEB_LOG_DEBUG << "Get chassis -- processor association"; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::vector<std::string>>( | 
|  | "xyz.openbmc_project.ObjectMapper", chassisPath + "/processors", | 
|  | "xyz.openbmc_project.Association", "endpoints", context, | 
|  | [asyncResp, multiHost](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& processors) { | 
|  | if (ec) | 
|  | { | 
|  | return; | 
|  | } | 
|  | nlohmann::json& jValue = | 
|  | asyncResp->res.jsonValue["Links"]["Processors"]; | 
|  | jValue = nlohmann::json::array(); | 
|  | for (const std::string& processor : processors) | 
|  | { | 
|  | sdbusplus::message::object_path processorPath(processor); | 
|  | std::string processorName = processorPath.filename(); | 
|  | if (processorName.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "filename() is empty in " << processor; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Special way of determining processor path for single vs multihost | 
|  | if(!multiHost){ | 
|  | jValue.emplace_back( | 
|  | nlohmann::json({{"@odata.id", | 
|  | "/redfish/v1/Systems/system/Processors/" + processorName}})); | 
|  | continue; | 
|  | } | 
|  | // The 3rd parent is the systemId for processor in multihost machines | 
|  | std::string systemId = processorPath.parent_path().parent_path().parent_path().filename(); | 
|  | jValue.emplace_back( | 
|  | nlohmann::json({{"@odata.id", | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Systems", | 
|  | systemId, "Processors", processorName)}})); | 
|  | } | 
|  |  | 
|  | std::sort(jValue.begin(), jValue.end()); | 
|  |  | 
|  | asyncResp->res.jsonValue["Links"]["Processors@odata.count"] = | 
|  | processors.size(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void getChassisMemoryAssociationSingleSystem( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisPath) | 
|  | { | 
|  |  | 
|  | BMCWEB_LOG_DEBUG << "Get chassis -- memory association"; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::vector<std::string>>( | 
|  | "xyz.openbmc_project.ObjectMapper", chassisPath + "/memories", | 
|  | "xyz.openbmc_project.Association", "endpoints", context, | 
|  | [asyncResp](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& memories) { | 
|  | if (ec) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (memories.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Redfish Chassis schema uses memory collection here. Only | 
|  | * motherboard would have such association. Hardcode the system | 
|  | * memory collection odata.id | 
|  | */ | 
|  | asyncResp->res.jsonValue["Memory"] = { | 
|  | {"@odata.id", "/redfish/v1/Systems/system/Memory"}}; | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Handles System related properties | 
|  | * | 
|  | * * On single-system system, a chassis will be default to the | 
|  | * only system w/ or w/o dedicated association. | 
|  | * | 
|  | * * On multi-system system, a chassis requires an association to | 
|  | * the target system or otherwise the chassis will be linked to all | 
|  | * systems on the system | 
|  | */ | 
|  | inline void | 
|  | getChassisSystem(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisPath) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTreePaths( | 
|  | "/", 0, redfish::system_utils::systemInterfaces, requestContext, | 
|  | [asyncResp, chassisPath](const boost::system::error_code& ec, | 
|  | const std::vector<std::string>& objects) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | // If there are less than 2 Item.Systems assume singlehost | 
|  | if (objects.size() < 2) | 
|  | { | 
|  | // Perform SingleSystem Only Actions | 
|  | getChassisMemoryAssociationSingleSystem(asyncResp, chassisPath); | 
|  | getChassisProcessorAssociation(asyncResp, chassisPath, false); | 
|  | asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Systems", | 
|  | "system", "PCIeDevices"); | 
|  |  | 
|  | nlohmann::json::object_t system; | 
|  | system["@odata.id"] = "/redfish/v1/Systems/system"; | 
|  | asyncResp->res.jsonValue["Links"]["ComputerSystems"].emplace_back( | 
|  | system); | 
|  | return; | 
|  | } | 
|  | // Otherwise find chassis -> system association | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | chassisPath + "/contained_by", {"/xyz/openbmc_project/inventory"}, | 
|  | 0, system_utils::systemInterfaces, requestContext, | 
|  | [asyncResp, systemPaths{objects} | 
|  | // default the systemPath to all systems | 
|  | ](const boost::system::error_code& ec, | 
|  | const std::vector<std::string>& assocPaths) mutable { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "DBUS response error on getting association for system/chassis"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // override the default w/ associated systems | 
|  | if (!assocPaths.empty()) | 
|  | { | 
|  | systemPaths = assocPaths; | 
|  | } | 
|  |  | 
|  | for_each(systemPaths.begin(), systemPaths.end(), | 
|  | [asyncResp](const std::string& path) { | 
|  | nlohmann::json::object_t system; | 
|  | system["@odata.id"] = | 
|  | std::filesystem::path("/redfish/v1/Systems") / | 
|  | std::filesystem::path(path).filename(); | 
|  | asyncResp->res.jsonValue["Links"]["ComputerSystems"] | 
|  | .emplace_back(system); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | // Perform mulithost only actions | 
|  | getChassisProcessorAssociation(asyncResp, chassisPath, true); | 
|  | }); | 
|  | } | 
|  | /** | 
|  | * ChassisCollection derived class for delivering Chassis Collection Schema | 
|  | *  Functions triggers appropriate requests on DBus | 
|  | */ | 
|  | inline void requestRoutesChassisCollection(App& app) | 
|  | { | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/") | 
|  | .privileges(redfish::privileges::getChassisCollection) | 
|  | .methods(boost::beast::http::verb::get)( | 
|  | std::bind_front(handleChassisCollectionGet, std::ref(app))); | 
|  | } | 
|  |  | 
|  | inline std::optional<std::string> | 
|  | getChassisTypeProperty(const std::variant<std::string>& property) | 
|  | { | 
|  | const std::string* value = std::get_if<std::string>(&property); | 
|  | if (value == nullptr) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | if (*value == | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component") | 
|  | { | 
|  | return "Component"; | 
|  | } | 
|  | if (*value == | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure") | 
|  | { | 
|  | return "Enclosure"; | 
|  | } | 
|  | if (*value == | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module") | 
|  | { | 
|  | return "Module"; | 
|  | } | 
|  | if (*value == | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount") | 
|  | { | 
|  | return "RackMount"; | 
|  | } | 
|  | if (*value == "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType." | 
|  | "StandAlone") | 
|  | { | 
|  | return "StandAlone"; | 
|  | } | 
|  | if (*value == "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType." | 
|  | "StorageEnclosure") | 
|  | { | 
|  | return "StorageEnclosure"; | 
|  | } | 
|  | if (*value == "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone") | 
|  | { | 
|  | return "Zone"; | 
|  | } | 
|  |  | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | inline void addChassisReset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, | 
|  | const std::optional<std::string>& chassisType) | 
|  | { | 
|  | // If chassisType is nullopt, then use the deault type of RackMount. | 
|  | if (chassisType) | 
|  | { | 
|  | asyncResp->res.jsonValue["ChassisType"] = *chassisType; | 
|  | } | 
|  |  | 
|  | // Only Default, RackMount, or StandAlone will have Reset Action. | 
|  | if (chassisType && *chassisType != "RackMount" && | 
|  | *chassisType != "StandAlone") | 
|  | { | 
|  | BMCWEB_LOG_INFO | 
|  | << "Chassis Type is not supported. Only support Chassis Reset on RackMount or StandAlone."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId, | 
|  | "Actions", "Chassis.Reset"); | 
|  | asyncResp->res | 
|  | .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId, | 
|  | "ResetActionInfo"); | 
|  | } | 
|  |  | 
|  | inline void getChassisType( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& connectionName, const std::string& path, | 
|  | std::function<void(const boost::system::error_code ec, | 
|  | const std::variant<std::string>&)>&& callback) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::string>( | 
|  | connectionName, path, | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis", "Type", | 
|  | context, | 
|  | [callback{std::move(callback)}](boost::system::error_code ec, | 
|  | const std::string& val) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "Failed to get Chassis Type. Set ec to 0 in callback."; | 
|  |  | 
|  | callback(boost::system::error_code(), | 
|  | std::variant<std::string>{""}); | 
|  | return; | 
|  | } | 
|  | callback(ec, val); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void | 
|  | getChassisLocationCode(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& connectionName, | 
|  | const std::string& path) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::string>( | 
|  | connectionName, path, | 
|  | "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", | 
|  | context, | 
|  | [asyncResp](const boost::system::error_code ec, | 
|  | const std::string& property) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error for Location"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = | 
|  | property; | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& connectionName, | 
|  | const std::string& path) | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | dbus_utils::getProperty<std::string>( | 
|  | connectionName, path, "xyz.openbmc_project.Common.UUID", "UUID", | 
|  | context, | 
|  | [asyncResp](const boost::system::error_code& ec, | 
|  | const std::string& chassisUUID) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "DBUS response error for UUID"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | asyncResp->res.jsonValue["UUID"] = chassisUUID; | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void | 
|  | handleChassisGet(App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | constexpr std::array<std::string_view, 2> interfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTree( | 
|  | "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | 
|  | [asyncResp, chassisId(std::string(chassisId)), requestContext]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreeResponse& subtree) { | 
|  | if (ec) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | // Iterate over all retrieved ObjectPaths. | 
|  | for (const std::pair< | 
|  | std::string, | 
|  | std::vector<std::pair<std::string, std::vector<std::string>>>>& | 
|  | object : subtree) | 
|  | { | 
|  | const std::string& path = object.first; | 
|  | const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
|  | connectionNames = object.second; | 
|  |  | 
|  | sdbusplus::message::object_path objPath(path); | 
|  | if (objPath.filename() != chassisId) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | getChassisConnectivity(asyncResp, chassisId, path); | 
|  | getChassisCableAssociation(asyncResp, path); | 
|  | getChassisSystem(asyncResp, path); | 
|  |  | 
|  | auto health = std::make_shared<HealthPopulate>(asyncResp); | 
|  |  | 
|  | #ifdef HEALTH_POPULATE | 
|  | dbus_utils::getAssociationEndPoints( | 
|  | path + "/all_sensors", requestContext, | 
|  | [health](const boost::system::error_code& ec2, | 
|  | const dbus::utility::MapperEndPoints& resp) { | 
|  | if (ec2) | 
|  | { | 
|  | return; // no sensors = no failures | 
|  | } | 
|  | health->inventory = resp; | 
|  | }); | 
|  |  | 
|  | health->populate(); | 
|  | #endif | 
|  |  | 
|  | if (connectionNames.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Got 0 Connection names"; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | asyncResp->res.jsonValue["@odata.type"] = | 
|  | "#Chassis.v1_17_0.Chassis"; | 
|  | asyncResp->res.jsonValue["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId); | 
|  | asyncResp->res.jsonValue["Name"] = "Chassis Collection"; | 
|  | asyncResp->res.jsonValue["ChassisType"] = "RackMount"; | 
|  |  | 
|  | asyncResp->res.jsonValue["Certificates"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "Certificates"); | 
|  | asyncResp->res.jsonValue["TrustedComponents"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "TrustedComponents"); | 
|  |  | 
|  | dbus_utils::getAssociationEndPoints( | 
|  | path + "/drive", requestContext, | 
|  | [asyncResp, | 
|  | chassisId](const boost::system::error_code& ec3, | 
|  | const dbus::utility::MapperEndPoints& resp) { | 
|  | if (ec3 || resp.empty()) | 
|  | { | 
|  | return; // no drives = no failures | 
|  | } | 
|  |  | 
|  | nlohmann::json reference; | 
|  | reference["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", chassisId, "Drives"); | 
|  | asyncResp->res.jsonValue["Drives"] = std::move(reference); | 
|  | }); | 
|  |  | 
|  | dbus_utils::getProperty<std::string>( | 
|  | "xyz.openbmc_project.EntityManager", | 
|  | "/xyz/openbmc_project/inventory/system/board/" + chassisId + | 
|  | "/BmcNet", | 
|  | "xyz.openbmc_project.Configuration.BmcNet", "Name", | 
|  | requestContext, | 
|  | [asyncResp, path, | 
|  | chassisId](const boost::system::error_code& ec3, | 
|  | const std::string& bmcNetName) { | 
|  | if (ec3 || bmcNetName != "BmcNet") | 
|  | { | 
|  | managedStore::ManagedObjectStoreContext requestContextInner( | 
|  | asyncResp); | 
|  | // non-bmc chassis | 
|  | dbus_utils::getAssociationEndPoints( | 
|  | path + "/network_adapter", requestContextInner, | 
|  | [asyncResp, chassisId]( | 
|  | const boost::system::error_code& ec4, | 
|  | const dbus::utility::MapperEndPoints& resp) { | 
|  | if (ec4 || resp.empty()) | 
|  | { | 
|  | return; // no networkadapters = no failures | 
|  | } | 
|  |  | 
|  | nlohmann::json reference; | 
|  | reference["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", chassisId, | 
|  | "NetworkAdapters"); | 
|  | asyncResp->res.jsonValue["NetworkAdapters"] = | 
|  | std::move(reference); | 
|  | }); | 
|  | return; | 
|  | } | 
|  | // bmc chassis | 
|  | nlohmann::json reference; | 
|  | reference["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", chassisId, "NetworkAdapters"); | 
|  | asyncResp->res.jsonValue["NetworkAdapters"] = | 
|  | std::move(reference); | 
|  | }); | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext context(asyncResp); | 
|  | const std::string& connectionName = connectionNames[0].first; | 
|  |  | 
|  | const std::vector<std::string>& interfaces2 = | 
|  | connectionNames[0].second; | 
|  | const std::array<const char*, 2> hasIndicatorLed = { | 
|  | "xyz.openbmc_project.Inventory.Item.Panel", | 
|  | "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; | 
|  |  | 
|  | const std::string assetTagInterface = | 
|  | "xyz.openbmc_project.Inventory.Decorator.AssetTag"; | 
|  | const std::string revisionInterface = | 
|  | "xyz.openbmc_project.Inventory.Decorator.Revision"; | 
|  | for (const auto& interface : interfaces2) | 
|  | { | 
|  | if (interface == assetTagInterface) | 
|  | { | 
|  | dbus_utils::getProperty<std::string>( | 
|  | connectionName, path, assetTagInterface, "AssetTag", | 
|  | context, | 
|  | [asyncResp, chassisId(std::string(chassisId))]( | 
|  | const boost::system::error_code& ec2, | 
|  | const std::string& property) { | 
|  | if (ec2) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << | 
|  | "DBus response error for AssetTag"; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | asyncResp->res.jsonValue["AssetTag"] = property; | 
|  | }); | 
|  | } | 
|  | else if (interface == revisionInterface) | 
|  | { | 
|  | dbus_utils::getProperty<std::string>( | 
|  | connectionName, path, revisionInterface, "Version", | 
|  | context, | 
|  | [asyncResp, chassisId]( | 
|  | const boost::system::error_code& ec2, | 
|  | const std::string& property) { | 
|  | if (ec2) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << | 
|  | "DBus response error for Version: " << ec2; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | asyncResp->res.jsonValue["Version"] = property; | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (const char* interface : hasIndicatorLed) | 
|  | { | 
|  | if (std::find(interfaces2.begin(), interfaces2.end(), | 
|  | interface) != interfaces2.end()) | 
|  | { | 
|  | getIndicatorLedState(asyncResp); | 
|  | getLocationIndicatorActive(asyncResp); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | managedStore::GetManagedObjectStore()->getAllProperties( | 
|  | connectionName, path, | 
|  | "xyz.openbmc_project.Inventory.Decorator.Asset", context, | 
|  | [asyncResp, chassisId(std::string(chassisId)), | 
|  | path](const boost::system::error_code& /*ec2*/, | 
|  | const dbus::utility::DBusPropertiesMap& propertiesList) { | 
|  | const std::string* partNumber = nullptr; | 
|  | const std::string* serialNumber = nullptr; | 
|  | const std::string* manufacturer = nullptr; | 
|  | const std::string* model = nullptr; | 
|  | const std::string* sparePartNumber = nullptr; | 
|  |  | 
|  | const bool success = sdbusplus::unpackPropertiesNoThrow( | 
|  | dbus_utils::UnpackErrorPrinter(), propertiesList, | 
|  | "PartNumber", partNumber, "SerialNumber", serialNumber, | 
|  | "Manufacturer", manufacturer, "Model", model, | 
|  | "SparePartNumber", sparePartNumber); | 
|  |  | 
|  | if (!success) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (partNumber != nullptr) | 
|  | { | 
|  | asyncResp->res.jsonValue["PartNumber"] = *partNumber; | 
|  | } | 
|  |  | 
|  | if (serialNumber != nullptr) | 
|  | { | 
|  | asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; | 
|  | } | 
|  |  | 
|  | if (manufacturer != nullptr) | 
|  | { | 
|  | asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; | 
|  | } | 
|  |  | 
|  | if (model != nullptr) | 
|  | { | 
|  | asyncResp->res.jsonValue["Model"] = *model; | 
|  | } | 
|  |  | 
|  | // SparePartNumber is optional on D-Bus | 
|  | // so skip if it is empty | 
|  | if (sparePartNumber != nullptr && !sparePartNumber->empty()) | 
|  | { | 
|  | asyncResp->res.jsonValue["SparePartNumber"] = | 
|  | *sparePartNumber; | 
|  | } | 
|  |  | 
|  | if constexpr (bmcwebEnableRdeDevice) | 
|  | { | 
|  | // Hack in the ContainedBy fields | 
|  | const std::regex rdeTrayRegex(".*Tray"); | 
|  | if (std::regex_match(chassisId, rdeTrayRegex)) | 
|  | { | 
|  | // TODO(b/273682625): platform6Chassis1 Trays are cabled | 
|  | // instead of contained | 
|  | asyncResp->res.jsonValue["Links"]["ContainedBy"] = { | 
|  | {"@odata.id", "/redfish/v1/Chassis/" + | 
|  | std::string(platform6Chassis0)}}; | 
|  | } | 
|  | // End Hack | 
|  | } | 
|  |  | 
|  | if constexpr (enablePlatform9) | 
|  | { | 
|  | // Hack in the Cables | 
|  | if (chassisId == platform9Chassis2) | 
|  | { | 
|  | nlohmann::json::array_t attachedCables; | 
|  | nlohmann::json::object_t cab; | 
|  | cab["@odata.id"] = "/redfish/v1/Cables/" + | 
|  | std::string(platform9Cable4); | 
|  | attachedCables.emplace_back(std::move(cab)); | 
|  | asyncResp->res.jsonValue["Links"]["Cables"] = | 
|  | std::move(attachedCables); | 
|  | } | 
|  | if (chassisId == platform9Chassis0) | 
|  | { | 
|  | nlohmann::json::array_t attachedCables; | 
|  | nlohmann::json::object_t cab; | 
|  | nlohmann::json::object_t cat; | 
|  | cat["@odata.id"] = "/redfish/v1/Cables/cat5_cable"; | 
|  | attachedCables.emplace_back(std::move(cat)); | 
|  | cab["@odata.id"] = "/redfish/v1/Cables/" + | 
|  | std::string(platform9Cable0); | 
|  | attachedCables.emplace_back(std::move(cab)); | 
|  | asyncResp->res.jsonValue["Links"]["Cables"] = | 
|  | std::move(attachedCables); | 
|  | } | 
|  | if (chassisId == platform9Chassis3) | 
|  | { | 
|  | nlohmann::json::array_t attachedCables; | 
|  | nlohmann::json::object_t cab; | 
|  | nlohmann::json::object_t cab1; | 
|  | cab["@odata.id"] = "/redfish/v1/Cables/" + | 
|  | std::string(platform9Cable2); | 
|  | cab1["@odata.id"] = "/redfish/v1/Cables/" + | 
|  | std::string(platform9Cable3); | 
|  | attachedCables.emplace_back(std::move(cab)); | 
|  | attachedCables.emplace_back(std::move(cab1)); | 
|  | asyncResp->res.jsonValue["Links"]["Cables"] = | 
|  | std::move(attachedCables); | 
|  | } | 
|  | // Hack in containing satellite Chassis | 
|  | if (chassisId == platform9Chassis4) | 
|  | { | 
|  | nlohmann::json& jValue = | 
|  | asyncResp->res.jsonValue["Links"]["Contains"]; | 
|  | jValue = nlohmann::json::array(); | 
|  | jValue.emplace_back( | 
|  | nlohmann::json({{"@odata.id", | 
|  | "/redfish/v1/Chassis/5B247A_" + | 
|  | std::string(platform9Chassis5)}})); | 
|  | asyncResp->res | 
|  | .jsonValue["Links"]["Contains@odata.count"] = | 
|  | jValue.size(); | 
|  | } | 
|  | // End of workarounds | 
|  | } | 
|  |  | 
|  | asyncResp->res.jsonValue["Name"] = chassisId; | 
|  | asyncResp->res.jsonValue["Id"] = chassisId; | 
|  | #ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL | 
|  | asyncResp->res.jsonValue["Thermal"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "Thermal"); | 
|  | // Power object | 
|  | asyncResp->res.jsonValue["Power"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "Power"); | 
|  | #endif | 
|  | asyncResp->res.jsonValue["Assembly"] = { | 
|  | {"@odata.id", | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "Assembly") | 
|  | .buffer()}}; | 
|  |  | 
|  | #ifdef BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM | 
|  | asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "ThermalSubsystem"); | 
|  | asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "PowerSubsystem"); | 
|  | asyncResp->res.jsonValue["EnvironmentMetrics"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, | 
|  | "EnvironmentMetrics"); | 
|  | #endif | 
|  | // SensorCollection | 
|  | asyncResp->res.jsonValue["Sensors"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "Sensors"); | 
|  | asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; | 
|  |  | 
|  | asyncResp->res.jsonValue["PCIeSlots"]["@odata.id"] = | 
|  | crow::utility::urlFromPieces("redfish", "v1", "Chassis", | 
|  | chassisId, "PCIeSlots"); | 
|  |  | 
|  | nlohmann::json::array_t managedBy; | 
|  | nlohmann::json::object_t manager; | 
|  | manager["@odata.id"] = "/redfish/v1/Managers/bmc"; | 
|  | managedBy.emplace_back(std::move(manager)); | 
|  | asyncResp->res.jsonValue["Links"]["ManagedBy"] = | 
|  | std::move(managedBy); | 
|  | getChassisState(asyncResp); | 
|  | getStorageLinkFromChassis(asyncResp, path); | 
|  | }); | 
|  |  | 
|  | bool hasChassisInterface = false; | 
|  | for (const auto& interface : interfaces2) | 
|  | { | 
|  | if (interface == "xyz.openbmc_project.Common.UUID") | 
|  | { | 
|  | getChassisUUID(asyncResp, connectionName, path); | 
|  | } | 
|  | else if (interface == | 
|  | "xyz.openbmc_project.Inventory.Decorator.LocationCode") | 
|  | { | 
|  | location_util::getLocationCode(asyncResp, connectionName, | 
|  | path, | 
|  | "/Location"_json_pointer); | 
|  | } | 
|  | else if (location_util::isConnector(interface)) | 
|  | { | 
|  | std::optional<std::string> locationType = | 
|  | location_util::getLocationType(interface); | 
|  | if (!locationType) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "getLocationType for Chassis failed for " | 
|  | << interface; | 
|  | continue; | 
|  | } | 
|  | asyncResp->res | 
|  | .jsonValue["Location"]["PartLocation"]["LocationType"] = | 
|  | *locationType; | 
|  | } | 
|  | else if (interface == | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis" || | 
|  | interface == | 
|  | "xyz.openbmc_project.Inventory.Item.Board") | 
|  | { | 
|  | hasChassisInterface = true; | 
|  | getChassisType( | 
|  | asyncResp, connectionName, path, | 
|  | [asyncResp, | 
|  | chassisId](const boost::system::error_code ec2, | 
|  | const std::variant<std::string>& property) { | 
|  | if (ec2) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  | addChassisReset(asyncResp, chassisId, | 
|  | getChassisTypeProperty(property)); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hasChassisInterface) | 
|  | { | 
|  | location_util::getPartLocationContext(asyncResp, | 
|  | "/Location"_json_pointer, | 
|  | path + "/contained_by"); | 
|  | } | 
|  | else | 
|  | { | 
|  | location_util::getPartLocationContext( | 
|  | asyncResp, "/Location"_json_pointer, path + "/chassis"); | 
|  | // Item.Chassis interface is not available | 
|  | // Enable Reset Action by default. | 
|  | addChassisReset(asyncResp, chassisId, std::nullopt); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Couldn't find an object with that name.  return an error | 
|  | messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); | 
|  | }); | 
|  |  | 
|  | getPhysicalSecurityData(asyncResp); | 
|  | } | 
|  |  | 
|  | inline void | 
|  | handleChassisPatch(App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& param) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | std::optional<bool> locationIndicatorActive; | 
|  | std::optional<std::string> indicatorLed; | 
|  |  | 
|  | if (param.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!json_util::readJsonPatch( | 
|  | req, asyncResp->res, "LocationIndicatorActive", | 
|  | locationIndicatorActive, "IndicatorLED", indicatorLed)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // TODO (Gunnar): Remove IndicatorLED after enough time has passed | 
|  | if (!locationIndicatorActive && !indicatorLed) | 
|  | { | 
|  | return; // delete this when we support more patch properties | 
|  | } | 
|  | if (indicatorLed) | 
|  | { | 
|  | asyncResp->res.addHeader( | 
|  | boost::beast::http::field::warning, | 
|  | "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\""); | 
|  | } | 
|  |  | 
|  | constexpr std::array<std::string_view, 2> interfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  |  | 
|  | const std::string& chassisId = param; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTree( | 
|  | "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | 
|  | [asyncResp, chassisId, locationIndicatorActive, | 
|  | indicatorLed](const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreeResponse& subtree) { | 
|  | if (ec) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Iterate over all retrieved ObjectPaths. | 
|  | for (const std::pair< | 
|  | std::string, | 
|  | std::vector<std::pair<std::string, std::vector<std::string>>>>& | 
|  | object : subtree) | 
|  | { | 
|  | const std::string& path = object.first; | 
|  | const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
|  | connectionNames = object.second; | 
|  |  | 
|  | sdbusplus::message::object_path objPath(path); | 
|  | if (objPath.filename() != chassisId) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (connectionNames.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Got 0 Connection names"; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const std::vector<std::string>& interfaces3 = | 
|  | connectionNames[0].second; | 
|  |  | 
|  | const std::array<const char*, 2> hasIndicatorLed = { | 
|  | "xyz.openbmc_project.Inventory.Item.Panel", | 
|  | "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; | 
|  | bool indicatorChassis = false; | 
|  | for (const char* interface : hasIndicatorLed) | 
|  | { | 
|  | if (std::find(interfaces3.begin(), interfaces3.end(), | 
|  | interface) != interfaces3.end()) | 
|  | { | 
|  | indicatorChassis = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (locationIndicatorActive) | 
|  | { | 
|  | if (indicatorChassis) | 
|  | { | 
|  | setLocationIndicatorActive(asyncResp, | 
|  | *locationIndicatorActive); | 
|  | } | 
|  | else | 
|  | { | 
|  | messages::propertyUnknown(asyncResp->res, | 
|  | "LocationIndicatorActive"); | 
|  | } | 
|  | } | 
|  | if (indicatorLed) | 
|  | { | 
|  | if (indicatorChassis) | 
|  | { | 
|  | setIndicatorLedState(asyncResp, *indicatorLed); | 
|  | } | 
|  | else | 
|  | { | 
|  | messages::propertyUnknown(asyncResp->res, "IndicatorLED"); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Chassis override class for delivering Chassis Schema | 
|  | * Functions triggers appropriate requests on DBus | 
|  | */ | 
|  | inline void requestRoutesChassis(App& app) | 
|  | { | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") | 
|  | .privileges(redfish::privileges::getChassis) | 
|  | .methods(boost::beast::http::verb::get)( | 
|  | std::bind_front(handleChassisGet, std::ref(app))); | 
|  |  | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") | 
|  | .privileges(redfish::privileges::patchChassis) | 
|  | .methods(boost::beast::http::verb::patch)( | 
|  | std::bind_front(handleChassisPatch, std::ref(app))); | 
|  | } | 
|  |  | 
|  | void delayResetCN(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | int delayTimeSecs); | 
|  |  | 
|  | inline void | 
|  | doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | std::optional<int> delayTimeSecs) | 
|  | { | 
|  | constexpr std::array<std::string_view, 1> interfaces = { | 
|  | "xyz.openbmc_project.State.Chassis"}; | 
|  |  | 
|  | // Use mapper to get subtree paths. | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTreePaths( | 
|  | "/", 0, interfaces, requestContext, | 
|  | [asyncResp, delayTimeSecs]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "[mapper] Bad D-Bus request error: " << ec; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const char* processName = "xyz.openbmc_project.State.Chassis"; | 
|  | const char* interfaceName = "xyz.openbmc_project.State.Chassis"; | 
|  | const char* destProperty = "RequestedPowerTransition"; | 
|  | const std::string propertyValue = | 
|  | "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; | 
|  | std::string objectPath = "/xyz/openbmc_project/state/chassis_system0"; | 
|  |  | 
|  | /* Look for system reset chassis path */ | 
|  | if ((std::find(chassisList.begin(), chassisList.end(), objectPath)) == | 
|  | chassisList.end()) | 
|  | { | 
|  | /* We prefer to reset the full chassis_system, but if it doesn't | 
|  | * exist on some platforms, fall back to a host-only power reset | 
|  | */ | 
|  | objectPath = "/xyz/openbmc_project/state/chassis0"; | 
|  | } | 
|  |  | 
|  | if (delayTimeSecs.has_value()) | 
|  | { | 
|  | if (delayTimeSecs.value() > KMAX_RESET_DELAY_SEC) | 
|  | { | 
|  | BMCWEB_LOG_ERROR | 
|  | << "chassis reset delay out of range, request: " | 
|  | << delayTimeSecs.value() << " expect: 0-" | 
|  | << KMAX_RESET_DELAY_SEC << " seconds"; | 
|  | messages::propertyValueOutOfRange( | 
|  | asyncResp->res, "delay", | 
|  | std::to_string(KMAX_RESET_DELAY_SEC)); | 
|  | return; | 
|  | } | 
|  | delayResetCN(asyncResp, delayTimeSecs.value()); | 
|  | return; | 
|  | } | 
|  | managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | 
|  | asyncResp->strand_, | 
|  | [asyncResp](const boost::system::error_code& ec2) { | 
|  | // Use "Set" method to set the property value. | 
|  | if (ec2) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "[Set] Bad D-Bus request error: " << ec2; | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | messages::success(asyncResp->res); | 
|  | }, | 
|  | processName, objectPath, "org.freedesktop.DBus.Properties", "Set", | 
|  | interfaceName, destProperty, | 
|  | dbus::utility::DbusVariantType{propertyValue}); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * validateChassisResetAction checks the Chassis type to ensure the Chassis | 
|  | * supports Chassis Reset. | 
|  | * | 
|  | * Checks the Chassis type for `RackMount` or `StandAlone` for Chassis Reset. | 
|  | * Otherwise, reset is not supported. | 
|  | */ | 
|  | inline void validateChassisResetAction( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, | 
|  | std::function<void(const boost::system::error_code ec, | 
|  | const std::variant<std::string>&)>&& callback) | 
|  | { | 
|  | std::array<std::string_view, 2> interfaces{ | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  | managedStore::ManagedObjectStoreContext requestContext(asyncResp); | 
|  | managedStore::GetManagedObjectStore()->getSubTree( | 
|  | "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | 
|  | [asyncResp, chassisId, | 
|  | callback(std::move(callback))](const boost::system::error_code ec, | 
|  | const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { | 
|  | if (ec) | 
|  | { | 
|  | callback(ec, std::variant<std::string>{""}); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (const std::pair< | 
|  | std::string, | 
|  | std::vector<std::pair<std::string, std::vector<std::string>>>>& | 
|  | object : subtree) | 
|  | { | 
|  | sdbusplus::message::object_path path(object.first); | 
|  | if (path.filename() != chassisId) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
|  | connectionNames = object.second; | 
|  |  | 
|  | if (connectionNames.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Got 0 Connection names"; | 
|  | continue; | 
|  | } | 
|  | getChassisType(asyncResp, connectionNames[0].first, path.str, std::move(callback)); | 
|  | return; | 
|  | } | 
|  | messages::resourceNotFound(asyncResp->res, "#Chassis.v1_14_0.Chassis", | 
|  | chassisId); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void validateChassisResetActionHandler( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | std::optional<std::string> resetType, | 
|  | const std::optional<int> delayTimeSecs, const boost::system::error_code ec, | 
|  | const std::variant<std::string>& property) | 
|  | { | 
|  | if (ec) | 
|  | { | 
|  | messages::internalError(asyncResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::optional<std::string> chassisType = getChassisTypeProperty(property); | 
|  |  | 
|  | if (chassisType && *chassisType != "RackMount" && | 
|  | *chassisType != "StandAlone") | 
|  | { | 
|  | messages::actionNotSupported( | 
|  | asyncResp->res, "Only RackMount or StandAlone support Chassis " | 
|  | "Reset. Got Chassis type of " + | 
|  | *chassisType); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (resetType && *resetType != "PowerCycle") | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Invalid property value for " | 
|  | "ResetType: " | 
|  | << *resetType; | 
|  | messages::actionParameterNotSupported(asyncResp->res, *resetType, | 
|  | "ResetType"); | 
|  |  | 
|  | return; | 
|  | } | 
|  | doChassisPowerCycle(asyncResp, delayTimeSecs); | 
|  | } | 
|  |  | 
|  | inline void handleChassisResetActionInfoPost( | 
|  | App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | BMCWEB_LOG_DEBUG << "Post Chassis Reset."; | 
|  |  | 
|  | std::optional<std::string> resetType; | 
|  | std::optional<int> delayTimeSecs; | 
|  | if(!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType, | 
|  | "Delay", delayTimeSecs)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | validateChassisResetAction( | 
|  | asyncResp, chassisId, | 
|  | std::bind_front(validateChassisResetActionHandler, asyncResp, resetType, | 
|  | delayTimeSecs)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ChassisResetAction class supports the POST method for the Reset | 
|  | * action. | 
|  | * Function handles POST method request. | 
|  | * Analyzes POST body before sending Reset request data to D-Bus. | 
|  | */ | 
|  |  | 
|  | inline void requestRoutesChassisResetAction(App& app) | 
|  | { | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/") | 
|  | .privileges(redfish::privileges::postChassis) | 
|  | .methods(boost::beast::http::verb::post)( | 
|  | std::bind_front(handleChassisResetActionInfoPost, std::ref(app))); | 
|  | } | 
|  |  | 
|  | inline void validateChassisResetActionInfoHandler( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId, const boost::system::error_code ec, | 
|  | const std::variant<std::string>& property) | 
|  | { | 
|  | asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; | 
|  | asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", chassisId, "ResetActionInfo"); | 
|  | asyncResp->res.jsonValue["Name"] = "Reset Action Info"; | 
|  | asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; | 
|  | nlohmann::json::array_t parameters; | 
|  | nlohmann::json::object_t parameter; | 
|  | parameter["Name"] = "ResetType"; | 
|  | parameter["Required"] = true; | 
|  | parameter["DataType"] = "String"; | 
|  | nlohmann::json::array_t allowed; | 
|  | allowed.emplace_back("PowerCycle"); | 
|  |  | 
|  | nlohmann::json::object_t delaySecsParameter; | 
|  | // argument names are consistent with system delay | 
|  | delaySecsParameter["Name"] = "Delay"; | 
|  | delaySecsParameter["Required"] = false; | 
|  | delaySecsParameter["DataType"] = "Number"; | 
|  |  | 
|  | // xyz.openbmc_project.Inventory.Item.Chassis not found on the Chassis | 
|  | // Default to PowerCycle | 
|  | if (ec) | 
|  | { | 
|  | parameter["AllowableValues"] = std::move(allowed); | 
|  | parameters.emplace_back(std::move(parameter)); | 
|  | parameters.emplace_back(std::move(delaySecsParameter)); | 
|  | asyncResp->res.jsonValue["Parameters"] = std::move(parameters); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::optional<std::string> chassisType = getChassisTypeProperty(property); | 
|  | if (chassisType && *chassisType != "RackMount" && | 
|  | *chassisType != "StandAlone") | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Only RackMount support Chassis Reset. Got " | 
|  | "Chassis type of " | 
|  | << *chassisType; | 
|  |  | 
|  | // Remove PowerCycle as allowed value. | 
|  | allowed.pop_back(); | 
|  | } | 
|  |  | 
|  | parameter["AllowableValues"] = std::move(allowed); | 
|  | parameters.emplace_back(std::move(parameter)); | 
|  | parameters.emplace_back(std::move(delaySecsParameter)); | 
|  | asyncResp->res.jsonValue["Parameters"] = std::move(parameters); | 
|  | } | 
|  |  | 
|  | inline void handleChassisResetActionInfoGet( | 
|  | App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  | validateChassisResetAction( | 
|  | asyncResp, chassisId, | 
|  | std::bind_front(validateChassisResetActionInfoHandler, asyncResp, | 
|  | chassisId)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ChassisResetActionInfo derived class for delivering Chassis | 
|  | * ResetType AllowableValues using ResetInfo schema. | 
|  | */ | 
|  | inline void requestRoutesChassisResetActionInfo(App& app) | 
|  | { | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/") | 
|  | .privileges(redfish::privileges::getActionInfo) | 
|  | .methods(boost::beast::http::verb::get)( | 
|  | std::bind_front(handleChassisResetActionInfoGet, std::ref(app))); | 
|  | } | 
|  |  | 
|  | inline void getChassisAssembly(App& app, const crow::Request& req, | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | 
|  | const std::string& chassisId) | 
|  | { | 
|  | if (!redfish::setUpRedfishRoute(app, req, asyncResp)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | chassis_utils::getValidChassisPath( | 
|  | asyncResp, chassisId, | 
|  | [asyncResp, chassisId](const std::optional<std::string>& path) { | 
|  | if (!path) | 
|  | { | 
|  | messages::resourceNotFound(asyncResp->res, | 
|  | "#Chassis.v1_16_0.Chassis", chassisId); | 
|  | return; | 
|  | } | 
|  | boost::urls::url assemblyId = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", chassisId, "Assembly"); | 
|  | getAssembly(asyncResp, assemblyId, *path); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline void requestRoutesChassisAssembly(App& app) | 
|  | { | 
|  | BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Assembly/") | 
|  | .privileges(redfish::privileges::getAssembly) | 
|  | .methods(boost::beast::http::verb::get)( | 
|  | std::bind_front(getChassisAssembly, std::ref(app))); | 
|  | } | 
|  |  | 
|  | } // namespace redfish |