blob: a2a2201dfa8ba6a72681909a79e64ac102dd9a0e [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080022#include <dbus_utility.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070024#include <sdbusplus/asio/property.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070025
26namespace redfish
27{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070028inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070029{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070030 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070031 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070032 .methods(boost::beast::http::verb::get)(
33 [](const crow::Request&,
34 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
35 asyncResp->res.jsonValue["@odata.type"] =
36 "#StorageCollection.StorageCollection";
37 asyncResp->res.jsonValue["@odata.id"] =
38 "/redfish/v1/Systems/system/Storage";
39 asyncResp->res.jsonValue["Name"] = "Storage Collection";
40 asyncResp->res.jsonValue["Members"] = {
41 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
42 asyncResp->res.jsonValue["Members@odata.count"] = 1;
43 });
44}
Nikhil Potadea25aecc2019-08-23 16:35:26 -070045
John Edward Broadbent7e860f12021-04-08 15:57:16 -070046inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070047{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070048 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
Ed Tanoused398212021-06-09 17:05:54 -070049 .privileges(redfish::privileges::getStorage)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070050 .methods(
51 boost::beast::http::verb::
52 get)([](const crow::Request&,
53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
54 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
55 asyncResp->res.jsonValue["@odata.id"] =
56 "/redfish/v1/Systems/system/Storage/1";
57 asyncResp->res.jsonValue["Name"] = "Storage";
58 asyncResp->res.jsonValue["Id"] = "1";
59 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
Nikhil Potadea25aecc2019-08-23 16:35:26 -070060
John Edward Broadbent7e860f12021-04-08 15:57:16 -070061 auto health = std::make_shared<HealthPopulate>(asyncResp);
62 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -070063
John Edward Broadbent7e860f12021-04-08 15:57:16 -070064 crow::connections::systemBus->async_method_call(
65 [asyncResp,
66 health](const boost::system::error_code ec,
67 const std::vector<std::string>& storageList) {
68 nlohmann::json& storageArray =
69 asyncResp->res.jsonValue["Drives"];
70 storageArray = nlohmann::json::array();
71 auto& count =
72 asyncResp->res.jsonValue["Drives@odata.count"];
73 count = 0;
James Feiste284a7c2019-11-20 16:20:23 -080074
John Edward Broadbent7e860f12021-04-08 15:57:16 -070075 if (ec)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070076 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070077 BMCWEB_LOG_ERROR << "Drive mapper call error";
78 messages::internalError(asyncResp->res);
James Feiste284a7c2019-11-20 16:20:23 -080079 return;
80 }
81
John Edward Broadbent7e860f12021-04-08 15:57:16 -070082 health->inventory.insert(health->inventory.end(),
83 storageList.begin(),
84 storageList.end());
85
86 for (const std::string& objpath : storageList)
87 {
88 std::size_t lastPos = objpath.rfind('/');
89 if (lastPos == std::string::npos ||
90 (objpath.size() <= lastPos + 1))
91 {
92 BMCWEB_LOG_ERROR << "Failed to find '/' in "
93 << objpath;
94 continue;
95 }
96
97 storageArray.push_back(
98 {{"@odata.id",
99 "/redfish/v1/Systems/system/Storage/1/Drives/" +
100 objpath.substr(lastPos + 1)}});
101 }
102
103 count = storageArray.size();
104 },
105 "xyz.openbmc_project.ObjectMapper",
106 "/xyz/openbmc_project/object_mapper",
107 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
108 "/xyz/openbmc_project/inventory", int32_t(0),
109 std::array<const char*, 1>{
110 "xyz.openbmc_project.Inventory.Item.Drive"});
111
112 crow::connections::systemBus->async_method_call(
113 [asyncResp,
114 health](const boost::system::error_code ec,
115 const crow::openbmc_mapper::GetSubTreeType& subtree) {
Ed Tanous26f69762022-01-25 09:49:11 -0800116 if (ec || subtree.empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700117 {
118 // doesn't have to be there
119 return;
120 }
121
122 nlohmann::json& root =
123 asyncResp->res.jsonValue["StorageControllers"];
124 root = nlohmann::json::array();
125 for (const auto& [path, interfaceDict] : subtree)
126 {
127 std::size_t lastPos = path.rfind('/');
128 if (lastPos == std::string::npos ||
129 (path.size() <= lastPos + 1))
130 {
131 BMCWEB_LOG_ERROR << "Failed to find '/' in "
132 << path;
133 return;
134 }
135
136 if (interfaceDict.size() != 1)
137 {
138 BMCWEB_LOG_ERROR << "Connection size "
139 << interfaceDict.size()
140 << ", greater than 1";
141 messages::internalError(asyncResp->res);
142 return;
143 }
144
145 const std::string& connectionName =
146 interfaceDict.front().first;
147
148 size_t index = root.size();
149 nlohmann::json& storageController =
150 root.emplace_back(nlohmann::json::object());
151
152 std::string id = path.substr(lastPos + 1);
153
154 storageController["@odata.type"] =
155 "#Storage.v1_7_0.StorageController";
156 storageController["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +0800157 "/redfish/v1/Systems/system/Storage/1#/StorageControllers/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700158 std::to_string(index);
159 storageController["Name"] = id;
160 storageController["MemberId"] = id;
161 storageController["Status"]["State"] = "Enabled";
162
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700163 sdbusplus::asio::getProperty<bool>(
164 *crow::connections::systemBus, connectionName, path,
165 "xyz.openbmc_project.Inventory.Item", "Present",
166 [asyncResp,
167 index](const boost::system::error_code ec2,
168 bool enabled) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700169 // this interface isn't necessary, only check it
170 // if we get a good return
171 if (ec2)
172 {
173 return;
174 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700175 if (!enabled)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700176 {
177 asyncResp->res
178 .jsonValue["StorageControllers"][index]
179 ["Status"]["State"] =
180 "Disabled";
181 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700182 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700183
184 crow::connections::systemBus->async_method_call(
185 [asyncResp, index](
186 const boost::system::error_code ec2,
Ed Tanous168e20c2021-12-13 14:39:53 -0800187 const std::vector<
188 std::pair<std::string,
189 dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700190 propertiesList) {
191 if (ec2)
192 {
193 // this interface isn't necessary
194 return;
195 }
196 for (const std::pair<
197 std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -0800198 dbus::utility::DbusVariantType>&
199 property : propertiesList)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700200 {
201 // Store DBus properties that are also
202 // Redfish properties with same name and a
203 // string value
204 const std::string& propertyName =
205 property.first;
206 nlohmann::json& object =
207 asyncResp->res
208 .jsonValue["StorageControllers"]
209 [index];
210 if ((propertyName == "PartNumber") ||
211 (propertyName == "SerialNumber") ||
212 (propertyName == "Manufacturer") ||
213 (propertyName == "Model"))
214 {
215 const std::string* value =
216 std::get_if<std::string>(
217 &property.second);
218 if (value == nullptr)
219 {
220 // illegal property
221 messages::internalError(
222 asyncResp->res);
223 return;
224 }
225 object[propertyName] = *value;
226 }
227 }
228 },
229 connectionName, path,
230 "org.freedesktop.DBus.Properties", "GetAll",
231 "xyz.openbmc_project.Inventory.Decorator.Asset");
232 }
233
234 // this is done after we know the json array will no longer
235 // be resized, as json::array uses vector underneath and we
236 // need references to its members that won't change
237 size_t count = 0;
238 for (const auto& [path, interfaceDict] : subtree)
239 {
240 auto subHealth = std::make_shared<HealthPopulate>(
241 asyncResp, root[count]["Status"]);
242 subHealth->inventory.emplace_back(path);
243 health->inventory.emplace_back(path);
244 health->children.emplace_back(subHealth);
245 count++;
246 }
247 },
248 "xyz.openbmc_project.ObjectMapper",
249 "/xyz/openbmc_project/object_mapper",
250 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
251 "/xyz/openbmc_project/inventory", int32_t(0),
252 std::array<const char*, 1>{
253 "xyz.openbmc_project.Inventory.Item.StorageController"});
254 });
255}
256
Willy Tu03913172021-11-08 02:03:19 -0800257inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
258 const std::string& connectionName,
259 const std::string& path)
260{
261 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -0800262 [asyncResp](const boost::system::error_code ec,
263 const std::vector<
264 std::pair<std::string, dbus::utility::DbusVariantType>>&
265 propertiesList) {
Willy Tu03913172021-11-08 02:03:19 -0800266 if (ec)
267 {
268 // this interface isn't necessary
269 return;
270 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800271 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
Willy Tu03913172021-11-08 02:03:19 -0800272 property : propertiesList)
273 {
274 // Store DBus properties that are also
275 // Redfish properties with same name and a
276 // string value
277 const std::string& propertyName = property.first;
278 if ((propertyName == "PartNumber") ||
279 (propertyName == "SerialNumber") ||
280 (propertyName == "Manufacturer") ||
281 (propertyName == "Model"))
282 {
283 const std::string* value =
284 std::get_if<std::string>(&property.second);
285 if (value == nullptr)
286 {
287 // illegal property
288 messages::internalError(asyncResp->res);
289 return;
290 }
291 asyncResp->res.jsonValue[propertyName] = *value;
292 }
293 }
294 },
295 connectionName, path, "org.freedesktop.DBus.Properties", "GetAll",
296 "xyz.openbmc_project.Inventory.Decorator.Asset");
297}
298
299inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
300 const std::string& connectionName,
301 const std::string& path)
302{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700303 sdbusplus::asio::getProperty<bool>(
304 *crow::connections::systemBus, connectionName, path,
305 "xyz.openbmc_project.Inventory.Item", "Present",
Willy Tu03913172021-11-08 02:03:19 -0800306 [asyncResp, path](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700307 const bool enabled) {
Willy Tu03913172021-11-08 02:03:19 -0800308 // this interface isn't necessary, only check it if
309 // we get a good return
310 if (ec)
311 {
312 return;
313 }
314
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700315 if (!enabled)
Willy Tu03913172021-11-08 02:03:19 -0800316 {
317 asyncResp->res.jsonValue["Status"]["State"] = "Disabled";
318 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700319 });
Willy Tu03913172021-11-08 02:03:19 -0800320}
321
322inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
323 const std::string& connectionName,
324 const std::string& path)
325{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700326 sdbusplus::asio::getProperty<bool>(
327 *crow::connections::systemBus, connectionName, path,
328 "xyz.openbmc_project.State.Drive", "Rebuilding",
329 [asyncResp](const boost::system::error_code ec, const bool updating) {
Willy Tu03913172021-11-08 02:03:19 -0800330 // this interface isn't necessary, only check it
331 // if we get a good return
332 if (ec)
333 {
334 return;
335 }
336
Willy Tu03913172021-11-08 02:03:19 -0800337 // updating and disabled in the backend shouldn't be
338 // able to be set at the same time, so we don't need
339 // to check for the race condition of these two
340 // calls
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700341 if (updating)
Willy Tu03913172021-11-08 02:03:19 -0800342 {
343 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
344 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700345 });
Willy Tu03913172021-11-08 02:03:19 -0800346}
347
Willy Tu19b8e9a2021-11-08 02:55:03 -0800348inline std::optional<std::string> convertDriveType(const std::string& type)
349{
350 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
351 {
352 return "HDD";
353 }
354 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
355 {
356 return "SSD";
357 }
358
359 return std::nullopt;
360}
361
362inline std::optional<std::string> convertDriveProtocol(const std::string& proto)
363{
364 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
365 {
366 return "SAS";
367 }
368 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
369 {
370 return "SATA";
371 }
372 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
373 {
374 return "NVMe";
375 }
376 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
377 {
378 return "FC";
379 }
380
381 return std::nullopt;
382}
383
384inline void
385 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
386 const std::string& connectionName,
387 const std::string& path)
388{
389 sdbusplus::asio::getAllProperties(
390 *crow::connections::systemBus, connectionName, path,
391 "xyz.openbmc_project.Inventory.Item.Drive",
392 [asyncResp](const boost::system::error_code ec,
393 const std::vector<
394 std::pair<std::string, dbus::utility::DbusVariantType>>&
395 propertiesList) {
396 if (ec)
397 {
398 // this interface isn't required
399 return;
400 }
401 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
402 property : propertiesList)
403 {
404 const std::string& propertyName = property.first;
405 if (propertyName == "Type")
406 {
407 const std::string* value =
408 std::get_if<std::string>(&property.second);
409 if (value == nullptr)
410 {
411 // illegal property
412 BMCWEB_LOG_ERROR << "Illegal property: Type";
413 messages::internalError(asyncResp->res);
414 return;
415 }
416
417 std::optional<std::string> mediaType =
418 convertDriveType(*value);
419 if (!mediaType)
420 {
421 BMCWEB_LOG_ERROR << "Unsupported DriveType Interface: "
422 << *value;
423 messages::internalError(asyncResp->res);
424 return;
425 }
426
427 asyncResp->res.jsonValue["MediaType"] = *mediaType;
428 }
429 else if (propertyName == "Capacity")
430 {
431 const uint64_t* capacity =
432 std::get_if<uint64_t>(&property.second);
433 if (capacity == nullptr)
434 {
435 BMCWEB_LOG_ERROR << "Illegal property: Capacity";
436 messages::internalError(asyncResp->res);
437 return;
438 }
439 if (*capacity == 0)
440 {
441 // drive capacity not known
442 continue;
443 }
444
445 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
446 }
447 else if (propertyName == "Protocol")
448 {
449 const std::string* value =
450 std::get_if<std::string>(&property.second);
451 if (value == nullptr)
452 {
453 BMCWEB_LOG_ERROR << "Illegal property: Protocol";
454 messages::internalError(asyncResp->res);
455 return;
456 }
457
458 std::optional<std::string> proto =
459 convertDriveProtocol(*value);
460 if (!proto)
461 {
462 BMCWEB_LOG_ERROR
463 << "Unsupported DrivePrototype Interface: "
464 << *value;
465 messages::internalError(asyncResp->res);
466 return;
467 }
468 asyncResp->res.jsonValue["Protocol"] = *proto;
469 }
470 }
471 });
472}
473
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700474inline void requestRoutesDrive(App& app)
475{
476 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700477 .privileges(redfish::privileges::getDrive)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700478 .methods(
479 boost::beast::http::verb::get)([](const crow::Request&,
480 const std::shared_ptr<
481 bmcweb::AsyncResp>& asyncResp,
482 const std::string& driveId) {
483 crow::connections::systemBus->async_method_call(
484 [asyncResp,
485 driveId](const boost::system::error_code ec,
486 const crow::openbmc_mapper::GetSubTreeType& subtree) {
487 if (ec)
488 {
489 BMCWEB_LOG_ERROR << "Drive mapper call error";
490 messages::internalError(asyncResp->res);
491 return;
492 }
493
Willy Tu03913172021-11-08 02:03:19 -0800494 auto drive = std::find_if(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700495 subtree.begin(), subtree.end(),
Willy Tu03913172021-11-08 02:03:19 -0800496 [&driveId](const std::pair<
497 std::string,
498 std::vector<std::pair<
499 std::string, std::vector<std::string>>>>&
500 object) {
501 return sdbusplus::message::object_path(object.first)
502 .filename() == driveId;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700503 });
504
Willy Tu03913172021-11-08 02:03:19 -0800505 if (drive == subtree.end())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700506 {
507 messages::resourceNotFound(asyncResp->res, "Drive",
508 driveId);
509 return;
510 }
511
Willy Tu03913172021-11-08 02:03:19 -0800512 const std::string& path = drive->first;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700513 const std::vector<
514 std::pair<std::string, std::vector<std::string>>>&
Willy Tu03913172021-11-08 02:03:19 -0800515 connectionNames = drive->second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700516
517 asyncResp->res.jsonValue["@odata.type"] =
518 "#Drive.v1_7_0.Drive";
519 asyncResp->res.jsonValue["@odata.id"] =
520 "/redfish/v1/Systems/system/Storage/1/Drives/" +
521 driveId;
522 asyncResp->res.jsonValue["Name"] = driveId;
523 asyncResp->res.jsonValue["Id"] = driveId;
524
525 if (connectionNames.size() != 1)
James Feiste284a7c2019-11-20 16:20:23 -0800526 {
527 BMCWEB_LOG_ERROR << "Connection size "
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700528 << connectionNames.size()
Willy Tu03913172021-11-08 02:03:19 -0800529 << ", not equal to 1";
James Feiste284a7c2019-11-20 16:20:23 -0800530 messages::internalError(asyncResp->res);
531 return;
532 }
533
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700534 getMainChassisId(
535 asyncResp,
536 [](const std::string& chassisId,
537 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
538 aRsp->res.jsonValue["Links"]["Chassis"] = {
539 {"@odata.id",
540 "/redfish/v1/Chassis/" + chassisId}};
541 });
542
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700543 // default it to Enabled
544 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
545
546 auto health = std::make_shared<HealthPopulate>(asyncResp);
James Feiste284a7c2019-11-20 16:20:23 -0800547 health->inventory.emplace_back(path);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700548 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700549
Willy Tu03913172021-11-08 02:03:19 -0800550 const std::string& connectionName =
551 connectionNames[0].first;
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700552
Willy Tu03913172021-11-08 02:03:19 -0800553 getDriveAsset(asyncResp, connectionName, path);
554 getDrivePresent(asyncResp, connectionName, path);
555 getDriveState(asyncResp, connectionName, path);
Willy Tu19b8e9a2021-11-08 02:55:03 -0800556 getDriveItemProperties(asyncResp, connectionName, path);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700557 },
558 "xyz.openbmc_project.ObjectMapper",
559 "/xyz/openbmc_project/object_mapper",
560 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
561 "/xyz/openbmc_project/inventory", int32_t(0),
562 std::array<const char*, 1>{
563 "xyz.openbmc_project.Inventory.Item.Drive"});
564 });
565}
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700566} // namespace redfish