blob: b1c20a538a40eb8ab628d83525fd59ffbbb1c64c [file] [log] [blame]
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19#include <boost/container/flat_map.hpp>
20
21namespace redfish {
22
23/**
24 * DBus types primitives for several generic DBus interfaces
25 * TODO(Pawel) consider move this to separate file into boost::dbus
26 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070027// Note, this is not a very useful Variant, but because it isn't used to get
Ed Tanousaa2e59c2018-04-12 12:17:20 -070028// values, it should be as simple as possible
29// TODO(ed) invent a nullvariant type
Lewanczyk, Dawidc5b2abe2018-05-30 16:59:42 +020030using VariantType = sdbusplus::message::variant<bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070031using ManagedObjectsType = std::vector<std::pair<
32 sdbusplus::message::object_path,
33 std::vector<std::pair<std::string,
34 std::vector<std::pair<std::string, VariantType>>>>>>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010035
Ed Tanousaa2e59c2018-04-12 12:17:20 -070036using PropertiesType = boost::container::flat_map<std::string, VariantType>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010037
38/**
39 * OnDemandChassisProvider
Gunnar Mills274fad52018-06-13 15:45:36 -050040 * Chassis provider class that retrieves data directly from dbus, before setting
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010041 * it into JSON output. This does not cache any data.
42 *
43 * Class can be a good example on how to scale different data providing
44 * solutions to produce single schema output.
45 *
46 * TODO(Pawel)
47 * This perhaps shall be different file, which has to be chosen on compile time
48 * depending on OEM needs
49 */
50class OnDemandChassisProvider {
51 public:
52 /**
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010053 * Function that retrieves all Chassis available through EntityManager.
54 * @param callback a function that shall be called to convert Dbus output into
55 * JSON.
56 */
57 template <typename CallbackFunc>
Ed Tanous55c7b7a2018-05-22 15:27:24 -070058 void getChassisList(CallbackFunc &&callback) {
Ed Tanousdaf36e22018-04-20 16:01:36 -070059 const std::array<const char *, 4> interfaces = {
60 "xyz.openbmc_project.Inventory.Item.Board",
61 "xyz.openbmc_project.Inventory.Item.Chassis",
62 "xyz.openbmc_project.Inventory.Item.PowerSupply",
63 "xyz.openbmc_project.Inventory.Item.System",
64 };
Ed Tanous55c7b7a2018-05-22 15:27:24 -070065 crow::connections::systemBus->async_method_call(
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010066 [callback{std::move(callback)}](
67 const boost::system::error_code error_code,
Ed Tanousdaf36e22018-04-20 16:01:36 -070068 const std::vector<std::string> &resp) {
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010069 // Callback requires vector<string> to retrieve all available chassis
70 // list.
Ed Tanous55c7b7a2018-05-22 15:27:24 -070071 std::vector<std::string> chassisList;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010072 if (error_code) {
73 // Something wrong on DBus, the error_code is not important at this
74 // moment, just return success=false, and empty output. Since size
75 // of vector may vary depending on information from Entity Manager,
76 // and empty output could not be treated same way as error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -070077 callback(false, chassisList);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010078 return;
79 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010080 // Iterate over all retrieved ObjectPaths.
Ed Tanousdaf36e22018-04-20 16:01:36 -070081 for (const std::string &objpath : resp) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070082 std::size_t lastPos = objpath.rfind("/");
83 if (lastPos != std::string::npos) {
Ed Tanousdaf36e22018-04-20 16:01:36 -070084 // and put it into output vector.
Ed Tanous55c7b7a2018-05-22 15:27:24 -070085 chassisList.emplace_back(objpath.substr(lastPos + 1));
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010086 }
87 }
Ed Tanousa434f2b2018-07-27 13:04:22 -070088 // Finally make a callback with useful data
Ed Tanous55c7b7a2018-05-22 15:27:24 -070089 callback(true, chassisList);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010090 },
Ed Tanousdaf36e22018-04-20 16:01:36 -070091 "xyz.openbmc_project.ObjectMapper",
92 "/xyz/openbmc_project/object_mapper",
93 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
94 "/xyz/openbmc_project/inventory", int32_t(3), interfaces);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010095 };
96};
97
98/**
99 * ChassisCollection derived class for delivering Chassis Collection Schema
100 */
101class ChassisCollection : public Node {
102 public:
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100103 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") {
104 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
105 Node::json["@odata.id"] = "/redfish/v1/Chassis";
106 Node::json["@odata.context"] =
107 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
108 Node::json["Name"] = "Chassis Collection";
109
Ed Tanouse0d918b2018-03-27 17:41:04 -0700110 entityPrivileges = {
111 {boost::beast::http::verb::get, {{"Login"}}},
112 {boost::beast::http::verb::head, {{"Login"}}},
113 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
114 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
115 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
116 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100117 }
118
119 private:
120 /**
121 * Functions triggers appropriate requests on DBus
122 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700123 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100124 const std::vector<std::string> &params) override {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700125 // get chassis list, and call the below callback for JSON preparation
126 chassisProvider.getChassisList(
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100127 [&](const bool &success, const std::vector<std::string> &output) {
128 if (success) {
129 // ... prepare json array with appropriate @odata.id links
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700130 nlohmann::json chassisArray = nlohmann::json::array();
131 for (const std::string &chassisItem : output) {
132 chassisArray.push_back(
133 {{"@odata.id", "/redfish/v1/Chassis/" + chassisItem}});
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100134 }
135 // Then attach members, count size and return,
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700136 Node::json["Members"] = chassisArray;
137 Node::json["Members@odata.count"] = chassisArray.size();
138 res.jsonValue = Node::json;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100139 } else {
140 // ... otherwise, return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700141 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100142 }
143 res.end();
144 });
145 }
146
147 // Chassis Provider object
148 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700149 OnDemandChassisProvider chassisProvider;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100150};
151
152/**
153 * Chassis override class for delivering Chassis Schema
154 */
155class Chassis : public Node {
156 public:
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100157 Chassis(CrowApp &app)
158 : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) {
159 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
160 Node::json["@odata.id"] = "/redfish/v1/Chassis";
161 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
162 Node::json["Name"] = "Chassis Collection";
Ed Tanous6c233012018-03-15 14:43:56 -0700163 Node::json["ChassisType"] = "RackMount";
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100164
Ed Tanouse0d918b2018-03-27 17:41:04 -0700165 entityPrivileges = {
166 {boost::beast::http::verb::get, {{"Login"}}},
167 {boost::beast::http::verb::head, {{"Login"}}},
168 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
169 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
170 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
171 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100172 }
173
174 private:
175 /**
176 * Functions triggers appropriate requests on DBus
177 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700178 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100179 const std::vector<std::string> &params) override {
180 // Check if there is required param, truly entering this shall be
181 // impossible.
182 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700183 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100184 res.end();
185 return;
186 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100187
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700188 res.jsonValue = Node::json;
189 const std::string &chassisId = params[0];
190 crow::connections::systemBus->async_method_call(
191 [&res, chassisId(std::string(chassisId)) ](
Ed Tanousdaf36e22018-04-20 16:01:36 -0700192 const boost::system::error_code error_code,
193 const std::vector<std::pair<
194 std::string,
195 std::vector<std::pair<std::string, std::vector<std::string>>>>>
196 &subtree) {
197 if (error_code) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700198 res.jsonValue = {};
Ed Tanouse0d918b2018-03-27 17:41:04 -0700199 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700200 res.end();
201 return;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100202 }
Ed Tanousdaf36e22018-04-20 16:01:36 -0700203 // Iterate over all retrieved ObjectPaths.
204 for (const std::pair<std::string,
205 std::vector<std::pair<std::string,
206 std::vector<std::string>>>>
207 &object : subtree) {
208 const std::string &path = object.first;
209 const std::vector<std::pair<std::string, std::vector<std::string>>>
210 &connectionNames = object.second;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700211
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700212 if (!boost::ends_with(path, chassisId)) {
Ed Tanousdaf36e22018-04-20 16:01:36 -0700213 continue;
214 }
215 if (connectionNames.size() < 1) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700216 BMCWEB_LOG_ERROR << "Only got " << connectionNames.size()
217 << " Connection names";
Ed Tanouse0d918b2018-03-27 17:41:04 -0700218 continue;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700219 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700220
Ed Tanousdaf36e22018-04-20 16:01:36 -0700221 const std::string connectionName = connectionNames[0].first;
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700222 crow::connections::systemBus->async_method_call(
223 [&res, chassisId(std::string(chassisId)) ](
Ed Tanousdaf36e22018-04-20 16:01:36 -0700224 const boost::system::error_code error_code,
225 const std::vector<std::pair<std::string, VariantType>>
226 &propertiesList) {
227 for (const std::pair<std::string, VariantType> &property :
228 propertiesList) {
229 const std::string *value =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700230 mapbox::getPtr<const std::string>(property.second);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700231 if (value != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700232 res.jsonValue[property.first] = *value;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700233 }
234 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700235 res.jsonValue["Name"] = chassisId;
236 res.jsonValue["Id"] = chassisId;
237 res.jsonValue["Thermal"] = {
Ed Tanousdaf36e22018-04-20 16:01:36 -0700238 {"@odata.id",
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700239 "/redfish/v1/Chassis/" + chassisId + "/Thermal"}};
Ed Tanousdaf36e22018-04-20 16:01:36 -0700240 res.end();
241 },
242 connectionName, path, "org.freedesktop.DBus.Properties",
243 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700244 // Found the Connection we were looking for, return
Ed Tanousdaf36e22018-04-20 16:01:36 -0700245 return;
246 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700247
Ed Tanousdaf36e22018-04-20 16:01:36 -0700248 // Couldn't find an object with that name. return an error
Ed Tanouse0d918b2018-03-27 17:41:04 -0700249 res.result(boost::beast::http::status::not_found);
250
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100251 res.end();
Ed Tanousdaf36e22018-04-20 16:01:36 -0700252 },
253 "xyz.openbmc_project.ObjectMapper",
254 "/xyz/openbmc_project/object_mapper",
255 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
256 "/xyz/openbmc_project/inventory", int32_t(0),
257 std::array<const char *, 1>{
258 "xyz.openbmc_project.Inventory.Decorator.Asset"});
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100259 }
260
261 // Chassis Provider object
262 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700263 OnDemandChassisProvider chassisProvider;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700264}; // namespace redfish
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100265
266} // namespace redfish