blob: d89e32eb0917357746a13df19d4cf9c118241282 [file] [log] [blame]
#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