diff options
author | Rapkiewicz, Pawel <pawel.rapkiewicz@intel.com> | 2018-03-09 15:49:50 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2018-04-22 21:13:32 +0300 |
commit | e37f8451795a5a17e14cf3bd13c72aa251c648f3 (patch) | |
tree | 136cf49989f86d7de82f915f3fb5a2d64a29396e | |
parent | 580f37216643f85dadad69c918613dbd1c326f1a (diff) | |
download | bmcweb-e37f8451795a5a17e14cf3bd13c72aa251c648f3.tar.xz |
Adding Chassis and ChassisCollection Schemas to Redfish
This commit:
* removes previous redfish_v1 Chassis implementation
* Adds Chassis and ChassisCollection implementation as Node way
* Adds Chassis Provider class for retrieving data from EntityManager
It was tested:
* Wolfpass run, to see if previous functionality was not broken
* Service Validator, which did not unveil any regression, and did
verified that implemented schemas are complient.
Change-Id: I75a9545a0abd8b85d6ce72329c523fc076affc28
Signed-off-by: Rapkiewicz, Pawel <pawel.rapkiewicz@intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Signed-off-by: Rapkiewicz, Pawel <pawel.rapkiewicz@intel.com>
-rw-r--r-- | include/redfish_v1.hpp | 28 | ||||
-rw-r--r-- | redfish-core/include/redfish.hpp | 3 | ||||
-rw-r--r-- | redfish-core/lib/chassis.hpp | 278 |
3 files changed, 281 insertions, 28 deletions
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp index 57ac7ea328..52b9410dcb 100644 --- a/include/redfish_v1.hpp +++ b/include/redfish_v1.hpp @@ -51,34 +51,6 @@ void request_routes(Crow<Middlewares...>& app) { res.end(); }); - CROW_ROUTE(app, "/redfish/v1/Chassis/") - .methods("GET"_method)( - [&](const crow::request& req, crow::response& res) { - std::vector<std::string> entities; - /*std::ifstream f("~/system.json"); - - nlohmann::json input = nlohmann::json::parse(f); - for (auto it = input.begin(); it != input.end(); it++) { - auto value = it.value(); - if (value["type"] == "Chassis") { - std::string str = value["name"]; - entities.emplace_back(str); - } - } - */ - - res.json_value = { - {"@odata.context", - "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"}, - {"@odata.id", "/redfish/v1/Chassis"}, - {"@odata.type", "#ChassisCollection.ChassisCollection"}, - {"Name", "Chassis Collection"}, - {"Members@odata.count", entities.size()}}; - - get_redfish_sub_routes(app, "/redfish/v1/Chassis", res.json_value); - res.end(); - }); - CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/") .methods( "GET"_method)([&](const crow::request& req, crow::response& res) { diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 0c2a91224b..897e27e1a1 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -21,6 +21,7 @@ #include "../lib/redfish_sessions.hpp" #include "../lib/roles.hpp" #include "../lib/service_root.hpp" +#include "../lib/chassis.hpp" #include "webserver_common.hpp" namespace redfish { @@ -45,6 +46,8 @@ class RedfishService { nodes.emplace_back(std::make_unique<NetworkProtocol>(app)); nodes.emplace_back(std::make_unique<SessionService>(app)); nodes.emplace_back(std::make_unique<ManagerCollection>(app)); + nodes.emplace_back(std::make_unique<ChassisCollection>(app)); + nodes.emplace_back(std::make_unique<Chassis>(app)); for (auto& node : nodes) { node->getSubRoutes(nodes); diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp new file mode 100644 index 0000000000..8f88eb1f5d --- /dev/null +++ b/redfish-core/lib/chassis.hpp @@ -0,0 +1,278 @@ +/* +// Copyright (c) 2018 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" +#include <boost/container/flat_map.hpp> + +namespace redfish { + +/** + * DBus types primitives for several generic DBus interfaces + * TODO(Pawel) consider move this to separate file into boost::dbus + */ +using ManagedObjectsType = std::vector< + std::pair<dbus::object_path, + std::vector<std::pair< + std::string, + std::vector<std::pair<std::string, dbus::dbus_variant>>>>>>; + +using PropertiesType = + boost::container::flat_map<std::string, dbus::dbus_variant>; + +/** + * OnDemandChassisProvider + * Chassis provider class that retrieves data directly from dbus, before seting + * it into JSON output. This does not cache any data. + * + * Class can be a good example on how to scale different data providing + * solutions to produce single schema output. + * + * TODO(Pawel) + * This perhaps shall be different file, which has to be chosen on compile time + * depending on OEM needs + */ +class OnDemandChassisProvider { + public: + /** + * Function that retrieves all properties for given Chassis Object from + * EntityManager + * @param res_name a chassis resource name to query on DBus + * @param callback a function that shall be called to convert Dbus output into + * JSON + */ + template <typename CallbackFunc> + void get_chassis_data(const std::string &res_name, CallbackFunc &&callback) { + crow::connections::system_bus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code error_code, + const PropertiesType &properties) { + // Callback requires flat_map<string, string> so prepare one. + boost::container::flat_map<std::string, std::string> output; + if (error_code) { + // Something wrong on DBus, the error_code is not important at this + // moment, just return success=false, and empty output. Since size + // of map may vary depending on Chassis type, an empty output could + // not be treated same way as error. + callback(false, output); + return; + } + for (const std::pair<const char *, const char *> &p : + std::array<std::pair<const char *, const char *>, 5>{ + {{"name", "Name"}, + {"manufacturer", "Manufacturer"}, + {"model", "Model"}, + {"part_number", "PartNumber"}, + {"serial_number", "SerialNumber"}}}) { + PropertiesType::const_iterator it = properties.find(p.first); + if (it != properties.end()) { + const std::string *s = boost::get<std::string>(&it->second); + if (s != nullptr) { + output[p.second] = *s; + } + } + } + // Callback with success, and hopefully data. + callback(true, output); + }, + {"xyz.openbmc_project.EntityManager", + "/xyz/openbmc_project/Inventory/Item/Chassis/" + res_name, + "org.freedesktop.DBus.Properties", "GetAll"}, + "xyz.openbmc_project.Configuration.Chassis"); + } + + /** + * Function that retrieves all Chassis available through EntityManager. + * @param callback a function that shall be called to convert Dbus output into + * JSON. + */ + template <typename CallbackFunc> + void get_chassis_list(CallbackFunc &&callback) { + crow::connections::system_bus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code error_code, + const ManagedObjectsType &resp) { + // Callback requires vector<string> to retrieve all available chassis + // list. + std::vector<std::string> chassis_list; + if (error_code) { + // Something wrong on DBus, the error_code is not important at this + // moment, just return success=false, and empty output. Since size + // of vector may vary depending on information from Entity Manager, + // and empty output could not be treated same way as error. + callback(false, chassis_list); + return; + } + + // Iterate over all retrieved ObjectPaths. + for (auto &objpath : resp) { + // And all interfaces available for certain ObjectPath. + for (auto &interface : objpath.second) { + // If interface is xyz.openbmc_project.Configuration.Chassis, this + // is Chassis. + if (interface.first == + "xyz.openbmc_project.Configuration.Chassis") { + // Cut out everyting until last "/", ... + const std::string &chassis_id = objpath.first.value; + std::size_t last_pos = chassis_id.rfind("/"); + if (last_pos != std::string::npos) { + // and put it into output vector. + chassis_list.emplace_back(chassis_id.substr(last_pos + 1)); + } + } + } + } + // Finally make a callback with usefull data + callback(true, chassis_list); + }, + {"xyz.openbmc_project.EntityManager", + "/xyz/openbmc_project/Inventory/Item/Chassis", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"}); + }; +}; + +/** + * ChassisCollection derived class for delivering Chassis Collection Schema + */ +class ChassisCollection : public Node { + public: + template <typename CrowApp> + ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { + Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; + Node::json["Name"] = "Chassis Collection"; + + entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, + {crow::HTTPMethod::HEAD, {{"Login"}}}, + {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; + } + + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::response &res, const crow::request &req, + const std::vector<std::string> ¶ms) override { + // Get chassis list, and call the below callback for JSON preparation + chassis_provider.get_chassis_list( + [&](const bool &success, const std::vector<std::string> &output) { + if (success) { + // ... prepare json array with appropriate @odata.id links + nlohmann::json chassis_array = nlohmann::json::array(); + for (const std::string &chassis_item : output) { + chassis_array.push_back( + {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}}); + } + // Then attach members, count size and return, + Node::json["Members"] = chassis_array; + Node::json["Members@odata.count"] = chassis_array.size(); + res.json_value = Node::json; + } else { + // ... otherwise, return INTERNALL ERROR + res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); + } + res.end(); + }); + } + + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassis_provider; +}; + +/** + * Chassis override class for delivering Chassis Schema + */ +class Chassis : public Node { + public: + /* + * Default Constructor + */ + template <typename CrowApp> + Chassis(CrowApp &app) + : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { + Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; + Node::json["Name"] = "Chassis Collection"; + + entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, + {crow::HTTPMethod::HEAD, {{"Login"}}}, + {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, + {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; + } + + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::response &res, const crow::request &req, + const std::vector<std::string> ¶ms) override { + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) { + res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); + res.end(); + return; + } + const std::string &chassis_id = params[0]; + // Get certain Chassis Data, and call the below callback for JSON + // preparation lambda requires everything as reference, and chassis_id, + // which is local by copy + chassis_provider.get_chassis_data( + chassis_id, + [&, chassis_id](const bool &success, + const boost::container::flat_map<std::string, + std::string> &output) { + // Create JSON copy based on Node::json, this is to avoid possible + // race condition + nlohmann::json json_response(Node::json); + // If success... + if (success) { + // prepare all the schema required fields. + json_response["@odata.id"] = "/redfish/v1/Chassis/" + chassis_id; + // also the one from dbus + for (const std::pair<std::string, std::string> &chassis_item : + output) { + json_response[chassis_item.first] = chassis_item.second; + } + json_response["Id"] = chassis_id; + + // prepare respond, and send + res.json_value = json_response; + } else { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing object, and + // other errors + res.code = static_cast<int>(HttpRespCode::NOT_FOUND); + } + res.end(); + }); + } + + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassis_provider; +}; + +} // namespace redfish |