| #pragma once |
| |
| #include "bmcweb_config.h" |
| |
| #include "dbus_utility.hpp" |
| #include "managed_store_types.hpp" |
| #include "query.hpp" |
| #include "utils/dbus_utils.hpp" |
| |
| #include <app.hpp> |
| #include <async_resp.hpp> |
| #include <dbus_utility.hpp> |
| #include <error_messages.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| #include <utils/collection.hpp> |
| #include <utils/json_utils.hpp> |
| #include <utils/storage_utils.hpp> |
| |
| #include <functional> |
| #include <vector> |
| |
| #include "managed_store.hpp" |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace crow |
| { |
| namespace google_api |
| { |
| |
| using NVMeControllerHandlerCb = std::function<void( |
| const boost::system::error_code ec, const std::string& storagePath, |
| const std::vector<std::string>& controllerList)>; |
| |
| using boost::asio::posix::stream_descriptor; |
| |
| static std::shared_ptr<boost::asio::io_context> fdIOContext; // NOLINT |
| |
| static void |
| fetchFile(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::shared_ptr<stream_descriptor>& fileConn, |
| const std::shared_ptr<std::array<char, 1024>>& readBuffer = |
| std::make_shared<std::array<char, 1024>>(), |
| const std::shared_ptr<std::string>& output = |
| std::make_shared<std::string>()) |
| { |
| if (!fileConn) |
| { |
| BMCWEB_LOG_ERROR << "Fetch File Context is not setup properly"; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| fileConn->async_read_some(boost::asio::buffer(*readBuffer), |
| [asyncResp, fileConn, readBuffer, |
| output](const boost::system::error_code& ec, |
| const std::size_t& bytesTransferred) { |
| if (ec == boost::asio::error::eof) |
| { |
| asyncResp->res.stringResponse->clear(); |
| asyncResp->res.addHeader(boost::beast::http::field::content_type, |
| "application/octet-stream"); |
| asyncResp->res.body() = std::move(*output); |
| fileConn->close(); |
| return; |
| } |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Failed to async_read_some" << ec.message(); |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| *output += std::string(readBuffer->begin(), |
| readBuffer->begin() + bytesTransferred); |
| fetchFile(asyncResp, fileConn, readBuffer, output); |
| }); |
| } |
| |
| inline void populateCustomNVMeControllerLink( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, const std::string& storageId, |
| const sdbusplus::message::object_path& controllerPath) |
| { |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Inventory.Item.StorageController"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( |
| controllerPath / customNVMeAssociation, |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| interfaces, requestContext, |
| [asyncResp, systemName, storageId, |
| controllerPath](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& |
| customNVMeList) { |
| if (ec && ec.value() != EBADR) |
| { |
| BMCWEB_LOG_ERROR << "Failed to get " << customNVMe |
| << "Controllers for controller " |
| << controllerPath.str; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (customNVMeList.size() > 1) |
| { |
| BMCWEB_LOG_ERROR << "More than 1 " << customNVMe |
| << "Controller for controller " |
| << controllerPath.str; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (customNVMeList.empty()) |
| { |
| return; |
| } |
| |
| const sdbusplus::message::object_path path{customNVMeList[0]}; |
| |
| redfish::storage_utils::findStorageForController( |
| asyncResp, storageId, path, |
| [asyncResp, systemName, |
| path](const std::optional<sdbusplus::message::object_path>& |
| canonStoragePath) { |
| if (canonStoragePath) |
| { |
| const std::string canonStorageId = canonStoragePath->filename(); |
| const std::string id = path.filename(); |
| asyncResp->res.jsonValue[std::string(customNVMe) + "Controller"] |
| ["@odata.id"] = |
| crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", systemName, "Storage", |
| canonStorageId, "Controllers", id); |
| } |
| }); |
| }); |
| } |
| |
| inline void checkPrimary(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const sdbusplus::message::object_path& controllerPath) |
| { |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Inventory.Item.StorageController"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| |
| managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( |
| controllerPath / "primary", |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| interfaces, requestContext, |
| [asyncResp, |
| controllerPath](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& |
| controllerList) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "getAssociatedSubTreePaths for path " |
| << controllerPath.str << " failed with code " |
| << ec; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| asyncResp->res.jsonValue["ControllerType"] = |
| controllerList.empty() ? "Primary" : "Secondary"; |
| }); |
| } |
| |
| inline void populateNVMeController( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, const std::string& nvmeId, |
| const std::string& controllerId, const std::string& controllerPath) |
| { |
| constexpr std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.NVMe.NVMeAdmin"}; |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| controllerPath, interfaces, context, |
| [asyncResp, systemName, nvmeId, controllerId, |
| controllerPath](const boost::system::error_code ec, |
| const dbus::utility::MapperGetObject& objects) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "GetObject for path " << controllerPath |
| << " failed with code " << ec; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (objects.size() != 1) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| BMCWEB_LOG_ERROR << "Service supporting " << controllerPath |
| << " is not equal to 1"; |
| return; |
| } |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#NVMeController.v1_0_0.NVMeController"; |
| asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( |
| "google", "v1", "NVMe", nvmeId, "Controllers", controllerId); |
| asyncResp->res.jsonValue["Name"] = "Google NVMe Controller"; |
| |
| // NVMe actions |
| asyncResp->res |
| .jsonValue["Actions"]["#NVMeController.AdminNonDataCmd"]["target"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId, |
| "Controllers", controllerId, "Actions", |
| "NVMeController.AdminNonDataCmd"); |
| asyncResp->res |
| .jsonValue["Actions"]["#NVMeController.GetLogPage"]["target"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId, |
| "Controllers", controllerId, "Actions", |
| "NVMeController.GetLogPage"); |
| asyncResp->res |
| .jsonValue["Actions"]["#NVMeController.Identify"]["target"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId, |
| "Controllers", controllerId, "Actions", |
| "NVMeController.Identify"); |
| asyncResp->res.jsonValue["Links"]["NVMe"]["@odata.id"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId); |
| asyncResp->res.jsonValue["StorageController"]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, |
| "Storage", nvmeId, "Controllers", |
| controllerId); |
| if (enableCustomNVMe) |
| { |
| asyncResp->res.jsonValue["Actions"]["#NVMeController." + |
| std::string(customNVMe) + |
| "Identify"]["target"] = |
| crow::utility::urlFromPieces( |
| "google", "v1", "NVMe", nvmeId, "Controllers", controllerId, |
| "Actions", |
| "NVMeController." + std::string(customNVMe) + "Identify"); |
| populateCustomNVMeControllerLink(asyncResp, systemName, nvmeId, |
| controllerPath); |
| checkPrimary(asyncResp, controllerPath); |
| } |
| }); |
| } |
| |
| inline void |
| setupNVMeController(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& nvmeId, |
| const std::string& controllerId, |
| const std::vector<std::string>& controllerList) |
| { |
| if (controllerList.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| for (const auto& controller : controllerList) |
| { |
| if (sdbusplus::message::object_path(controller).filename() != |
| controllerId) |
| { |
| continue; |
| } |
| populateNVMeController(asyncResp, systemName, nvmeId, controllerId, controller); |
| return; |
| } |
| |
| redfish::messages::internalError(asyncResp->res); |
| } |
| |
| inline void populateNVMeControllerCollection( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvme, const boost::system::error_code ec, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::vector<std::string>& controllerList) |
| { |
| if (ec == boost::system::errc::io_error || controllerList.empty()) |
| { |
| auto controllers = nlohmann::json::object(); |
| controllers["Name"] = "Google NVMe Controller Collection"; |
| controllers["@odata.id"] = crow::utility::urlFromPieces( |
| "google", "v1", "NVMe", nvme, "Controllers"); |
| controllers["Members"] = nlohmann::json::array(); |
| controllers["Members@odata.count"] = 0; |
| asyncResp->res.jsonValue[jsonPtr] = std::move(controllers); |
| return; |
| } |
| |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec.value(); |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| auto controllers = nlohmann::json::object(); |
| controllers["Name"] = "Google NVMe Controller Collection"; |
| controllers["@odata.id"] = crow::utility::urlFromPieces( |
| "google", "v1", "NVMe", nvme, "Controllers"); |
| nlohmann::json members = nlohmann::json::array(); |
| for (const std::string& controller : controllerList) |
| { |
| std::string name = |
| sdbusplus::message::object_path(controller).filename(); |
| if (name.empty()) |
| { |
| continue; |
| } |
| nlohmann::json::object_t member; |
| member["@odata.id"] = crow::utility::urlFromPieces( |
| "google", "v1", "NVMe", nvme, "Controllers", name); |
| members.push_back(std::move(member)); |
| } |
| controllers["Members@odata.count"] = members.size(); |
| controllers["Members"] = std::move(members); |
| asyncResp->res.jsonValue[jsonPtr] = std::move(controllers); |
| } |
| |
| inline void |
| processDriveResource(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::optional<std::string>& nvme, |
| std::function<void(const std::string&)>&& cb) |
| { |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| constexpr std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.Drive"}; |
| managedStore::GetManagedObjectStore()->getSubTree( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [asyncResp, nvme, requestContext, |
| cb{std::forward<std::function<void(const std::string&)>>(cb)}]( |
| const boost::system::error_code ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_INFO << "DBUS error: no matched iface for Drive: " |
| << ec.message(); |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| for (const auto& [path, services] : subtree) |
| { |
| if (nvme.has_value() && |
| sdbusplus::message::object_path(path).filename() != *nvme) |
| { |
| continue; |
| } |
| |
| for (const auto& [service, interfaces] : services) |
| { |
| for (const std::string& interface : interfaces) |
| { |
| if (interface != "xyz.openbmc_project.Inventory.Item.Drive") |
| { |
| continue; |
| } |
| |
| redfish::dbus_utils::getProperty<std::string>( |
| service, path, interface, "Protocol", requestContext, |
| [path{path}, cb](const boost::system::error_code ec2, |
| const std::string& driveProtocol) { |
| if (ec2) |
| { |
| return; |
| } |
| |
| if (driveProtocol != |
| "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe") |
| { |
| return; |
| } |
| |
| cb(path); |
| }); |
| |
| // If expecting valid NVMe, return after finding the first |
| // NVMe. |
| if (nvme.has_value()) |
| { |
| return; |
| } |
| } |
| } |
| } |
| // Expecting valid NVMe |
| if (nvme.has_value()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| } |
| }); |
| } |
| |
| inline void handleGoogleNvmeController( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvme, NVMeControllerHandlerCb&& cb) |
| { |
| processDriveResource( |
| asyncResp, nvme, |
| [asyncResp, cb{std::forward<NVMeControllerHandlerCb>(cb)}]( |
| const std::string& path) mutable { |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| redfish::dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", path + "/storage_controller", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| [cb{std::move(cb)}, |
| path](const boost::system::error_code ec, |
| const std::vector<std::string>& controllerList) { |
| cb(ec, path, controllerList); |
| }); |
| }); |
| } |
| |
| inline void populateNVMe(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& path, const std::string& nvmeId) |
| { |
| asyncResp->res.jsonValue["@odata.type"] = "#NVMe.v1_0_0.NVMe"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId); |
| asyncResp->res.jsonValue["Name"] = nvmeId; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| redfish::dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", path + "/storage", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| [asyncResp, nvmeId, |
| requestContext](const boost::system::error_code ec, |
| const std::vector<std::string>& storageList) { |
| if (ec || storageList.empty()) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Failed to find storage that is associated with " << nvmeId; |
| return; |
| } |
| if (storageList.size() > 1) |
| { |
| BMCWEB_LOG_DEBUG << nvmeId |
| << " is associated with mutliple storages"; |
| return; |
| } |
| |
| sdbusplus::message::object_path storagePath(storageList[0]); |
| std::string storageId = storagePath.filename(); |
| if (storageId.empty()) |
| { |
| BMCWEB_LOG_ERROR << "filename() is empty in " << storagePath.str; |
| return; |
| } |
| redfish::storage_utils::getSystemPathFromStorage( |
| asyncResp, storagePath, |
| [asyncResp, storageId](std::optional<std::string_view> systemPath) { |
| std::string systemName = |
| systemPath ? std::filesystem::path(*systemPath).filename() |
| : "system"; |
| asyncResp->res.jsonValue["Links"]["Storage"]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", |
| systemName, "Storage", storageId); |
| }); |
| }); |
| |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.NVMe.NVMeAdmin"}; |
| managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( |
| path + "/storage_controller", |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, |
| interfaces, requestContext, |
| [asyncResp, nvmeId](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& |
| controllerList) { |
| populateNVMeControllerCollection(asyncResp, nvmeId, ec, |
| "/Controllers"_json_pointer, controllerList); |
| }); |
| } |
| |
| inline void |
| handleGoogleNvme(App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvme) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| processDriveResource(asyncResp, nvme, |
| [asyncResp, nvme](const std::string& path) { |
| populateNVMe(asyncResp, path, nvme); |
| }); |
| } |
| |
| inline void handleGoogleNvmeControllerCollection( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvme) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| processDriveResource(asyncResp, nvme, |
| [asyncResp, nvme](const std::string& path) { |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.NVMe.NVMeAdmin"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( |
| path + "/storage_controller", |
| sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), |
| 0, interfaces, requestContext, |
| [asyncResp, |
| nvme](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& |
| controllerList) { |
| populateNVMeControllerCollection(asyncResp, nvme, ec, |
| ""_json_pointer, controllerList); |
| }); |
| }); |
| } |
| |
| inline void handleGoogleNvmeCollection( |
| 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"] = |
| "#NVMeCollection.v1_0_0.NVMeCollection"; |
| asyncResp->res.jsonValue["@odata.id"] = "/google/v1/NVMe"; |
| asyncResp->res.jsonValue["Name"] = "Google NVMe Collection"; |
| |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; |
| |
| processDriveResource(asyncResp, std::nullopt, |
| [asyncResp](const std::string& path) { |
| std::string id = sdbusplus::message::object_path(path).filename(); |
| if (id.empty()) |
| { |
| return; |
| } |
| |
| nlohmann::json& members = asyncResp->res.jsonValue["Members"]; |
| nlohmann::json::object_t member; |
| member["@odata.id"] = |
| crow::utility::urlFromPieces("google", "v1", "NVMe", id); |
| members.push_back(std::move(member)); |
| asyncResp->res.jsonValue["Members@odata.count"] = members.size(); |
| }); |
| } |
| |
| inline void getIdentity( |
| uint8_t cns, uint16_t cntid, uint32_t nsid, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& controllerId, const boost::system::error_code ec, |
| const std::vector<std::string>& controllerList, |
| const std::string& adminInterface = "xyz.openbmc_project.NVMe.NVMeAdmin") |
| { |
| if (ec || controllerList.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| std::string controllerPath; |
| for (const std::string& controller : controllerList) |
| { |
| if (sdbusplus::message::object_path(controller).filename() == |
| controllerId) |
| { |
| controllerPath = controller; |
| break; |
| } |
| } |
| |
| if (controllerPath.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| const std::string_view adminInterfaceView = adminInterface; |
| std::span<const std::string_view> interfaces(&adminInterfaceView, 1); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| controllerPath, interfaces, context, |
| [asyncResp, cns, cntid, nsid, controllerPath, |
| adminInterface](const boost::system::error_code ec2, |
| const dbus::utility::MapperGetObject& objects) { |
| if (ec2) |
| { |
| BMCWEB_LOG_ERROR << "GetObject for path " << controllerPath |
| << " failed with code " << ec2; |
| return; |
| } |
| |
| if (objects.size() != 1) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| BMCWEB_LOG_ERROR << "Service supporting " << controllerPath |
| << " is not equal to 1"; |
| return; |
| } |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp, objects, |
| controllerPath](const boost::system::error_code ec3, |
| const sdbusplus::message::unix_fd& fd) { |
| if (ec3) |
| { |
| BMCWEB_LOG_ERROR << "Failed to call NVMe Identity: " |
| << ec3.message(); |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| int dupFd = dup(fd.fd); |
| fetchFile(asyncResp, |
| std::make_shared<stream_descriptor>(*fdIOContext, dupFd)); |
| }, |
| objects[0].first, controllerPath, adminInterface, "Identify", cns, |
| nsid, cntid); |
| }); |
| } |
| |
| inline void handleGoogleNvmeControllerCustomNVMeIdentifyAction( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvmeId, const std::string& controllerId) |
| { |
| uint8_t cns = 0; // Controller or Namespace Structure |
| uint16_t cntid = 0; // Controller Identifier |
| uint32_t nsid = 0; // Namespace Identifier |
| |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (!redfish::json_util::readJsonAction(req, asyncResp->res, "CNS", cns, |
| "CNTID", cntid, "NSID", nsid)) |
| { |
| redfish::messages::actionParameterMissing( |
| asyncResp->res, "ControllerNamespace", "ControllerNamespaceId"); |
| return; |
| } |
| handleGoogleNvmeController( |
| asyncResp, nvmeId, |
| [cns, cntid, nsid, asyncResp, |
| controllerId](const boost::system::error_code ec, |
| const std::string& /* storagePath */, |
| const std::vector<std::string>& controllerList) { |
| getIdentity( |
| cns, cntid, nsid, asyncResp, controllerId, ec, controllerList, |
| "xyz.openbmc_project.NVMe." + std::string(customNVMe) + "Admin"); |
| }); |
| } |
| |
| inline void handleGoogleNvmeControllerIdentifyAction( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvmeId, const std::string& controllerId) |
| { |
| uint8_t cns = 0; // Controller or Namespace Structure |
| uint16_t cntid = 0; // Controller Identifier |
| uint32_t nsid = 0; // Namespace Identifier |
| |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (!redfish::json_util::readJsonAction(req, asyncResp->res, "CNS", cns, |
| "CNTID", cntid, "NSID", nsid)) |
| { |
| redfish::messages::actionParameterMissing( |
| asyncResp->res, "ControllerNamespace", "ControllerNamespaceId"); |
| return; |
| } |
| handleGoogleNvmeController( |
| asyncResp, nvmeId, |
| [cns, cntid, nsid, asyncResp, |
| controllerId](const boost::system::error_code ec, |
| const std::string& /* storagePath */, |
| const std::vector<std::string>& controllerList) { |
| getIdentity(cns, cntid, nsid, asyncResp, controllerId, ec, |
| controllerList); |
| }); |
| } |
| |
| inline void adminNonDataCmd([[maybe_unused]] uint8_t opcode,[[maybe_unused]] uint32_t cdw1,[[maybe_unused]] uint32_t cdw2, |
| [[maybe_unused]] uint32_t cdw3,[[maybe_unused]] uint32_t cdw10,[[maybe_unused]] uint32_t cdw11, |
| [[maybe_unused]] uint32_t cdw12,[[maybe_unused]] uint32_t cdw13,[[maybe_unused]] uint32_t cdw14, |
| [[maybe_unused]] uint32_t cdw15, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& controllerId, |
| const boost::system::error_code ec, |
| const std::vector<std::string>& controllerList) |
| { |
| if (ec || controllerList.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| std::string controllerPath; |
| for (const std::string& controller : controllerList) |
| { |
| if (sdbusplus::message::object_path(controller).filename() == |
| controllerId) |
| { |
| controllerPath = controller; |
| break; |
| } |
| } |
| |
| if (controllerPath.empty()) |
| { |
| BMCWEB_LOG_ERROR << "failed to find DBus controller path for " |
| << controllerId; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // The PostDbusCallToIoContextThreadSafe is NOT unitestable. MOCK_METHOD dosent work with this many params. |
| // If we want to unit test this funciton, we probably will have to change dbus. |
| #ifndef UNIT_TEST_BUILD |
| |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.NVMe.Passthru"}; |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| controllerPath, interfaces, context, |
| [asyncResp, opcode, cdw1, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, |
| cdw15, controllerPath](const boost::system::error_code ec2, |
| const dbus::utility::MapperGetObject& objects) { |
| if (ec2) |
| { |
| BMCWEB_LOG_ERROR << "GetObject for path " << controllerPath |
| << " failed with code " << ec2; |
| return; |
| } |
| |
| if (objects.size() != 1) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| BMCWEB_LOG_ERROR << "Service supporting " << controllerPath |
| << " is not equal to 1"; |
| return; |
| } |
| |
| const std::string& service {objects[0].first}; |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp]( |
| const boost::system::error_code ec3, |
| const sdbusplus::message_t& msg, |
| const std::tuple<uint32_t, uint32_t, uint32_t>& response) { |
| const ::sd_bus_error* sd_err = msg.get_error(); |
| if (sd_err != nullptr) |
| { |
| redfish::messages::generalError(asyncResp->res); |
| if (sd_err->message != nullptr) |
| { |
| BMCWEB_LOG_ERROR << "Error: " << sd_err->name << " message " |
| << sd_err->message; |
| asyncResp->res.jsonValue["error"]["message"] = |
| sd_err->message; |
| } |
| return; |
| } |
| |
| if (ec3) |
| { |
| BMCWEB_LOG_ERROR << "AdminNonDataCmd dbus error " << ec3; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // Success |
| asyncResp->res.jsonValue["MIStatus"] = std::get<0>(response); |
| asyncResp->res.jsonValue["AdminStatus"] = std::get<1>(response); |
| asyncResp->res.jsonValue["CompletionDW0"] = std::get<2>(response); |
| }, |
| service, controllerPath, "xyz.openbmc_project.NVMe.Passthru", |
| "AdminNonDataCmd", opcode, cdw1, cdw2, cdw3, cdw10, cdw11, cdw12, |
| cdw13, cdw14, cdw15); |
| }); |
| #endif |
| } |
| |
| inline void getLogPage( |
| uint8_t lid, // Log Page Identifier, Command Dword 10 bits[07:00] |
| uint32_t nsid, |
| uint8_t lsp, // Log Specific Field, Command Dword 10 bits[14:08] |
| uint16_t lsi, // Log Specific Identifier, Command Dword 11 bits[31:16] |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& controllerId, const boost::system::error_code ec, |
| const std::vector<std::string>& controllerList) |
| { |
| if (ec || controllerList.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| std::string controllerPath; |
| for (const std::string& controller : controllerList) |
| { |
| if (sdbusplus::message::object_path(controller).filename() == |
| controllerId) |
| { |
| controllerPath = controller; |
| break; |
| } |
| } |
| |
| if (controllerPath.empty()) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.NVMe.NVMeAdmin"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| controllerPath, interfaces, requestContext, |
| [asyncResp, lid, nsid, lsp, lsi, |
| controllerPath](const boost::system::error_code ec2, |
| const dbus::utility::MapperGetObject& objects) { |
| if (ec2) |
| { |
| BMCWEB_LOG_ERROR << "GetObject for path " << controllerPath |
| << " failed with code " << ec2; |
| return; |
| } |
| |
| if (objects.size() != 1) |
| { |
| BMCWEB_LOG_ERROR << "Service supporting " << controllerPath |
| << " is not equal to 1"; |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp, objects, |
| controllerPath](const boost::system::error_code ec3, |
| const sdbusplus::message::unix_fd& fd) { |
| if (ec3) |
| { |
| BMCWEB_LOG_INFO << "Failed to call NVMe Log " << ec3.message(); |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| int dupFd = dup(fd.fd); |
| fetchFile(asyncResp, |
| std::make_shared<stream_descriptor>(*fdIOContext, dupFd)); |
| }, |
| objects[0].first, controllerPath, |
| "xyz.openbmc_project.NVMe.NVMeAdmin", "GetLogPage", lid, nsid, lsp, |
| lsi); |
| }); |
| } |
| |
| inline void handleGoogleNvmeControllerLogAction( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvmeId, const std::string& controllerId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| uint8_t lid = 0; // Log Page Identifier, Command Dword 10 bits[07:00] |
| uint32_t nsid = 0; |
| uint8_t lsp = 0; // Log Specific Field, Command Dword 10 bits[14:08] |
| uint16_t lsi = 0; // Log Specific Identifier, Command Dword 11 bits[31:16] |
| |
| if (!redfish::json_util::readJsonAction(req, asyncResp->res, "LID", lid, |
| "NSID", nsid, "LSP", lsp, "LSI", |
| lsi)) |
| { |
| redfish::messages::actionParameterMissing(asyncResp->res, |
| "LogSpecificId", "LogId"); |
| return; |
| } |
| |
| handleGoogleNvmeController( |
| asyncResp, nvmeId, |
| [lid, nsid, lsp, lsi, asyncResp, |
| controllerId](const boost::system::error_code ec, |
| const std::string& /* storagePath */, |
| const std::vector<std::string>& controllerList) { |
| getLogPage(lid, nsid, lsp, lsi, asyncResp, controllerId, ec, |
| controllerList); |
| }); |
| } |
| |
| inline void handleGoogleNvmeControllerAdminNonDataAction( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvmeId, const std::string& controllerId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| // Only opcode is mandatory |
| uint8_t opcode = 0; |
| std::optional<uint32_t> cdw1; |
| std::optional<uint32_t> cdw2; |
| std::optional<uint32_t> cdw3; |
| std::optional<uint32_t> cdw10; |
| std::optional<uint32_t> cdw11; |
| std::optional<uint32_t> cdw12; |
| std::optional<uint32_t> cdw13; |
| std::optional<uint32_t> cdw14; |
| std::optional<uint32_t> cdw15; |
| |
| if (!redfish::json_util::readJsonAction( |
| req, asyncResp->res, "opcode", opcode, "cdw1", cdw1, "cdw2", cdw2, |
| "cdw3", cdw3, "cdw10", cdw10, "cdw11", cdw11, "cdw12", cdw12, |
| "cdw13", cdw13, "cdw14", cdw14, "cdw15", cdw15)) |
| { |
| redfish::messages::actionParameterMissing( |
| asyncResp->res, "NVMeController.AdminNonDataCmd", "opcode"); |
| return; |
| } |
| |
| handleGoogleNvmeController( |
| asyncResp, nvmeId, |
| [opcode, cdw1{cdw1.value_or(0)}, cdw2{cdw2.value_or(0)}, |
| cdw3{cdw3.value_or(0)}, cdw10{cdw10.value_or(0)}, |
| cdw11{cdw11.value_or(0)}, cdw12{cdw12.value_or(0)}, |
| cdw13{cdw13.value_or(0)}, cdw14{cdw14.value_or(0)}, |
| cdw15{cdw15.value_or(0)}, asyncResp, |
| controllerId](const boost::system::error_code ec, |
| const std::string& /* storagePath */, |
| const std::vector<std::string>& controllerList) { |
| adminNonDataCmd(opcode, cdw1, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, |
| cdw14, cdw15, asyncResp, controllerId, ec, |
| controllerList); |
| }); |
| } |
| |
| inline void setupGoogleNVMeFdFetchIOContext( |
| const std::shared_ptr<boost::asio::io_context>& ioc) |
| { |
| fdIOContext = ioc; |
| } |
| |
| inline void requestGoogleNVMeControllerActionCustomNVMeIdentify(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, "/google/v1/NVMe/<str>/Controllers/<str>/Actions/NVMeController." + |
| std::string(customNVMe) + "Identify") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleGoogleNvmeControllerCustomNVMeIdentifyAction, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMeControllerActionIdentify(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/google/v1/NVMe/<str>/Controllers/<str>/Actions/NVMeController.Identify") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleGoogleNvmeControllerIdentifyAction, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMeControllerActionLog(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/google/v1/NVMe/<str>/Controllers/<str>/Actions/NVMeController.GetLogPage") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleGoogleNvmeControllerLogAction, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMeControllerActionAdminNonData(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/google/v1/NVMe/<str>/Controllers/<str>/Actions/NVMeController.AdminNonDataCmd") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleGoogleNvmeControllerAdminNonDataAction, std::ref(app))); |
| } |
| |
| inline void handleGoogleNVMeControllerGet( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& nvmeId, const std::string& controllerId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| handleGoogleNvmeController( |
| asyncResp, nvmeId, |
| [asyncResp, nvmeId, controllerId]( |
| const boost::system::error_code ec, const std::string& storagePath, |
| const std::vector<std::string>& controllerList) { |
| if (ec) |
| { |
| redfish::messages::internalError(asyncResp->res); |
| return; |
| } |
| redfish::storage_utils::getSystemPathFromStorage( |
| asyncResp, storagePath, |
| [asyncResp, nvmeId, controllerId, |
| controllerList](std::optional<std::string_view> systemPath) { |
| std::string systemName = |
| systemPath ? std::filesystem::path(*systemPath).filename() |
| : "system"; |
| setupNVMeController(asyncResp, systemName, nvmeId, controllerId, |
| controllerList); |
| }); |
| }); |
| } |
| |
| inline void requestGoogleNVMeController(App& app) |
| { |
| BMCWEB_ROUTE(app, "/google/v1/NVMe/<str>/Controllers/<str>") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleGoogleNVMeControllerGet, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMeControllerCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, "/google/v1/NVMe/<str>/Controllers") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::get)(std::bind_front( |
| handleGoogleNvmeControllerCollection, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMe(App& app) |
| { |
| BMCWEB_ROUTE(app, "/google/v1/NVMe/<str>") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleGoogleNvme, std::ref(app))); |
| } |
| |
| inline void requestGoogleNVMeCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, "/google/v1/NVMe") |
| .privileges({{"ConfigureManager"}}) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleGoogleNvmeCollection, std::ref(app))); |
| } |
| |
| } // namespace google_api |
| } // namespace crow |