summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRapkiewicz, Pawel <pawel.rapkiewicz@intel.com>2018-03-09 15:49:50 +0300
committerEd Tanous <ed.tanous@intel.com>2018-04-22 21:13:32 +0300
commite37f8451795a5a17e14cf3bd13c72aa251c648f3 (patch)
tree136cf49989f86d7de82f915f3fb5a2d64a29396e
parent580f37216643f85dadad69c918613dbd1c326f1a (diff)
downloadbmcweb-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.hpp28
-rw-r--r--redfish-core/include/redfish.hpp3
-rw-r--r--redfish-core/lib/chassis.hpp278
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> &params) 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> &params) 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