blob: 856ef5e4507066280bc7b9bfa8f42889000e5ce6 [file] [log] [blame]
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001/*
2// Copyright (c) 2019 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
James Feist2ad9c2f2019-10-29 16:26:48 -070018#include "health.hpp"
James Feiste284a7c2019-11-20 16:20:23 -080019#include "openbmc_dbus_rest.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070020
Nikhil Potadea25aecc2019-08-23 16:35:26 -070021#include <node.hpp>
22
23namespace redfish
24{
25class StorageCollection : public Node
26{
27 public:
28 StorageCollection(CrowApp &app) :
29 Node(app, "/redfish/v1/Systems/system/Storage/")
30 {
31 entityPrivileges = {
32 {boost::beast::http::verb::get, {{"Login"}}},
33 {boost::beast::http::verb::head, {{"Login"}}},
34 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
35 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
36 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
37 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
38 }
39
40 private:
41 void doGet(crow::Response &res, const crow::Request &req,
42 const std::vector<std::string> &params) override
43 {
44 res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection";
45 res.jsonValue["@odata.context"] =
46 "/redfish/v1/$metadata#StorageCollection.StorageCollection";
47 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage";
48 res.jsonValue["Name"] = "Storage Collection";
49 res.jsonValue["Members"] = {
50 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
51 res.jsonValue["Members@odata.count"] = 1;
52 res.end();
53 }
54};
55
56class Storage : public Node
57{
58 public:
59 Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1")
60 {
61 entityPrivileges = {
62 {boost::beast::http::verb::get, {{"Login"}}},
63 {boost::beast::http::verb::head, {{"Login"}}},
64 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
65 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
66 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
67 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
68 }
69
70 private:
71 void doGet(crow::Response &res, const crow::Request &req,
72 const std::vector<std::string> &params) override
73 {
74 res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
75 res.jsonValue["@odata.context"] =
76 "/redfish/v1/$metadata#Storage.Storage";
77 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
James Feiste284a7c2019-11-20 16:20:23 -080078 res.jsonValue["Name"] = "Storage";
Nikhil Potadea25aecc2019-08-23 16:35:26 -070079 res.jsonValue["Id"] = "1";
James Feist2ad9c2f2019-10-29 16:26:48 -070080 res.jsonValue["Status"]["State"] = "Enabled";
Nikhil Potadea25aecc2019-08-23 16:35:26 -070081
82 auto asyncResp = std::make_shared<AsyncResp>(res);
James Feiste284a7c2019-11-20 16:20:23 -080083 auto health = std::make_shared<HealthPopulate>(asyncResp);
84 health->populate();
85
Nikhil Potadea25aecc2019-08-23 16:35:26 -070086 crow::connections::systemBus->async_method_call(
James Feiste284a7c2019-11-20 16:20:23 -080087 [asyncResp, health](const boost::system::error_code ec,
88 const std::vector<std::string> &storageList) {
Nikhil Potadea25aecc2019-08-23 16:35:26 -070089 nlohmann::json &storageArray =
90 asyncResp->res.jsonValue["Drives"];
91 storageArray = nlohmann::json::array();
James Feiste284a7c2019-11-20 16:20:23 -080092 auto &count = asyncResp->res.jsonValue["Drives@odata.count"];
93 count = 0;
James Feist2ad9c2f2019-10-29 16:26:48 -070094
Nikhil Potadea25aecc2019-08-23 16:35:26 -070095 if (ec)
96 {
97 BMCWEB_LOG_ERROR << "Drive mapper call error";
98 messages::internalError(asyncResp->res);
99 return;
100 }
James Feist2ad9c2f2019-10-29 16:26:48 -0700101
James Feiste284a7c2019-11-20 16:20:23 -0800102 health->inventory.insert(health->inventory.end(),
103 storageList.begin(),
104 storageList.end());
James Feist2ad9c2f2019-10-29 16:26:48 -0700105
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700106 for (const std::string &objpath : storageList)
107 {
108 std::size_t lastPos = objpath.rfind("/");
109 if (lastPos == std::string::npos ||
110 (objpath.size() <= lastPos + 1))
111 {
112 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
113 continue;
114 }
115
116 storageArray.push_back(
117 {{"@odata.id",
James Feistbe13cec2019-10-30 16:38:16 -0700118 "/redfish/v1/Systems/system/Storage/1/Drives/" +
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700119 objpath.substr(lastPos + 1)}});
120 }
121
James Feiste284a7c2019-11-20 16:20:23 -0800122 count = storageArray.size();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700123 },
124 "xyz.openbmc_project.ObjectMapper",
125 "/xyz/openbmc_project/object_mapper",
126 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
127 "/xyz/openbmc_project/inventory", int32_t(0),
128 std::array<const char *, 1>{
129 "xyz.openbmc_project.Inventory.Item.Drive"});
James Feiste284a7c2019-11-20 16:20:23 -0800130
131 crow::connections::systemBus->async_method_call(
132 [asyncResp,
133 health](const boost::system::error_code ec,
134 const crow::openbmc_mapper::GetSubTreeType &subtree) {
135 if (ec || !subtree.size())
136 {
137 messages::internalError(asyncResp->res);
138 return;
139 }
140
141 nlohmann::json &root =
142 asyncResp->res.jsonValue["StorageControllers"];
143 root = nlohmann::json::array();
144 for (const auto &[path, interfaceDict] : subtree)
145 {
146 std::size_t lastPos = path.rfind("/");
147 if (lastPos == std::string::npos ||
148 (path.size() <= lastPos + 1))
149 {
150 BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
151 return;
152 }
153
154 if (interfaceDict.size() != 1)
155 {
156 BMCWEB_LOG_ERROR << "Connection size "
157 << interfaceDict.size()
158 << ", greater than 1";
159 messages::internalError(asyncResp->res);
160 return;
161 }
162
163 const std::string &connectionName =
164 interfaceDict.front().first;
165
166 size_t index = root.size();
167 nlohmann::json &storageController =
168 root.emplace_back(nlohmann::json::object());
169
170 std::string id = path.substr(lastPos + 1);
171
172 storageController["@odata.type"] =
173 "#Storage.v1_7_0.StorageController";
174 storageController["@odata.context"] =
175 "/redfish/v1/$metadata#Storage.StorageController";
176 storageController["@odata.id"] =
177 "/redfish/v1/Systems/system/Storage/1"
178 "#/StorageControllers/" +
179 std::to_string(index);
180 storageController["Name"] = id;
181 storageController["MemberId"] = id;
182 storageController["Status"]["State"] = "Enabled";
183
184 crow::connections::systemBus->async_method_call(
185 [asyncResp, index](const boost::system::error_code ec,
186 const std::variant<bool> present) {
187 // this interface isn't necessary, only check it if
188 // we get a good return
189 if (ec)
190 {
191 return;
192 }
193 const bool *enabled = std::get_if<bool>(&present);
194 if (enabled == nullptr)
195 {
196 BMCWEB_LOG_DEBUG << "Illegal property present";
197 messages::internalError(asyncResp->res);
198 return;
199 }
200 if (!(*enabled))
201 {
202 asyncResp->res
203 .jsonValue["StorageControllers"][index]
204 ["Status"]["State"] = "Disabled";
205 }
206 },
207 connectionName, path, "org.freedesktop.DBus.Properties",
208 "Get", "xyz.openbmc_project.Inventory.Item", "Present");
209
210 crow::connections::systemBus->async_method_call(
211 [asyncResp,
212 index](const boost::system::error_code ec,
213 const std::vector<std::pair<
214 std::string,
215 std::variant<bool, std::string, uint64_t>>>
216 &propertiesList) {
217 if (ec)
218 {
219 // this interface isn't necessary
220 return;
221 }
222 for (const std::pair<
223 std::string,
224 std::variant<bool, std::string, uint64_t>>
225 &property : propertiesList)
226 {
227 // Store DBus properties that are also
228 // Redfish properties with same name and a
229 // string value
230 const std::string &propertyName =
231 property.first;
232 nlohmann::json &object =
233 asyncResp->res
234 .jsonValue["StorageControllers"][index];
235 if ((propertyName == "PartNumber") ||
236 (propertyName == "SerialNumber") ||
237 (propertyName == "Manufacturer") ||
238 (propertyName == "Model"))
239 {
240 const std::string *value =
241 std::get_if<std::string>(
242 &property.second);
243 if (value == nullptr)
244 {
245 // illegal property
246 messages::internalError(asyncResp->res);
247 continue;
248 }
249 object[propertyName] = *value;
250 }
251 }
252 },
253 connectionName, path, "org.freedesktop.DBus.Properties",
254 "GetAll",
255 "xyz.openbmc_project.Inventory.Decorator.Asset");
256 }
257
258 // this is done after we know the json array will no longer be
259 // resized, as json::array uses vector underneath and we need
260 // references to its members that won't change
261 size_t count = 0;
262 for (const auto &[path, interfaceDict] : subtree)
263 {
264 auto subHealth = std::make_shared<HealthPopulate>(
265 asyncResp, root[count]["Status"]);
266 subHealth->inventory.emplace_back(path);
267 health->inventory.emplace_back(path);
268 health->children.emplace_back(subHealth);
269 count++;
270 }
271 },
272 "xyz.openbmc_project.ObjectMapper",
273 "/xyz/openbmc_project/object_mapper",
274 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
275 "/xyz/openbmc_project/inventory", int32_t(0),
276 std::array<const char *, 1>{
277 "xyz.openbmc_project.Inventory.Item.StorageController"});
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700278 }
279};
280
281class Drive : public Node
282{
283 public:
284 Drive(CrowApp &app) :
James Feistbe13cec2019-10-30 16:38:16 -0700285 Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/",
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700286 std::string())
287 {
288 entityPrivileges = {
289 {boost::beast::http::verb::get, {{"Login"}}},
290 {boost::beast::http::verb::head, {{"Login"}}},
291 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
292 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
293 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
294 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
295 }
296
297 private:
298 void doGet(crow::Response &res, const crow::Request &req,
299 const std::vector<std::string> &params) override
300 {
James Feiste284a7c2019-11-20 16:20:23 -0800301 auto asyncResp = std::make_shared<AsyncResp>(res);
302 if (params.size() != 1)
303 {
304 messages::internalError(asyncResp->res);
305 return;
306 }
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700307 const std::string &driveId = params[0];
308
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700309 crow::connections::systemBus->async_method_call(
James Feiste284a7c2019-11-20 16:20:23 -0800310 [asyncResp,
311 driveId](const boost::system::error_code ec,
312 const crow::openbmc_mapper::GetSubTreeType &subtree) {
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700313 if (ec)
314 {
315 BMCWEB_LOG_ERROR << "Drive mapper call error";
316 messages::internalError(asyncResp->res);
317 return;
318 }
319
320 auto object = std::find_if(
321 subtree.begin(), subtree.end(), [&driveId](auto &object) {
322 const std::string &path = object.first;
323 return boost::ends_with(path, "/" + driveId);
324 });
325
326 if (object == subtree.end())
327 {
328 messages::resourceNotFound(asyncResp->res, "Drive",
329 driveId);
330 return;
331 }
332
333 const std::string &path = object->first;
334 const std::vector<
335 std::pair<std::string, std::vector<std::string>>>
336 &connectionNames = object->second;
337
338 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
339 asyncResp->res.jsonValue["@odata.context"] =
340 "/redfish/v1/$metadata#Drive.Drive";
341 asyncResp->res.jsonValue["@odata.id"] =
James Feistbe13cec2019-10-30 16:38:16 -0700342 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
James Feist2ad9c2f2019-10-29 16:26:48 -0700343 asyncResp->res.jsonValue["Name"] = driveId;
344 asyncResp->res.jsonValue["Id"] = driveId;
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700345
346 if (connectionNames.size() != 1)
347 {
348 BMCWEB_LOG_ERROR << "Connection size "
349 << connectionNames.size()
350 << ", greater than 1";
351 messages::internalError(asyncResp->res);
352 return;
353 }
354
355 getMainChassisId(
356 asyncResp, [](const std::string &chassisId,
357 std::shared_ptr<AsyncResp> aRsp) {
358 aRsp->res.jsonValue["Links"]["Chassis"] = {
359 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
360 });
361
362 const std::string &connectionName = connectionNames[0].first;
363 crow::connections::systemBus->async_method_call(
James Feist2ad9c2f2019-10-29 16:26:48 -0700364 [asyncResp](const boost::system::error_code ec,
365 const std::vector<std::pair<
366 std::string,
367 std::variant<bool, std::string, uint64_t>>>
368 &propertiesList) {
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700369 if (ec)
370 {
371 // this interface isn't necessary
372 return;
373 }
374 for (const std::pair<std::string,
375 std::variant<bool, std::string,
376 uint64_t>> &property :
377 propertiesList)
378 {
379 // Store DBus properties that are also
380 // Redfish properties with same name and a
381 // string value
382 const std::string &propertyName = property.first;
383 if ((propertyName == "PartNumber") ||
384 (propertyName == "SerialNumber") ||
385 (propertyName == "Manufacturer") ||
386 (propertyName == "Model"))
387 {
388 const std::string *value =
389 std::get_if<std::string>(&property.second);
390 if (value == nullptr)
391 {
392 // illegal property
393 messages::internalError(asyncResp->res);
394 continue;
395 }
396 asyncResp->res.jsonValue[propertyName] = *value;
397 }
398 }
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700399 },
400 connectionName, path, "org.freedesktop.DBus.Properties",
401 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
402
403 // default it to Enabled
404 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
405
James Feist2ad9c2f2019-10-29 16:26:48 -0700406 auto health = std::make_shared<HealthPopulate>(asyncResp);
James Feiste284a7c2019-11-20 16:20:23 -0800407 health->inventory.emplace_back(path);
James Feist2ad9c2f2019-10-29 16:26:48 -0700408 health->populate();
409
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700410 crow::connections::systemBus->async_method_call(
411 [asyncResp, path](const boost::system::error_code ec,
412 const std::variant<bool> present) {
413 // this interface isn't necessary, only check it if we
414 // get a good return
James Feist2ad9c2f2019-10-29 16:26:48 -0700415 if (ec)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700416 {
James Feist2ad9c2f2019-10-29 16:26:48 -0700417 return;
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700418 }
James Feist2ad9c2f2019-10-29 16:26:48 -0700419 const bool *enabled = std::get_if<bool>(&present);
420 if (enabled == nullptr)
421 {
422 BMCWEB_LOG_DEBUG << "Illegal property present";
423 messages::internalError(asyncResp->res);
424 return;
425 }
426 if (!(*enabled))
427 {
428 asyncResp->res.jsonValue["Status"]["State"] =
429 "Disabled";
430 }
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700431 },
432 connectionName, path, "org.freedesktop.DBus.Properties",
433 "Get", "xyz.openbmc_project.Inventory.Item", "Present");
James Feist22984072019-10-21 16:10:49 -0700434
435 crow::connections::systemBus->async_method_call(
436 [asyncResp](const boost::system::error_code ec,
437 const std::variant<bool> rebuilding) {
438 // this interface isn't necessary, only check it if we
439 // get a good return
440 if (ec)
441 {
442 return;
443 }
444 const bool *updating = std::get_if<bool>(&rebuilding);
445 if (updating == nullptr)
446 {
447 BMCWEB_LOG_DEBUG << "Illegal property present";
448 messages::internalError(asyncResp->res);
449 return;
450 }
451
452 // updating and disabled in the backend shouldn't be
453 // able to be set at the same time, so we don't need to
454 // check for the race condition of these two calls
455 if ((*updating))
456 {
457 asyncResp->res.jsonValue["Status"]["State"] =
458 "Updating";
459 }
460 },
461 connectionName, path, "org.freedesktop.DBus.Properties",
462 "Get", "xyz.openbmc_project.State.Drive", "Rebuilding");
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700463 },
464 "xyz.openbmc_project.ObjectMapper",
465 "/xyz/openbmc_project/object_mapper",
466 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
467 "/xyz/openbmc_project/inventory", int32_t(0),
468 std::array<const char *, 1>{
469 "xyz.openbmc_project.Inventory.Item.Drive"});
470 }
471};
472} // namespace redfish