blob: 02a614b645589b5baf566d15f60177ab984a0df9 [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 */
27using ManagedObjectsType = std::vector<
28 std::pair<dbus::object_path,
29 std::vector<std::pair<
30 std::string,
31 std::vector<std::pair<std::string, dbus::dbus_variant>>>>>>;
32
33using PropertiesType =
34 boost::container::flat_map<std::string, dbus::dbus_variant>;
35
36/**
37 * OnDemandChassisProvider
38 * Chassis provider class that retrieves data directly from dbus, before seting
39 * it into JSON output. This does not cache any data.
40 *
41 * Class can be a good example on how to scale different data providing
42 * solutions to produce single schema output.
43 *
44 * TODO(Pawel)
45 * This perhaps shall be different file, which has to be chosen on compile time
46 * depending on OEM needs
47 */
48class OnDemandChassisProvider {
49 public:
50 /**
51 * Function that retrieves all properties for given Chassis Object from
52 * EntityManager
53 * @param res_name a chassis resource name to query on DBus
54 * @param callback a function that shall be called to convert Dbus output into
55 * JSON
56 */
57 template <typename CallbackFunc>
58 void get_chassis_data(const std::string &res_name, CallbackFunc &&callback) {
59 crow::connections::system_bus->async_method_call(
60 [callback{std::move(callback)}](
61 const boost::system::error_code error_code,
62 const PropertiesType &properties) {
63 // Callback requires flat_map<string, string> so prepare one.
64 boost::container::flat_map<std::string, std::string> output;
65 if (error_code) {
66 // Something wrong on DBus, the error_code is not important at this
67 // moment, just return success=false, and empty output. Since size
68 // of map may vary depending on Chassis type, an empty output could
69 // not be treated same way as error.
70 callback(false, output);
71 return;
72 }
73 for (const std::pair<const char *, const char *> &p :
74 std::array<std::pair<const char *, const char *>, 5>{
75 {{"name", "Name"},
76 {"manufacturer", "Manufacturer"},
77 {"model", "Model"},
78 {"part_number", "PartNumber"},
79 {"serial_number", "SerialNumber"}}}) {
80 PropertiesType::const_iterator it = properties.find(p.first);
81 if (it != properties.end()) {
82 const std::string *s = boost::get<std::string>(&it->second);
83 if (s != nullptr) {
84 output[p.second] = *s;
85 }
86 }
87 }
88 // Callback with success, and hopefully data.
89 callback(true, output);
90 },
91 {"xyz.openbmc_project.EntityManager",
92 "/xyz/openbmc_project/Inventory/Item/Chassis/" + res_name,
93 "org.freedesktop.DBus.Properties", "GetAll"},
94 "xyz.openbmc_project.Configuration.Chassis");
95 }
96
97 /**
98 * Function that retrieves all Chassis available through EntityManager.
99 * @param callback a function that shall be called to convert Dbus output into
100 * JSON.
101 */
102 template <typename CallbackFunc>
103 void get_chassis_list(CallbackFunc &&callback) {
104 crow::connections::system_bus->async_method_call(
105 [callback{std::move(callback)}](
106 const boost::system::error_code error_code,
107 const ManagedObjectsType &resp) {
108 // Callback requires vector<string> to retrieve all available chassis
109 // list.
110 std::vector<std::string> chassis_list;
111 if (error_code) {
112 // Something wrong on DBus, the error_code is not important at this
113 // moment, just return success=false, and empty output. Since size
114 // of vector may vary depending on information from Entity Manager,
115 // and empty output could not be treated same way as error.
116 callback(false, chassis_list);
117 return;
118 }
119
120 // Iterate over all retrieved ObjectPaths.
121 for (auto &objpath : resp) {
122 // And all interfaces available for certain ObjectPath.
123 for (auto &interface : objpath.second) {
124 // If interface is xyz.openbmc_project.Configuration.Chassis, this
125 // is Chassis.
126 if (interface.first ==
127 "xyz.openbmc_project.Configuration.Chassis") {
128 // Cut out everyting until last "/", ...
129 const std::string &chassis_id = objpath.first.value;
130 std::size_t last_pos = chassis_id.rfind("/");
131 if (last_pos != std::string::npos) {
132 // and put it into output vector.
133 chassis_list.emplace_back(chassis_id.substr(last_pos + 1));
134 }
135 }
136 }
137 }
138 // Finally make a callback with usefull data
139 callback(true, chassis_list);
140 },
141 {"xyz.openbmc_project.EntityManager",
142 "/xyz/openbmc_project/Inventory/Item/Chassis",
143 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
144 };
145};
146
147/**
148 * ChassisCollection derived class for delivering Chassis Collection Schema
149 */
150class ChassisCollection : public Node {
151 public:
152 template <typename CrowApp>
153 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") {
154 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
155 Node::json["@odata.id"] = "/redfish/v1/Chassis";
156 Node::json["@odata.context"] =
157 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
158 Node::json["Name"] = "Chassis Collection";
159
160 entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
161 {crow::HTTPMethod::HEAD, {{"Login"}}},
162 {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
163 {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
164 {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
165 {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
166 }
167
168 private:
169 /**
170 * Functions triggers appropriate requests on DBus
171 */
172 void doGet(crow::response &res, const crow::request &req,
173 const std::vector<std::string> &params) override {
174 // Get chassis list, and call the below callback for JSON preparation
175 chassis_provider.get_chassis_list(
176 [&](const bool &success, const std::vector<std::string> &output) {
177 if (success) {
178 // ... prepare json array with appropriate @odata.id links
179 nlohmann::json chassis_array = nlohmann::json::array();
180 for (const std::string &chassis_item : output) {
181 chassis_array.push_back(
182 {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}});
183 }
184 // Then attach members, count size and return,
185 Node::json["Members"] = chassis_array;
186 Node::json["Members@odata.count"] = chassis_array.size();
187 res.json_value = Node::json;
188 } else {
189 // ... otherwise, return INTERNALL ERROR
190 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
191 }
192 res.end();
193 });
194 }
195
196 // Chassis Provider object
197 // TODO(Pawel) consider move it to singleton
198 OnDemandChassisProvider chassis_provider;
199};
200
201/**
202 * Chassis override class for delivering Chassis Schema
203 */
204class Chassis : public Node {
205 public:
206 /*
207 * Default Constructor
208 */
209 template <typename CrowApp>
210 Chassis(CrowApp &app)
211 : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) {
212 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
213 Node::json["@odata.id"] = "/redfish/v1/Chassis";
214 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
215 Node::json["Name"] = "Chassis Collection";
Ed Tanous6c233012018-03-15 14:43:56 -0700216 Node::json["ChassisType"] = "RackMount";
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100217
218 entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
219 {crow::HTTPMethod::HEAD, {{"Login"}}},
220 {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
221 {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
222 {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
223 {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
224 }
225
226 private:
227 /**
228 * Functions triggers appropriate requests on DBus
229 */
230 void doGet(crow::response &res, const crow::request &req,
231 const std::vector<std::string> &params) override {
232 // Check if there is required param, truly entering this shall be
233 // impossible.
234 if (params.size() != 1) {
235 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
236 res.end();
237 return;
238 }
239 const std::string &chassis_id = params[0];
240 // Get certain Chassis Data, and call the below callback for JSON
241 // preparation lambda requires everything as reference, and chassis_id,
242 // which is local by copy
243 chassis_provider.get_chassis_data(
244 chassis_id,
245 [&, chassis_id](const bool &success,
246 const boost::container::flat_map<std::string,
247 std::string> &output) {
248 // Create JSON copy based on Node::json, this is to avoid possible
249 // race condition
250 nlohmann::json json_response(Node::json);
251 // If success...
252 if (success) {
253 // prepare all the schema required fields.
254 json_response["@odata.id"] = "/redfish/v1/Chassis/" + chassis_id;
255 // also the one from dbus
256 for (const std::pair<std::string, std::string> &chassis_item :
257 output) {
258 json_response[chassis_item.first] = chassis_item.second;
259 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100260
Ed Tanous6c233012018-03-15 14:43:56 -0700261 json_response["Id"] = chassis_id;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100262 // prepare respond, and send
263 res.json_value = json_response;
264 } else {
265 // ... otherwise return error
266 // TODO(Pawel)consider distinguish between non existing object, and
267 // other errors
268 res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
269 }
270 res.end();
271 });
272 }
273
274 // Chassis Provider object
275 // TODO(Pawel) consider move it to singleton
276 OnDemandChassisProvider chassis_provider;
277};
278
279} // namespace redfish