blob: 7b68a270add0805bd403c1aae4faee6ba73d0416 [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"
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010020#include <boost/container/flat_map.hpp>
21
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace redfish
23{
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010024
25/**
26 * DBus types primitives for several generic DBus interfaces
27 * TODO(Pawel) consider move this to separate file into boost::dbus
28 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070029// Note, this is not a very useful Variant, but because it isn't used to get
Ed Tanousaa2e59c2018-04-12 12:17:20 -070030// values, it should be as simple as possible
31// TODO(ed) invent a nullvariant type
Lewanczyk, Dawidc5b2abe2018-05-30 16:59:42 +020032using VariantType = sdbusplus::message::variant<bool, std::string>;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070033using ManagedObjectsType = std::vector<std::pair<
34 sdbusplus::message::object_path,
35 std::vector<std::pair<std::string,
36 std::vector<std::pair<std::string, VariantType>>>>>>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010037
Ed Tanousaa2e59c2018-04-12 12:17:20 -070038using PropertiesType = boost::container::flat_map<std::string, VariantType>;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010039
40/**
41 * OnDemandChassisProvider
Gunnar Mills274fad52018-06-13 15:45:36 -050042 * Chassis provider class that retrieves data directly from dbus, before setting
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010043 * it into JSON output. This does not cache any data.
44 *
45 * Class can be a good example on how to scale different data providing
46 * solutions to produce single schema output.
47 *
48 * TODO(Pawel)
49 * This perhaps shall be different file, which has to be chosen on compile time
50 * depending on OEM needs
51 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070052class OnDemandChassisProvider
53{
54 public:
55 /**
56 * Function that retrieves all Chassis available through EntityManager.
57 * @param callback a function that shall be called to convert Dbus output
58 * into JSON.
59 */
60 template <typename CallbackFunc>
61 void getChassisList(CallbackFunc &&callback)
62 {
63 const std::array<const char *, 4> interfaces = {
64 "xyz.openbmc_project.Inventory.Item.Board",
65 "xyz.openbmc_project.Inventory.Item.Chassis",
66 "xyz.openbmc_project.Inventory.Item.PowerSupply",
67 "xyz.openbmc_project.Inventory.Item.System",
68 };
69 crow::connections::systemBus->async_method_call(
70 [callback{std::move(callback)}](
71 const boost::system::error_code error_code,
72 const std::vector<std::string> &resp) {
73 // Callback requires vector<string> to retrieve all available
74 // chassis list.
75 std::vector<std::string> chassisList;
76 if (error_code)
77 {
78 // Something wrong on DBus, the error_code is not important
79 // at this moment, just return success=false, and empty
80 // output. Since size of vector may vary depending on
81 // information from Entity Manager, and empty output could
82 // not be treated same way as error.
83 callback(false, chassisList);
84 return;
85 }
86 // Iterate over all retrieved ObjectPaths.
87 for (const std::string &objpath : resp)
88 {
89 std::size_t lastPos = objpath.rfind("/");
90 if (lastPos != std::string::npos)
91 {
92 // and put it into output vector.
93 chassisList.emplace_back(objpath.substr(lastPos + 1));
94 }
95 }
96 // Finally make a callback with useful data
97 callback(true, chassisList);
98 },
99 "xyz.openbmc_project.ObjectMapper",
100 "/xyz/openbmc_project/object_mapper",
101 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
102 "/xyz/openbmc_project/inventory", int32_t(3), interfaces);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700103 };
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100104};
105
106/**
107 * ChassisCollection derived class for delivering Chassis Collection Schema
108 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109class ChassisCollection : public Node
110{
111 public:
112 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/")
113 {
114 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
115 Node::json["@odata.id"] = "/redfish/v1/Chassis";
116 Node::json["@odata.context"] =
117 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
118 Node::json["Name"] = "Chassis Collection";
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100119
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 entityPrivileges = {
121 {boost::beast::http::verb::get, {{"Login"}}},
122 {boost::beast::http::verb::head, {{"Login"}}},
123 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
124 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
125 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
126 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
127 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100128
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 private:
130 /**
131 * Functions triggers appropriate requests on DBus
132 */
133 void doGet(crow::Response &res, const crow::Request &req,
134 const std::vector<std::string> &params) override
135 {
136 // get chassis list, and call the below callback for JSON preparation
137 chassisProvider.getChassisList(
138 [&](const bool &success, const std::vector<std::string> &output) {
139 if (success)
140 {
141 // ... prepare json array with appropriate @odata.id links
142 nlohmann::json chassisArray = nlohmann::json::array();
143 for (const std::string &chassisItem : output)
144 {
145 chassisArray.push_back(
146 {{"@odata.id",
147 "/redfish/v1/Chassis/" + chassisItem}});
148 }
149 // Then attach members, count size and return,
150 Node::json["Members"] = chassisArray;
151 Node::json["Members@odata.count"] = chassisArray.size();
152 res.jsonValue = Node::json;
153 }
154 else
155 {
156 // ... otherwise, return INTERNALL ERROR
157 res.result(
158 boost::beast::http::status::internal_server_error);
159 }
160 res.end();
161 });
162 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100163
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 // Chassis Provider object
165 // TODO(Pawel) consider move it to singleton
166 OnDemandChassisProvider chassisProvider;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100167};
168
169/**
170 * Chassis override class for delivering Chassis Schema
171 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172class Chassis : public Node
173{
174 public:
175 Chassis(CrowApp &app) :
176 Node(app, "/redfish/v1/Chassis/<str>/", std::string())
177 {
178 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
179 Node::json["@odata.id"] = "/redfish/v1/Chassis";
180 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
181 Node::json["Name"] = "Chassis Collection";
182 Node::json["ChassisType"] = "RackMount";
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100183
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 entityPrivileges = {
185 {boost::beast::http::verb::get, {{"Login"}}},
186 {boost::beast::http::verb::head, {{"Login"}}},
187 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
188 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
189 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
190 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100191 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100192
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 private:
194 /**
195 * Functions triggers appropriate requests on DBus
196 */
197 void doGet(crow::Response &res, const crow::Request &req,
198 const std::vector<std::string> &params) override
199 {
200 // Check if there is required param, truly entering this shall be
201 // impossible.
202 if (params.size() != 1)
203 {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700204 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousdaf36e22018-04-20 16:01:36 -0700205 res.end();
206 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700208
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209 res.jsonValue = Node::json;
210 const std::string &chassisId = params[0];
211 crow::connections::systemBus->async_method_call(
212 [&res, chassisId(std::string(chassisId))](
213 const boost::system::error_code error_code,
214 const std::vector<std::pair<
215 std::string, std::vector<std::pair<
216 std::string, std::vector<std::string>>>>>
217 &subtree) {
218 if (error_code)
219 {
220 res.jsonValue = {};
221 res.result(
222 boost::beast::http::status::internal_server_error);
223 res.end();
224 return;
225 }
226 // Iterate over all retrieved ObjectPaths.
227 for (const std::pair<
228 std::string,
229 std::vector<
230 std::pair<std::string, std::vector<std::string>>>>
231 &object : subtree)
232 {
233 const std::string &path = object.first;
234 const std::vector<
235 std::pair<std::string, std::vector<std::string>>>
236 &connectionNames = object.second;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700237
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 if (!boost::ends_with(path, chassisId))
239 {
240 continue;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700241 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 if (connectionNames.size() < 1)
243 {
244 BMCWEB_LOG_ERROR << "Only got "
245 << connectionNames.size()
246 << " Connection names";
247 continue;
248 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700249
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 const std::string connectionName = connectionNames[0].first;
251 crow::connections::systemBus->async_method_call(
252 [&res, chassisId(std::string(chassisId))](
253 const boost::system::error_code error_code,
254 const std::vector<std::pair<
255 std::string, VariantType>> &propertiesList) {
256 for (const std::pair<std::string, VariantType>
257 &property : propertiesList)
258 {
259 const std::string *value =
260 mapbox::getPtr<const std::string>(
261 property.second);
262 if (value != nullptr)
263 {
264 res.jsonValue[property.first] = *value;
265 }
266 }
267 res.jsonValue["Name"] = chassisId;
268 res.jsonValue["Id"] = chassisId;
269 res.jsonValue["Thermal"] = {
270 {"@odata.id", "/redfish/v1/Chassis/" +
271 chassisId + "/Thermal"}};
272 res.end();
273 },
274 connectionName, path, "org.freedesktop.DBus.Properties",
275 "GetAll",
276 "xyz.openbmc_project.Inventory.Decorator.Asset");
277 // Found the Connection we were looking for, return
278 return;
279 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700280
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 // Couldn't find an object with that name. return an error
282 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 res.end();
285 },
286 "xyz.openbmc_project.ObjectMapper",
287 "/xyz/openbmc_project/object_mapper",
288 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
289 "/xyz/openbmc_project/inventory", int32_t(0),
290 std::array<const char *, 1>{
291 "xyz.openbmc_project.Inventory.Decorator.Asset"});
292 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100293
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 // Chassis Provider object
295 // TODO(Pawel) consider move it to singleton
296 OnDemandChassisProvider chassisProvider;
297}; // namespace redfish
298
299} // namespace redfish