blob: a882f51e7168b347cfbf809bdd702d2dbdeeb92f [file]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_NVME_FEATURES_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_NVME_FEATURES_H_
#include <systemd/sd-bus.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "boost/beast/http/verb.hpp" // NOLINT
#include "boost/system/error_code.hpp" // NOLINT
#include "app.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "dbus_utils.hpp"
#include "json_utils.hpp"
#include "storage_utils.hpp"
#include "storage.hpp"
#include <nlohmann/json_fwd.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message.hpp"
#include "sdbusplus/message/native_types.hpp"
namespace redfish {
inline void processNvmeFeaturesCollectionGet(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& parentUri, const std::string& service,
const std::string& path) {
managedStore::ManagedObjectStoreContext context(asyncResp);
dbus_utils::getProperty<
std::vector<std::tuple<std::string, std::string, std::string>>>(
service, path, "xyz.openbmc_project.NVMe.FeatureStore",
"FeatureCollection", context,
[asyncResp, parentUri](
const boost::system::error_code& ec,
const std::vector<std::tuple<std::string, std::string, std::string>>&
features) {
if (ec) {
BMCWEB_LOG_ERROR << "Failed to get FeatureCollection: " << ec;
messages::internalError(asyncResp->res);
return;
}
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
for (const auto& feature : features) {
nlohmann::json::object_t member;
member["@odata.id"] =
absl::StrCat(parentUri, "/", std::get<0>(feature));
members.push_back(member);
}
asyncResp->res.jsonValue["@odata.id"] = parentUri;
asyncResp->res.jsonValue["@odata.type"] =
"#GoogleNvmeFeatureCollection.GoogleNvmeFeatureCollection";
asyncResp->res.jsonValue["Name"] = "NVMe Features Collection";
asyncResp->res.jsonValue["Members@odata.count"] = members.size();
});
}
inline void processNvmeFeatureGet(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& parentUri, const std::string& featureName,
const std::string& service, const std::string& path) {
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
service, path, "xyz.openbmc_project.NVMe.FeatureStore", context,
[asyncResp, parentUri, featureName, service, path](
const boost::system::error_code& ec,
const std::vector<std::pair<
std::string, dbus::utility::DbusVariantType>>& properties) {
if (ec) {
BMCWEB_LOG_ERROR << "Failed to get properties: " << ec;
messages::internalError(asyncResp->res);
return;
}
std::string revision;
std::vector<std::tuple<std::string, std::string, std::string>>
featureCollection;
bool revisionFound = false;
bool featureCollectionFound = false;
for (const auto& [propName, propVar] : properties) {
if (propName == "ProtoLibraryVersion") {
const std::string* revisionPtr = std::get_if<std::string>(&propVar);
if (revisionPtr != nullptr) {
revision = *revisionPtr;
revisionFound = true;
}
} else if (propName == "FeatureCollection") {
const auto* fcPtr = std::get_if<
std::vector<std::tuple<std::string, std::string, std::string>>>(
&propVar);
if (fcPtr != nullptr) {
featureCollection = *fcPtr;
featureCollectionFound = true;
}
}
}
if (!revisionFound || !featureCollectionFound) {
BMCWEB_LOG_ERROR << "Failed to read ProtoLibraryVersion or "
"FeatureCollection";
messages::internalError(asyncResp->res);
return;
}
std::string featureIdStr;
std::string className;
bool featureFoundInCollection = false;
for (const auto& feature : featureCollection) {
if (std::get<0>(feature) == featureName) {
featureIdStr = std::get<1>(feature);
className = std::get<2>(feature);
featureFoundInCollection = true;
break;
}
}
if (!featureFoundInCollection) {
messages::resourceNotFound(asyncResp->res, "GoogleNvmeFeature",
featureName);
return;
}
asyncResp->res.jsonValue["@odata.id"] =
absl::StrCat(parentUri, "/", featureName);
asyncResp->res.jsonValue["@odata.type"] =
"#GoogleNvmeFeature.v1_0_0.GoogleNvmeFeature";
asyncResp->res.jsonValue["Id"] = featureName;
asyncResp->res.jsonValue["Name"] = featureName;
asyncResp->res.jsonValue["FeatureId"] = featureIdStr;
size_t pos = featureName.rfind('_');
if (pos != std::string::npos) {
asyncResp->res.jsonValue["Select"] = featureName.substr(pos + 1);
}
asyncResp->res.jsonValue["DataProto"]["ClassName"] = className;
asyncResp->res.jsonValue["DataProto"]["Revision"] = revision;
managedStore::GetManagedObjectStore()
->PostDbusCallToIoContextThreadSafe(
asyncResp->strand_,
absl::AnyInvocable<void(const boost::system::error_code&,
const sdbusplus::message_t&,
const std::vector<uint8_t>&)>(
[asyncResp](const boost::system::error_code& ec2,
const sdbusplus::message_t& msg,
const std::vector<uint8_t>& payload) {
if (ec2) {
BMCWEB_LOG_ERROR << "Failed to call GetFeature: "
<< ec2;
messages::internalError(asyncResp->res);
return;
}
const ::sd_bus_error* sd_err = nullptr;
if (msg) {
sd_err = msg.get_error();
}
if (sd_err != nullptr) {
storageAddDbusError(asyncResp->res, "GetFeature", "",
sd_err->name, sd_err->message);
return;
}
asyncResp->res.jsonValue["DataProto"]["PayloadBase64"] =
crow::utility::base64encode(std::string_view(
reinterpret_cast<const char*>(payload.data()),
payload.size()));
}),
service, path, "xyz.openbmc_project.NVMe.FeatureStore",
"GetFeature", featureName);
});
}
inline absl::StatusOr<std::string> getPayloadBase64(
const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
nlohmann::json dataProto;
if (!json_util::readJsonPatch(req, asyncResp->res, "DataProto", dataProto)) {
return absl::InvalidArgumentError("Invalid or Missing DataProto");
}
std::string payloadBase64;
if (!json_util::readJson(dataProto, asyncResp->res, "PayloadBase64",
payloadBase64)) {
return absl::InvalidArgumentError("Invalid or Missing PayloadBase64");
}
return payloadBase64;
}
inline void processNvmeFeaturePatch(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& featureName, const std::string& payloadBase64,
const std::string& service, const std::string& path) {
std::string decodedPayload;
if (!crow::utility::base64Decode(payloadBase64, decodedPayload)) {
messages::propertyValueFormatError(asyncResp->res, payloadBase64,
"DataProto/PayloadBase64");
return;
}
std::vector<uint8_t> payload(decodedPayload.begin(), decodedPayload.end());
BMCWEB_LOG_DEBUG << "Patching NVMeFeature " << featureName << " on " << path;
managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe(
asyncResp->strand_,
[asyncResp, featureName](const boost::system::error_code& ec,
const sdbusplus::message_t& msg) {
const ::sd_bus_error* sd_err = msg.get_error();
if (sd_err != nullptr) {
storageAddDbusError(asyncResp->res, "SetFeature", featureName,
sd_err->name, sd_err->message);
return;
}
if (ec) {
BMCWEB_LOG_ERROR << "Failed to call SetFeature: " << ec;
messages::internalError(asyncResp->res);
return;
}
messages::success(asyncResp->res);
},
service, path, "xyz.openbmc_project.NVMe.FeatureStore", "SetFeature",
featureName, payload);
}
inline void getFeatureStoreServiceForPath(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& path,
std::function<void(const std::string& service)> cb) {
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.NVMe.FeatureStore"};
managedStore::ManagedObjectStoreContext context(asyncResp);
storage_utils::getDbusObject(
path, interfaces, context,
[asyncResp, path, cb = std::move(cb)](
const boost::system::error_code& ec,
const dbus::utility::MapperGetObject& objects) mutable {
if (ec || objects.empty()) {
BMCWEB_LOG_ERROR << "Failed to find FeatureStore interface for "
<< path;
messages::internalError(asyncResp->res);
return;
}
std::move(cb)(objects.front().first);
});
}
inline void handleControllerNvmeFeaturesCollectionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId,
const std::string& controllerId) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeatures Collection for controller "
<< controllerId;
storage_utils::findStorageAndController(
asyncResp, storageId, controllerId,
[asyncResp, systemName, storageId, controllerId](
const sdbusplus::message::object_path& path,
const dbus::utility::MapperServiceMap& ifaces) {
std::optional<std::string> service = storage_utils::matchServiceName(
ifaces, "xyz.openbmc_project.NVMe.FeatureStore");
if (!service) {
BMCWEB_LOG_ERROR << "Failed to find FeatureStore interface for "
<< path.str;
messages::internalError(asyncResp->res);
return;
}
std::string parentUri(crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName,
"Storage", storageId, "Controllers",
controllerId, "Oem", "Google", "Features")
.buffer());
processNvmeFeaturesCollectionGet(asyncResp, parentUri, *service,
path.str);
});
}
inline bool parseNvmeFeatureProperties(
const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
properties,
const std::string& featureName, std::string& revision,
std::string& featureIdStr, std::string& className, crow::Response& res) {
std::vector<std::tuple<std::string, std::string, std::string>>
featureCollection;
bool revisionFound = false;
bool featureCollectionFound = false;
for (const auto& [propName, propVar] : properties) {
if (propName == "ProtoLibraryVersion") {
const std::string* revisionPtr = std::get_if<std::string>(&propVar);
if (revisionPtr != nullptr) {
revision = *revisionPtr;
revisionFound = true;
}
} else if (propName == "FeatureCollection") {
const auto* fcPtr = std::get_if<
std::vector<std::tuple<std::string, std::string, std::string>>>(
&propVar);
if (fcPtr != nullptr) {
featureCollection = *fcPtr;
featureCollectionFound = true;
}
}
}
if (!revisionFound || !featureCollectionFound) {
BMCWEB_LOG_ERROR << "Failed to read ProtoLibraryVersion or "
"FeatureCollection";
messages::internalError(res);
return false;
}
bool featureFoundInCollection = false;
for (const auto& feature : featureCollection) {
if (std::get<0>(feature) == featureName) {
featureIdStr = std::get<1>(feature);
className = std::get<2>(feature);
featureFoundInCollection = true;
break;
}
}
if (!featureFoundInCollection) {
messages::resourceNotFound(res, "GoogleNvmeFeature", featureName);
return false;
}
return true;
}
inline void handleControllerNvmeFeatureGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId,
const std::string& controllerId, const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeature " << featureName;
storage_utils::findStorageAndController(
asyncResp, storageId, controllerId,
[asyncResp, systemName, storageId, controllerId, featureName](
const sdbusplus::message::object_path& path,
const dbus::utility::MapperServiceMap& ifaces) {
std::optional<std::string> service = storage_utils::matchServiceName(
ifaces, "xyz.openbmc_project.NVMe.FeatureStore");
if (!service) {
BMCWEB_LOG_ERROR << "Failed to find FeatureStore interface for "
<< path.str;
messages::internalError(asyncResp->res);
return;
}
std::string parentUri(crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName,
"Storage", storageId, "Controllers",
controllerId, "Oem", "Google", "Features")
.buffer());
processNvmeFeatureGet(asyncResp, parentUri, featureName, *service,
path.str);
});
}
inline void handleControllerNvmeFeaturePatch(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& /*systemName*/, const std::string& storageId,
const std::string& controllerId, const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
absl::StatusOr<std::string> payloadBase64 = getPayloadBase64(req, asyncResp);
if (!payloadBase64.ok()) {
BMCWEB_LOG_ERROR << "Failed to parse Request Payload for " << featureName
<< " Base64: " << payloadBase64.status();
return;
}
storage_utils::findStorageAndController(
asyncResp, storageId, controllerId,
[asyncResp, featureName, payloadBase64 = std::move(*payloadBase64)](
const sdbusplus::message::object_path& path,
const dbus::utility::MapperServiceMap& ifaces) {
auto service = storage_utils::matchServiceName(
ifaces, "xyz.openbmc_project.NVMe.FeatureStore");
if (!service) {
messages::internalError(asyncResp->res);
return;
}
processNvmeFeaturePatch(asyncResp, featureName, payloadBase64, *service,
path.str);
});
}
inline void handleStorageNvmeFeaturesCollectionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeatures Collection for storage " << storageId;
storage_utils::findStorage(
asyncResp, storageId,
[asyncResp, systemName,
storageId](const sdbusplus::message::object_path& storagePath) {
getFeatureStoreServiceForPath(
asyncResp, storagePath.str,
[asyncResp, systemName, storageId,
storagePath](const std::string& service) {
std::string parentUri(
crow::utility::urlFromPieces("redfish", "v1", "Systems",
systemName, "Storage", storageId,
"Oem", "Google", "Features")
.buffer());
processNvmeFeaturesCollectionGet(asyncResp, parentUri, service,
storagePath.str);
});
});
}
inline void handleStorageNvmeFeatureGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId,
const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeature " << featureName;
storage_utils::findStorage(
asyncResp, storageId,
[asyncResp, systemName, storageId,
featureName](const sdbusplus::message::object_path& storagePath) {
getFeatureStoreServiceForPath(
asyncResp, storagePath.str,
[asyncResp, systemName, storageId, featureName,
storagePath](const std::string& service) {
std::string parentUri(
crow::utility::urlFromPieces("redfish", "v1", "Systems",
systemName, "Storage", storageId,
"Oem", "Google", "Features")
.buffer());
processNvmeFeatureGet(asyncResp, parentUri, featureName, service,
storagePath.str);
});
});
}
inline void handleStorageNvmeFeaturePatch(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& /*systemName*/, const std::string& storageId,
const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
absl::StatusOr<std::string> payloadBase64 = getPayloadBase64(req, asyncResp);
if (!payloadBase64.ok()) {
BMCWEB_LOG_ERROR << "Failed to parse Request Payload for " << featureName
<< " Base64: " << payloadBase64.status();
return;
}
storage_utils::findStorage(
asyncResp, storageId,
[asyncResp, featureName, payloadBase64 = std::move(*payloadBase64)](
const sdbusplus::message::object_path& storagePath) {
getFeatureStoreServiceForPath(
asyncResp, storagePath.str,
[asyncResp, featureName, payloadBase64,
storagePath](const std::string& service) {
processNvmeFeaturePatch(asyncResp, featureName, payloadBase64,
service, storagePath.str);
});
});
}
inline void findVolumePath(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& storageId, const std::string& volumeId,
std::function<void(const sdbusplus::message::object_path& path)> cb) {
storage_utils::findStorage(
asyncResp, storageId,
[asyncResp, volumeId, cb = std::move(cb)](
const sdbusplus::message::object_path& storagePath) mutable {
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.Inventory.Item.Volume"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getAssociatedSubTree(
storagePath / "containing",
sdbusplus::message::object_path("/xyz/openbmc_project/inventory"),
0, interfaces, requestContext,
[asyncResp, volumeId, cb = std::move(cb)](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse&
subtree) mutable {
if (ec) {
messages::internalError(asyncResp->res);
return;
}
for (const auto& [path, ifaces] : subtree) {
if (sdbusplus::message::object_path(path).filename() ==
volumeId) {
std::move(cb)(path);
return;
}
}
messages::resourceNotFound(asyncResp->res, "Volume", volumeId);
});
});
}
inline void handleVolumeNvmeFeaturesCollectionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId,
const std::string& volumeId) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeatures Collection for volume " << volumeId;
findVolumePath(
asyncResp, storageId, volumeId,
[asyncResp, systemName, storageId,
volumeId](const sdbusplus::message::object_path& volumePath) {
getFeatureStoreServiceForPath(
asyncResp, volumePath.str,
[asyncResp, systemName, storageId, volumeId,
volumePath](const std::string& service) {
std::string parentUri(crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName,
"Storage", storageId, "Volumes",
volumeId, "Oem", "Google", "Features")
.buffer());
processNvmeFeaturesCollectionGet(asyncResp, parentUri, service,
volumePath.str);
});
});
}
inline void handleVolumeNvmeFeatureGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& storageId,
const std::string& volumeId, const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
BMCWEB_LOG_DEBUG << "Get NVMeFeature " << featureName;
findVolumePath(
asyncResp, storageId, volumeId,
[asyncResp, systemName, storageId, volumeId,
featureName](const sdbusplus::message::object_path& volumePath) {
getFeatureStoreServiceForPath(
asyncResp, volumePath.str,
[asyncResp, systemName, storageId, volumeId, featureName,
volumePath](const std::string& service) {
std::string parentUri(crow::utility::urlFromPieces(
"redfish", "v1", "Systems", systemName,
"Storage", storageId, "Volumes",
volumeId, "Oem", "Google", "Features")
.buffer());
processNvmeFeatureGet(asyncResp, parentUri, featureName, service,
volumePath.str);
});
});
}
inline void handleVolumeNvmeFeaturePatch(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& /*systemName*/, const std::string& storageId,
const std::string& volumeId, const std::string& featureName) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp)) {
return;
}
absl::StatusOr<std::string> payloadBase64 = getPayloadBase64(req, asyncResp);
if (!payloadBase64.ok()) {
BMCWEB_LOG_ERROR << "Failed to parse Request Payload for " << featureName
<< " Base64: " << payloadBase64.status();
return;
}
findVolumePath(
asyncResp, storageId, volumeId,
[asyncResp, featureName, payloadBase64 = std::move(*payloadBase64)](
const sdbusplus::message::object_path& volumePath) {
getFeatureStoreServiceForPath(
asyncResp, volumePath.str,
[asyncResp, featureName, payloadBase64,
volumePath](const std::string& service) {
processNvmeFeaturePatch(asyncResp, featureName, payloadBase64,
service, volumePath.str);
});
});
}
inline void requestRoutesNvmeFeatures(App& app) {
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Controllers/<str>/Oem/"
"Google/Features/")
.privileges(redfish::privileges::getStorageController)
.methods(boost::beast::http::verb::get)(std::bind_front(
handleControllerNvmeFeaturesCollectionGet, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Controllers/<str>/Oem/"
"Google/Features/<str>/")
.privileges(redfish::privileges::getStorageController)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleControllerNvmeFeatureGet, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Controllers/<str>/Oem/"
"Google/Features/<str>/")
.privileges(redfish::privileges::patchStorageController)
.methods(boost::beast::http::verb::patch)(
std::bind_front(handleControllerNvmeFeaturePatch, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Oem/Google/Features/")
.privileges(redfish::privileges::getStorage)
.methods(boost::beast::http::verb::get)(std::bind_front(
handleStorageNvmeFeaturesCollectionGet, std::ref(app)));
BMCWEB_ROUTE(
app, "/redfish/v1/Systems/<str>/Storage/<str>/Oem/Google/Features/<str>/")
.privileges(redfish::privileges::getStorage)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleStorageNvmeFeatureGet, std::ref(app)));
BMCWEB_ROUTE(
app, "/redfish/v1/Systems/<str>/Storage/<str>/Oem/Google/Features/<str>/")
.privileges(redfish::privileges::patchStorage)
.methods(boost::beast::http::verb::patch)(
std::bind_front(handleStorageNvmeFeaturePatch, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Volumes/<str>/Oem/"
"Google/Features/")
.privileges(redfish::privileges::getVolume)
.methods(boost::beast::http::verb::get)(std::bind_front(
handleVolumeNvmeFeaturesCollectionGet, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Volumes/<str>/Oem/"
"Google/Features/<str>/")
.privileges(redfish::privileges::getVolume)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleVolumeNvmeFeatureGet, std::ref(app)));
BMCWEB_ROUTE(app,
"/redfish/v1/Systems/<str>/Storage/<str>/Volumes/<str>/Oem/"
"Google/Features/<str>/")
.privileges(redfish::privileges::patchVolume)
.methods(boost::beast::http::verb::patch)(
std::bind_front(handleVolumeNvmeFeaturePatch, std::ref(app)));
}
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_NVME_FEATURES_H_