From a25aeccf45933dbccfd68c633d0ae13ddd3e1962 Mon Sep 17 00:00:00 2001 From: Nikhil Potade Date: Fri, 23 Aug 2019 16:35:26 -0700 Subject: 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 Signed-off-by: James Feist --- redfish-core/include/redfish.hpp | 4 + redfish-core/lib/storage.hpp | 286 +++++++++++++++++++++++++++++++++++++++ redfish-core/lib/systems.hpp | 2 + 3 files changed, 292 insertions(+) create mode 100644 redfish-core/lib/storage.hpp 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 @@ class RedfishService nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); + nodes.emplace_back(std::make_unique(app)); + nodes.emplace_back(std::make_unique(app)); + nodes.emplace_back(std::make_unique(app)); #ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE nodes.emplace_back( std::make_unique(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 + +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 ¶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 ¶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(res); + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, + const std::vector &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{ + "xyz.openbmc_project.Inventory.Item.Drive"}); + } +}; + +class Drive : public Node +{ + public: + Drive(CrowApp &app) : + Node(app, "/redfish/v1/Systems/system/Storage/1/Drive//", + 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 ¶ms) override + { + const std::string &driveId = params[0]; + + auto asyncResp = std::make_shared(res); + + crow::connections::systemBus->async_method_call( + [asyncResp, driveId]( + const boost::system::error_code ec, + const std::vector>>>> + &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>> + &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 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>> + &propertiesList) { + if (ec) + { + // this interface isn't necessary + return; + } + for (const std::pair> &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(&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 present) { + // this interface isn't necessary, only check it if we + // get a good return + if (!ec) + { + const bool *enabled = std::get_if(&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(asyncResp); + health->inventory = std::vector{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{ + "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 @@ class Systems : public Node {"@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"] = { -- cgit v1.2.3