blob: feaa204cf95bb628aac5ed0fcaa5c7bdeb1f844a [file]
#ifndef THIRD_PARTY_GBMCWEB_INCLUDE_GOOGLE_GOOGLE_SERVICE_NVME_H_
#define THIRD_PARTY_GBMCWEB_INCLUDE_GOOGLE_GOOGLE_SERVICE_NVME_H_
#include <systemd/sd-bus.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem> // NOLINT
#include <functional>
#include <memory>
#include <optional>
#include <span> // NOLINT
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "bmcweb_config.h"
#include "app.hpp"
#include "http_request.hpp"
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "query.hpp"
#include "dbus_utils.hpp"
#include "json_utils.hpp"
#include "storage_utils.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message.hpp"
#include "sdbusplus/message/native_types.hpp"
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.LockdownInband"]["target"] =
crow::utility::urlFromPieces("google", "v1", "NVMe", nvmeId,
"Controllers", controllerId, "Actions",
"NVMeController.LockdownInband");
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 lockdownInband(const uint8_t prohibit,
const std::vector<uint8_t>& adminCmds,
const std::vector<uint8_t>& features,
const std::vector<uint8_t>& logPages,
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;
}
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.NVMe.Lockdown"};
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getDbusObject(
controllerPath, interfaces, context,
[asyncResp, prohibit, adminCmds, features, logPages, 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_,
absl::AnyInvocable<void(
const boost::system::error_code&,
const sdbusplus::message_t&,
const std::tuple<uint32_t, uint32_t, uint32_t, std::string,
uint32_t>&)>(
[asyncResp](
const boost::system::error_code& ec3,
const sdbusplus::message_t& msg,
const std::tuple<uint32_t, uint32_t, uint32_t,
std::string, uint32_t>& response) {
const ::sd_bus_error* sdErr = msg.get_error();
if (sdErr != nullptr) {
redfish::messages::generalError(asyncResp->res);
if (sdErr->message != nullptr) {
BMCWEB_LOG_ERROR << "Error: " << sdErr->name
<< " message " << sdErr->message;
asyncResp->res.jsonValue["error"]["message"] =
sdErr->message;
}
return;
}
if (ec3) {
BMCWEB_LOG_ERROR << "LockdownInband dbus error " << ec3;
redfish::messages::internalError(asyncResp->res);
return;
}
// Success
uint32_t miStatus = std::get<0>(response);
uint32_t adminStatus = std::get<1>(response);
asyncResp->res.jsonValue["MIStatus"] = miStatus;
asyncResp->res.jsonValue["AdminStatus"] = adminStatus;
asyncResp->res.jsonValue["CompletionDW0"] =
std::get<2>(response);
if (miStatus != 0 || adminStatus != 0) {
asyncResp->res.jsonValue["FailureScope"] =
std::get<3>(response);
asyncResp->res.jsonValue["FailureId"] =
std::get<4>(response);
}
}),
service, controllerPath, "xyz.openbmc_project.NVMe.Lockdown",
"LockdownInband", prohibit, adminCmds, features, logPages);
});
}
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 handleGoogleNvmeControllerLockdownInband(
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;
}
bool prohibit = false;
std::optional<std::vector<uint8_t>> adminCmds;
std::optional<std::vector<uint8_t>> features;
std::optional<std::vector<uint8_t>> logPages;
if (!redfish::json_util::readJsonAction(
req, asyncResp->res, "Prohibit", prohibit, "AdminCmds", adminCmds,
"Features", features, "LogPages", logPages)) {
redfish::messages::actionParameterMissing(
asyncResp->res, "NVMeController.LockdownInband", "Prohibit");
return;
}
handleGoogleNvmeController(
asyncResp, nvmeId,
[prohibit, adminCmds, features, logPages, asyncResp, controllerId](
const boost::system::error_code& ec,
const std::string& /* storagePath */,
const std::vector<std::string>& controllerList) {
lockdownInband(prohibit ? 1 : 0,
adminCmds.value_or(std::vector<uint8_t>{}),
features.value_or(std::vector<uint8_t>{}),
logPages.value_or(std::vector<uint8_t>{}), 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 requestGoogleNVMeControllerActionLockdownInband(App& app) {
BMCWEB_ROUTE(app,
"/google/v1/NVMe/<str>/Controllers/<str>/Actions/"
"NVMeController.LockdownInband")
.privileges({{"ConfigureManager"}})
.methods(boost::beast::http::verb::post)(std::bind_front(
handleGoogleNvmeControllerLockdownInband, 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
#endif // THIRD_PARTY_GBMCWEB_INCLUDE_GOOGLE_GOOGLE_SERVICE_NVME_H_