blob: 5cf667a00b2c4e10f302039b9939e6b204210cca [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 Tanousaa2e59c2018-04-12 12:17:20 -070027// Note, this is not a very useful variant, but because it isn't used to get
28// 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>
58 void get_chassis_list(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 };
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010065 crow::connections::system_bus->async_method_call(
66 [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.
71 std::vector<std::string> chassis_list;
72 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.
77 callback(false, chassis_list);
78 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) {
82 std::size_t last_pos = objpath.rfind("/");
83 if (last_pos != std::string::npos) {
84 // and put it into output vector.
85 chassis_list.emplace_back(objpath.substr(last_pos + 1));
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010086 }
87 }
Gunnar Mills274fad52018-06-13 15:45:36 -050088 // Finally make a callback with useful data
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +010089 callback(true, chassis_list);
90 },
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:
103 template <typename CrowApp>
104 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") {
105 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection";
106 Node::json["@odata.id"] = "/redfish/v1/Chassis";
107 Node::json["@odata.context"] =
108 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection";
109 Node::json["Name"] = "Chassis Collection";
110
Ed Tanouse0d918b2018-03-27 17:41:04 -0700111 entityPrivileges = {
112 {boost::beast::http::verb::get, {{"Login"}}},
113 {boost::beast::http::verb::head, {{"Login"}}},
114 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
115 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
116 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
117 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100118 }
119
120 private:
121 /**
122 * Functions triggers appropriate requests on DBus
123 */
124 void doGet(crow::response &res, const crow::request &req,
125 const std::vector<std::string> &params) override {
126 // Get chassis list, and call the below callback for JSON preparation
127 chassis_provider.get_chassis_list(
128 [&](const bool &success, const std::vector<std::string> &output) {
129 if (success) {
130 // ... prepare json array with appropriate @odata.id links
131 nlohmann::json chassis_array = nlohmann::json::array();
132 for (const std::string &chassis_item : output) {
133 chassis_array.push_back(
134 {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}});
135 }
136 // Then attach members, count size and return,
137 Node::json["Members"] = chassis_array;
138 Node::json["Members@odata.count"] = chassis_array.size();
139 res.json_value = Node::json;
140 } else {
141 // ... otherwise, return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700142 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100143 }
144 res.end();
145 });
146 }
147
148 // Chassis Provider object
149 // TODO(Pawel) consider move it to singleton
150 OnDemandChassisProvider chassis_provider;
151};
152
153/**
154 * Chassis override class for delivering Chassis Schema
155 */
156class Chassis : public Node {
157 public:
158 /*
159 * Default Constructor
160 */
161 template <typename CrowApp>
162 Chassis(CrowApp &app)
163 : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) {
164 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis";
165 Node::json["@odata.id"] = "/redfish/v1/Chassis";
166 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis";
167 Node::json["Name"] = "Chassis Collection";
Ed Tanous6c233012018-03-15 14:43:56 -0700168 Node::json["ChassisType"] = "RackMount";
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100169
Ed Tanouse0d918b2018-03-27 17:41:04 -0700170 entityPrivileges = {
171 {boost::beast::http::verb::get, {{"Login"}}},
172 {boost::beast::http::verb::head, {{"Login"}}},
173 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
174 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
175 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
176 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100177 }
178
179 private:
180 /**
181 * Functions triggers appropriate requests on DBus
182 */
183 void doGet(crow::response &res, const crow::request &req,
184 const std::vector<std::string> &params) override {
185 // Check if there is required param, truly entering this shall be
186 // impossible.
187 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700188 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100189 res.end();
190 return;
191 }
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100192
Ed Tanousdaf36e22018-04-20 16:01:36 -0700193 res.json_value = Node::json;
194 const std::string &chassis_id = params[0];
195 crow::connections::system_bus->async_method_call(
196 [&res, chassis_id(std::string(chassis_id)) ](
197 const boost::system::error_code error_code,
198 const std::vector<std::pair<
199 std::string,
200 std::vector<std::pair<std::string, std::vector<std::string>>>>>
201 &subtree) {
202 if (error_code) {
Ed Tanousdaf36e22018-04-20 16:01:36 -0700203 res.json_value = {};
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;
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100207 }
Ed Tanousdaf36e22018-04-20 16:01:36 -0700208 // Iterate over all retrieved ObjectPaths.
209 for (const std::pair<std::string,
210 std::vector<std::pair<std::string,
211 std::vector<std::string>>>>
212 &object : subtree) {
213 const std::string &path = object.first;
214 const std::vector<std::pair<std::string, std::vector<std::string>>>
215 &connectionNames = object.second;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700216
Ed Tanousdaf36e22018-04-20 16:01:36 -0700217 if (!boost::ends_with(path, chassis_id)) {
218 continue;
219 }
220 if (connectionNames.size() < 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700221 CROW_LOG_ERROR << "Only got " << connectionNames.size()
222 << " connection names";
223 continue;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700224 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700225
Ed Tanousdaf36e22018-04-20 16:01:36 -0700226 const std::string connectionName = connectionNames[0].first;
227 crow::connections::system_bus->async_method_call(
228 [&res, chassis_id(std::string(chassis_id)) ](
229 const boost::system::error_code error_code,
230 const std::vector<std::pair<std::string, VariantType>>
231 &propertiesList) {
232 for (const std::pair<std::string, VariantType> &property :
233 propertiesList) {
234 const std::string *value =
235 mapbox::get_ptr<const std::string>(property.second);
236 if (value != nullptr) {
237 res.json_value[property.first] = *value;
238 }
239 }
240 res.json_value["Name"] = chassis_id;
Lewanczyk, Dawid78859542018-06-11 16:58:40 +0200241 res.json_value["Id"] = chassis_id;
Ed Tanousdaf36e22018-04-20 16:01:36 -0700242 res.json_value["Thermal"] = {
243 {"@odata.id",
244 "/redfish/v1/Chassis/" + chassis_id + "/Thermal"}};
245 res.end();
246 },
247 connectionName, path, "org.freedesktop.DBus.Properties",
248 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
249 // Found the connection we were looking for, return
250 return;
251 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700252
Ed Tanousdaf36e22018-04-20 16:01:36 -0700253 // Couldn't find an object with that name. return an error
Ed Tanouse0d918b2018-03-27 17:41:04 -0700254 res.result(boost::beast::http::status::not_found);
255
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100256 res.end();
Ed Tanousdaf36e22018-04-20 16:01:36 -0700257 },
258 "xyz.openbmc_project.ObjectMapper",
259 "/xyz/openbmc_project/object_mapper",
260 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
261 "/xyz/openbmc_project/inventory", int32_t(0),
262 std::array<const char *, 1>{
263 "xyz.openbmc_project.Inventory.Decorator.Asset"});
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100264 }
265
266 // Chassis Provider object
267 // TODO(Pawel) consider move it to singleton
268 OnDemandChassisProvider chassis_provider;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700269}; // namespace redfish
Rapkiewicz, Pawele37f8452018-03-09 13:49:50 +0100270
271} // namespace redfish