Add Storage Schema
This takes the original commit below and updates it so that it
passes the validatior, and provides the Status attribute in
redfish when appropriate.
Tested: Passed the validator
{
"@odata.context": "/redfish/v1/$metadata#Drive.Drive",
"@odata.id": "/redfish/v1/Systems/system/Storage/1/Drive/Drive_2",
"@odata.type": "#Drive.v1_2_0.Drive",
"Id": "Drive_2",
"Manufacturer": "INTEL",
"Model": "P4800X",
"Name": "Drive_2",
"PartNumber": "INTEL SSDPE21K375GA",
"SerialNumber": "PHKE722600NL375AGN",
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
}
}
Original Commit Message:
-------------------------------------------------------------------
Add Storage Schema for NVMe on Redfish
This provides an implementation for the Get methods for the Storage
schemas using following classes :
- StorageCollection
- Storage
Tested:
- Ran Redfish Service Validator to verify no issues are reported.
- Tested that the NVMe drives in the system show up and proper fields
are populated with appropriate data.
- Tested with no drives present. Made sure the Storage interface shows
no drives and Drive interface returns error message.
Change-Id: Id0306ea413ac16a993110bb1a36cd95d939cff71
Signed-off-by: Nikhil Potade <nikhil.potade@linux.intel.com>
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index bf0f51f..b39ea0b 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -30,6 +30,7 @@
#include "../lib/roles.hpp"
#include "../lib/sensors.hpp"
#include "../lib/service_root.hpp"
+#include "../lib/storage.hpp"
#include "../lib/systems.hpp"
#include "../lib/thermal.hpp"
#include "../lib/update_service.hpp"
@@ -71,6 +72,9 @@
nodes.emplace_back(std::make_unique<ChassisCollection>(app));
nodes.emplace_back(std::make_unique<Chassis>(app));
nodes.emplace_back(std::make_unique<UpdateService>(app));
+ nodes.emplace_back(std::make_unique<StorageCollection>(app));
+ nodes.emplace_back(std::make_unique<Storage>(app));
+ nodes.emplace_back(std::make_unique<Drive>(app));
#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
nodes.emplace_back(
std::make_unique<UpdateServiceActionsSimpleUpdate>(app));
diff --git a/redfish-core/lib/storage.hpp b/redfish-core/lib/storage.hpp
new file mode 100644
index 0000000..3c4fe4c
--- /dev/null
+++ b/redfish-core/lib/storage.hpp
@@ -0,0 +1,286 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+
+#include <node.hpp>
+
+namespace redfish
+{
+class StorageCollection : public Node
+{
+ public:
+ StorageCollection(CrowApp &app) :
+ Node(app, "/redfish/v1/Systems/system/Storage/")
+ {
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> ¶ms) override
+ {
+ res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection";
+ res.jsonValue["@odata.context"] =
+ "/redfish/v1/$metadata#StorageCollection.StorageCollection";
+ res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage";
+ res.jsonValue["Name"] = "Storage Collection";
+ res.jsonValue["Members"] = {
+ {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
+ res.jsonValue["Members@odata.count"] = 1;
+ res.end();
+ }
+};
+
+class Storage : public Node
+{
+ public:
+ Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1")
+ {
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> ¶ms) override
+ {
+ res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
+ res.jsonValue["@odata.context"] =
+ "/redfish/v1/$metadata#Storage.Storage";
+ res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
+ res.jsonValue["Name"] = "Storage Controller";
+ res.jsonValue["Id"] = "1";
+
+ auto asyncResp = std::make_shared<AsyncResp>(res);
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+ const std::vector<std::string> &storageList) {
+ nlohmann::json &storageArray =
+ asyncResp->res.jsonValue["Drives"];
+ storageArray = nlohmann::json::array();
+ asyncResp->res.jsonValue["Drives@odata.count"] = 0;
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Drive mapper call error";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ for (const std::string &objpath : storageList)
+ {
+ std::size_t lastPos = objpath.rfind("/");
+ if (lastPos == std::string::npos ||
+ (objpath.size() <= lastPos + 1))
+ {
+ BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
+ continue;
+ }
+
+ storageArray.push_back(
+ {{"@odata.id",
+ "/redfish/v1/Systems/system/Storage/1/Drive/" +
+ objpath.substr(lastPos + 1)}});
+ }
+
+ asyncResp->res.jsonValue["Drives@odata.count"] =
+ storageArray.size();
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
+ "/xyz/openbmc_project/inventory", int32_t(0),
+ std::array<const char *, 1>{
+ "xyz.openbmc_project.Inventory.Item.Drive"});
+ }
+};
+
+class Drive : public Node
+{
+ public:
+ Drive(CrowApp &app) :
+ Node(app, "/redfish/v1/Systems/system/Storage/1/Drive/<str>/",
+ std::string())
+ {
+ entityPrivileges = {
+ {boost::beast::http::verb::get, {{"Login"}}},
+ {boost::beast::http::verb::head, {{"Login"}}},
+ {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
+ {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
+ }
+
+ private:
+ void doGet(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> ¶ms) override
+ {
+ const std::string &driveId = params[0];
+
+ auto asyncResp = std::make_shared<AsyncResp>(res);
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, driveId](
+ const boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string, std::vector<std::pair<
+ std::string, std::vector<std::string>>>>>
+ &subtree) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Drive mapper call error";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ auto object = std::find_if(
+ subtree.begin(), subtree.end(), [&driveId](auto &object) {
+ const std::string &path = object.first;
+ return boost::ends_with(path, "/" + driveId);
+ });
+
+ if (object == subtree.end())
+ {
+ messages::resourceNotFound(asyncResp->res, "Drive",
+ driveId);
+ return;
+ }
+
+ const std::string &path = object->first;
+ const std::vector<
+ std::pair<std::string, std::vector<std::string>>>
+ &connectionNames = object->second;
+
+ asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
+ asyncResp->res.jsonValue["@odata.context"] =
+ "/redfish/v1/$metadata#Drive.Drive";
+ asyncResp->res.jsonValue["@odata.id"] =
+ "/redfish/v1/Systems/system/Storage/1/Drive/" + driveId;
+
+ if (connectionNames.size() != 1)
+ {
+ BMCWEB_LOG_ERROR << "Connection size "
+ << connectionNames.size()
+ << ", greater than 1";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ getMainChassisId(
+ asyncResp, [](const std::string &chassisId,
+ std::shared_ptr<AsyncResp> aRsp) {
+ aRsp->res.jsonValue["Links"]["Chassis"] = {
+ {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
+ });
+
+ const std::string &connectionName = connectionNames[0].first;
+ crow::connections::systemBus->async_method_call(
+ [asyncResp,
+ driveId](const boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::variant<bool, std::string, uint64_t>>>
+ &propertiesList) {
+ if (ec)
+ {
+ // this interface isn't necessary
+ return;
+ }
+ for (const std::pair<std::string,
+ std::variant<bool, std::string,
+ uint64_t>> &property :
+ propertiesList)
+ {
+ // Store DBus properties that are also
+ // Redfish properties with same name and a
+ // string value
+ const std::string &propertyName = property.first;
+ if ((propertyName == "PartNumber") ||
+ (propertyName == "SerialNumber") ||
+ (propertyName == "Manufacturer") ||
+ (propertyName == "Model"))
+ {
+ const std::string *value =
+ std::get_if<std::string>(&property.second);
+ if (value == nullptr)
+ {
+ // illegal property
+ messages::internalError(asyncResp->res);
+ continue;
+ }
+ asyncResp->res.jsonValue[propertyName] = *value;
+ }
+ }
+ asyncResp->res.jsonValue["Name"] = driveId;
+ asyncResp->res.jsonValue["Id"] = driveId;
+ },
+ connectionName, path, "org.freedesktop.DBus.Properties",
+ "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
+
+ // default it to Enabled
+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, path](const boost::system::error_code ec,
+ const std::variant<bool> present) {
+ // this interface isn't necessary, only check it if we
+ // get a good return
+ if (!ec)
+ {
+ const bool *enabled = std::get_if<bool>(&present);
+ if (enabled == nullptr)
+ {
+ BMCWEB_LOG_DEBUG << "Illegal property present";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ if (!(*enabled))
+ {
+ asyncResp->res.jsonValue["Status"]["State"] =
+ "Disabled";
+ return;
+ }
+ }
+
+ // only populate if Enabled, assume enabled unless item
+ // interface says otherwise
+ auto health =
+ std::make_shared<HealthPopulate>(asyncResp);
+ health->inventory = std::vector<std::string>{path};
+
+ health->populate();
+ },
+ connectionName, path, "org.freedesktop.DBus.Properties",
+ "Get", "xyz.openbmc_project.Inventory.Item", "Present");
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+ "/xyz/openbmc_project/inventory", int32_t(0),
+ std::array<const char *, 1>{
+ "xyz.openbmc_project.Inventory.Item.Drive"});
+ }
+};
+} // namespace redfish
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 2edce0a..0724c85 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1382,6 +1382,8 @@
{"@odata.id", "/redfish/v1/Systems/system/Processors"}};
res.jsonValue["Memory"] = {
{"@odata.id", "/redfish/v1/Systems/system/Memory"}};
+ res.jsonValue["Storage"] = {
+ {"@odata.id", "/redfish/v1/Systems/system/Storage"}};
// TODO Need to support ForceRestart.
res.jsonValue["Actions"]["#ComputerSystem.Reset"] = {