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