blob: a48ef8c5fc725a756d21a8ea3bbb1aff32fa37a4 [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 Tanoused398212021-06-09 17:05:54 -070022#include <registries/privilege_registry.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070023
24namespace redfish
25{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070026inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070027{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070028 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070029 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070030 .methods(boost::beast::http::verb::get)(
31 [](const crow::Request&,
32 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
33 asyncResp->res.jsonValue["@odata.type"] =
34 "#StorageCollection.StorageCollection";
35 asyncResp->res.jsonValue["@odata.id"] =
36 "/redfish/v1/Systems/system/Storage";
37 asyncResp->res.jsonValue["Name"] = "Storage Collection";
38 asyncResp->res.jsonValue["Members"] = {
39 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
40 asyncResp->res.jsonValue["Members@odata.count"] = 1;
41 });
42}
Nikhil Potadea25aecc2019-08-23 16:35:26 -070043
John Edward Broadbent7e860f12021-04-08 15:57:16 -070044inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070045{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070046 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
Ed Tanoused398212021-06-09 17:05:54 -070047 .privileges(redfish::privileges::getStorage)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070048 .methods(
49 boost::beast::http::verb::
50 get)([](const crow::Request&,
51 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
52 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
53 asyncResp->res.jsonValue["@odata.id"] =
54 "/redfish/v1/Systems/system/Storage/1";
55 asyncResp->res.jsonValue["Name"] = "Storage";
56 asyncResp->res.jsonValue["Id"] = "1";
57 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
Nikhil Potadea25aecc2019-08-23 16:35:26 -070058
John Edward Broadbent7e860f12021-04-08 15:57:16 -070059 auto health = std::make_shared<HealthPopulate>(asyncResp);
60 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -070061
John Edward Broadbent7e860f12021-04-08 15:57:16 -070062 crow::connections::systemBus->async_method_call(
63 [asyncResp,
64 health](const boost::system::error_code ec,
65 const std::vector<std::string>& storageList) {
66 nlohmann::json& storageArray =
67 asyncResp->res.jsonValue["Drives"];
68 storageArray = nlohmann::json::array();
69 auto& count =
70 asyncResp->res.jsonValue["Drives@odata.count"];
71 count = 0;
James Feiste284a7c2019-11-20 16:20:23 -080072
John Edward Broadbent7e860f12021-04-08 15:57:16 -070073 if (ec)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070074 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070075 BMCWEB_LOG_ERROR << "Drive mapper call error";
76 messages::internalError(asyncResp->res);
James Feiste284a7c2019-11-20 16:20:23 -080077 return;
78 }
79
John Edward Broadbent7e860f12021-04-08 15:57:16 -070080 health->inventory.insert(health->inventory.end(),
81 storageList.begin(),
82 storageList.end());
83
84 for (const std::string& objpath : storageList)
85 {
86 std::size_t lastPos = objpath.rfind('/');
87 if (lastPos == std::string::npos ||
88 (objpath.size() <= lastPos + 1))
89 {
90 BMCWEB_LOG_ERROR << "Failed to find '/' in "
91 << objpath;
92 continue;
93 }
94
95 storageArray.push_back(
96 {{"@odata.id",
97 "/redfish/v1/Systems/system/Storage/1/Drives/" +
98 objpath.substr(lastPos + 1)}});
99 }
100
101 count = storageArray.size();
102 },
103 "xyz.openbmc_project.ObjectMapper",
104 "/xyz/openbmc_project/object_mapper",
105 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
106 "/xyz/openbmc_project/inventory", int32_t(0),
107 std::array<const char*, 1>{
108 "xyz.openbmc_project.Inventory.Item.Drive"});
109
110 crow::connections::systemBus->async_method_call(
111 [asyncResp,
112 health](const boost::system::error_code ec,
113 const crow::openbmc_mapper::GetSubTreeType& subtree) {
114 if (ec || !subtree.size())
115 {
116 // doesn't have to be there
117 return;
118 }
119
120 nlohmann::json& root =
121 asyncResp->res.jsonValue["StorageControllers"];
122 root = nlohmann::json::array();
123 for (const auto& [path, interfaceDict] : subtree)
124 {
125 std::size_t lastPos = path.rfind('/');
126 if (lastPos == std::string::npos ||
127 (path.size() <= lastPos + 1))
128 {
129 BMCWEB_LOG_ERROR << "Failed to find '/' in "
130 << path;
131 return;
132 }
133
134 if (interfaceDict.size() != 1)
135 {
136 BMCWEB_LOG_ERROR << "Connection size "
137 << interfaceDict.size()
138 << ", greater than 1";
139 messages::internalError(asyncResp->res);
140 return;
141 }
142
143 const std::string& connectionName =
144 interfaceDict.front().first;
145
146 size_t index = root.size();
147 nlohmann::json& storageController =
148 root.emplace_back(nlohmann::json::object());
149
150 std::string id = path.substr(lastPos + 1);
151
152 storageController["@odata.type"] =
153 "#Storage.v1_7_0.StorageController";
154 storageController["@odata.id"] =
155 "/redfish/v1/Systems/system/Storage/1"
156 "#/StorageControllers/" +
157 std::to_string(index);
158 storageController["Name"] = id;
159 storageController["MemberId"] = id;
160 storageController["Status"]["State"] = "Enabled";
161
162 crow::connections::systemBus->async_method_call(
163 [asyncResp,
164 index](const boost::system::error_code ec2,
165 const std::variant<bool> present) {
166 // this interface isn't necessary, only check it
167 // if we get a good return
168 if (ec2)
169 {
170 return;
171 }
172 const bool* enabled =
173 std::get_if<bool>(&present);
174 if (enabled == nullptr)
175 {
176 BMCWEB_LOG_DEBUG
177 << "Illegal property present";
178 messages::internalError(asyncResp->res);
179 return;
180 }
181 if (!(*enabled))
182 {
183 asyncResp->res
184 .jsonValue["StorageControllers"][index]
185 ["Status"]["State"] =
186 "Disabled";
187 }
188 },
189 connectionName, path,
190 "org.freedesktop.DBus.Properties", "Get",
191 "xyz.openbmc_project.Inventory.Item", "Present");
192
193 crow::connections::systemBus->async_method_call(
194 [asyncResp, index](
195 const boost::system::error_code ec2,
196 const std::vector<std::pair<
197 std::string,
198 std::variant<bool, std::string, uint64_t>>>&
199 propertiesList) {
200 if (ec2)
201 {
202 // this interface isn't necessary
203 return;
204 }
205 for (const std::pair<
206 std::string,
207 std::variant<bool, std::string,
208 uint64_t>>& property :
209 propertiesList)
210 {
211 // Store DBus properties that are also
212 // Redfish properties with same name and a
213 // string value
214 const std::string& propertyName =
215 property.first;
216 nlohmann::json& object =
217 asyncResp->res
218 .jsonValue["StorageControllers"]
219 [index];
220 if ((propertyName == "PartNumber") ||
221 (propertyName == "SerialNumber") ||
222 (propertyName == "Manufacturer") ||
223 (propertyName == "Model"))
224 {
225 const std::string* value =
226 std::get_if<std::string>(
227 &property.second);
228 if (value == nullptr)
229 {
230 // illegal property
231 messages::internalError(
232 asyncResp->res);
233 return;
234 }
235 object[propertyName] = *value;
236 }
237 }
238 },
239 connectionName, path,
240 "org.freedesktop.DBus.Properties", "GetAll",
241 "xyz.openbmc_project.Inventory.Decorator.Asset");
242 }
243
244 // this is done after we know the json array will no longer
245 // be resized, as json::array uses vector underneath and we
246 // need references to its members that won't change
247 size_t count = 0;
248 for (const auto& [path, interfaceDict] : subtree)
249 {
250 auto subHealth = std::make_shared<HealthPopulate>(
251 asyncResp, root[count]["Status"]);
252 subHealth->inventory.emplace_back(path);
253 health->inventory.emplace_back(path);
254 health->children.emplace_back(subHealth);
255 count++;
256 }
257 },
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.Item.StorageController"});
264 });
265}
266
267inline void requestRoutesDrive(App& app)
268{
269 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700270 .privileges(redfish::privileges::getDrive)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700271 .methods(
272 boost::beast::http::verb::get)([](const crow::Request&,
273 const std::shared_ptr<
274 bmcweb::AsyncResp>& asyncResp,
275 const std::string& driveId) {
276 crow::connections::systemBus->async_method_call(
277 [asyncResp,
278 driveId](const boost::system::error_code ec,
279 const crow::openbmc_mapper::GetSubTreeType& subtree) {
280 if (ec)
281 {
282 BMCWEB_LOG_ERROR << "Drive mapper call error";
283 messages::internalError(asyncResp->res);
284 return;
285 }
286
287 auto object2 = std::find_if(
288 subtree.begin(), subtree.end(),
289 [&driveId](auto& object) {
290 const std::string& path = object.first;
291 return boost::ends_with(path, "/" + driveId);
292 });
293
294 if (object2 == subtree.end())
295 {
296 messages::resourceNotFound(asyncResp->res, "Drive",
297 driveId);
298 return;
299 }
300
301 const std::string& path = object2->first;
302 const std::vector<
303 std::pair<std::string, std::vector<std::string>>>&
304 connectionNames = object2->second;
305
306 asyncResp->res.jsonValue["@odata.type"] =
307 "#Drive.v1_7_0.Drive";
308 asyncResp->res.jsonValue["@odata.id"] =
309 "/redfish/v1/Systems/system/Storage/1/Drives/" +
310 driveId;
311 asyncResp->res.jsonValue["Name"] = driveId;
312 asyncResp->res.jsonValue["Id"] = driveId;
313
314 if (connectionNames.size() != 1)
James Feiste284a7c2019-11-20 16:20:23 -0800315 {
316 BMCWEB_LOG_ERROR << "Connection size "
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700317 << connectionNames.size()
James Feiste284a7c2019-11-20 16:20:23 -0800318 << ", greater than 1";
319 messages::internalError(asyncResp->res);
320 return;
321 }
322
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700323 getMainChassisId(
324 asyncResp,
325 [](const std::string& chassisId,
326 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
327 aRsp->res.jsonValue["Links"]["Chassis"] = {
328 {"@odata.id",
329 "/redfish/v1/Chassis/" + chassisId}};
330 });
331
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500332 const std::string& connectionName =
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700333 connectionNames[0].first;
James Feiste284a7c2019-11-20 16:20:23 -0800334 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700335 [asyncResp](
336 const boost::system::error_code ec2,
337 const std::vector<std::pair<
338 std::string,
339 std::variant<bool, std::string, uint64_t>>>&
340 propertiesList) {
Ed Tanous23a21a12020-07-25 04:45:05 +0000341 if (ec2)
James Feiste284a7c2019-11-20 16:20:23 -0800342 {
343 // this interface isn't necessary
344 return;
345 }
346 for (const std::pair<
347 std::string,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500348 std::variant<bool, std::string, uint64_t>>&
349 property : propertiesList)
James Feiste284a7c2019-11-20 16:20:23 -0800350 {
351 // Store DBus properties that are also
352 // Redfish properties with same name and a
353 // string value
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500354 const std::string& propertyName =
James Feiste284a7c2019-11-20 16:20:23 -0800355 property.first;
James Feiste284a7c2019-11-20 16:20:23 -0800356 if ((propertyName == "PartNumber") ||
357 (propertyName == "SerialNumber") ||
358 (propertyName == "Manufacturer") ||
359 (propertyName == "Model"))
360 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500361 const std::string* value =
James Feiste284a7c2019-11-20 16:20:23 -0800362 std::get_if<std::string>(
363 &property.second);
364 if (value == nullptr)
365 {
366 // illegal property
367 messages::internalError(asyncResp->res);
Chicago Duan601af5e2021-04-15 16:59:25 +0800368 return;
James Feiste284a7c2019-11-20 16:20:23 -0800369 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700370 asyncResp->res.jsonValue[propertyName] =
371 *value;
James Feiste284a7c2019-11-20 16:20:23 -0800372 }
373 }
374 },
375 connectionName, path, "org.freedesktop.DBus.Properties",
376 "GetAll",
377 "xyz.openbmc_project.Inventory.Decorator.Asset");
James Feiste284a7c2019-11-20 16:20:23 -0800378
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700379 // default it to Enabled
380 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
381
382 auto health = std::make_shared<HealthPopulate>(asyncResp);
James Feiste284a7c2019-11-20 16:20:23 -0800383 health->inventory.emplace_back(path);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700384 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700385
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700386 crow::connections::systemBus->async_method_call(
387 [asyncResp, path](const boost::system::error_code ec2,
388 const std::variant<bool> present) {
389 // this interface isn't necessary, only check it if
390 // we get a good return
391 if (ec2)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700392 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700393 return;
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700394 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700395 const bool* enabled = std::get_if<bool>(&present);
396 if (enabled == nullptr)
397 {
398 BMCWEB_LOG_DEBUG << "Illegal property present";
399 messages::internalError(asyncResp->res);
400 return;
401 }
402 if (!(*enabled))
403 {
404 asyncResp->res.jsonValue["Status"]["State"] =
405 "Disabled";
406 }
407 },
408 connectionName, path, "org.freedesktop.DBus.Properties",
409 "Get", "xyz.openbmc_project.Inventory.Item", "Present");
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700410
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700411 crow::connections::systemBus->async_method_call(
412 [asyncResp](const boost::system::error_code ec2,
413 const std::variant<bool> rebuilding) {
414 // this interface isn't necessary, only check it if
415 // we get a good return
416 if (ec2)
417 {
418 return;
419 }
420 const bool* updating =
421 std::get_if<bool>(&rebuilding);
422 if (updating == nullptr)
423 {
424 BMCWEB_LOG_DEBUG << "Illegal property present";
425 messages::internalError(asyncResp->res);
426 return;
427 }
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700428
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700429 // updating and disabled in the backend shouldn't be
430 // able to be set at the same time, so we don't need
431 // to check for the race condition of these two
432 // calls
433 if ((*updating))
434 {
435 asyncResp->res.jsonValue["Status"]["State"] =
436 "Updating";
437 }
438 },
439 connectionName, path, "org.freedesktop.DBus.Properties",
440 "Get", "xyz.openbmc_project.State.Drive", "Rebuilding");
441 },
442 "xyz.openbmc_project.ObjectMapper",
443 "/xyz/openbmc_project/object_mapper",
444 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
445 "/xyz/openbmc_project/inventory", int32_t(0),
446 std::array<const char*, 1>{
447 "xyz.openbmc_project.Inventory.Item.Drive"});
448 });
449}
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700450} // namespace redfish