blob: 80e60599b5313a6e5ad62d24e6e7bf9f15ae1333 [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
Willy Tu03913172021-11-08 02:03:19 -0800267inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
268 const std::string& connectionName,
269 const std::string& path)
270{
271 crow::connections::systemBus->async_method_call(
272 [asyncResp](
273 const boost::system::error_code ec,
274 const std::vector<std::pair<
275 std::string, std::variant<bool, std::string, uint64_t>>>&
276 propertiesList) {
277 if (ec)
278 {
279 // this interface isn't necessary
280 return;
281 }
282 for (const std::pair<std::string,
283 std::variant<bool, std::string, uint64_t>>&
284 property : propertiesList)
285 {
286 // Store DBus properties that are also
287 // Redfish properties with same name and a
288 // string value
289 const std::string& propertyName = property.first;
290 if ((propertyName == "PartNumber") ||
291 (propertyName == "SerialNumber") ||
292 (propertyName == "Manufacturer") ||
293 (propertyName == "Model"))
294 {
295 const std::string* value =
296 std::get_if<std::string>(&property.second);
297 if (value == nullptr)
298 {
299 // illegal property
300 messages::internalError(asyncResp->res);
301 return;
302 }
303 asyncResp->res.jsonValue[propertyName] = *value;
304 }
305 }
306 },
307 connectionName, path, "org.freedesktop.DBus.Properties", "GetAll",
308 "xyz.openbmc_project.Inventory.Decorator.Asset");
309}
310
311inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
312 const std::string& connectionName,
313 const std::string& path)
314{
315 crow::connections::systemBus->async_method_call(
316 [asyncResp, path](const boost::system::error_code ec,
317 const std::variant<bool> present) {
318 // this interface isn't necessary, only check it if
319 // we get a good return
320 if (ec)
321 {
322 return;
323 }
324
325 const bool* enabled = std::get_if<bool>(&present);
326 if (enabled == nullptr)
327 {
328 BMCWEB_LOG_DEBUG << "Illegal property present";
329 messages::internalError(asyncResp->res);
330 return;
331 }
332 if (!(*enabled))
333 {
334 asyncResp->res.jsonValue["Status"]["State"] = "Disabled";
335 }
336 },
337 connectionName, path, "org.freedesktop.DBus.Properties", "Get",
338 "xyz.openbmc_project.Inventory.Item", "Present");
339}
340
341inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
342 const std::string& connectionName,
343 const std::string& path)
344{
345 crow::connections::systemBus->async_method_call(
346 [asyncResp](const boost::system::error_code ec,
347 const std::variant<bool> rebuilding) {
348 // this interface isn't necessary, only check it
349 // if we get a good return
350 if (ec)
351 {
352 return;
353 }
354
355 const bool* updating = std::get_if<bool>(&rebuilding);
356 if (updating == nullptr)
357 {
358 BMCWEB_LOG_DEBUG << "Illegal property present";
359 messages::internalError(asyncResp->res);
360 return;
361 }
362
363 // updating and disabled in the backend shouldn't be
364 // able to be set at the same time, so we don't need
365 // to check for the race condition of these two
366 // calls
367 if (*updating)
368 {
369 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
370 }
371 },
372 connectionName, path, "org.freedesktop.DBus.Properties", "Get",
373 "xyz.openbmc_project.State.Drive", "Rebuilding");
374}
375
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700376inline void requestRoutesDrive(App& app)
377{
378 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700379 .privileges(redfish::privileges::getDrive)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700380 .methods(
381 boost::beast::http::verb::get)([](const crow::Request&,
382 const std::shared_ptr<
383 bmcweb::AsyncResp>& asyncResp,
384 const std::string& driveId) {
385 crow::connections::systemBus->async_method_call(
386 [asyncResp,
387 driveId](const boost::system::error_code ec,
388 const crow::openbmc_mapper::GetSubTreeType& subtree) {
389 if (ec)
390 {
391 BMCWEB_LOG_ERROR << "Drive mapper call error";
392 messages::internalError(asyncResp->res);
393 return;
394 }
395
Willy Tu03913172021-11-08 02:03:19 -0800396 auto drive = std::find_if(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700397 subtree.begin(), subtree.end(),
Willy Tu03913172021-11-08 02:03:19 -0800398 [&driveId](const std::pair<
399 std::string,
400 std::vector<std::pair<
401 std::string, std::vector<std::string>>>>&
402 object) {
403 return sdbusplus::message::object_path(object.first)
404 .filename() == driveId;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700405 });
406
Willy Tu03913172021-11-08 02:03:19 -0800407 if (drive == subtree.end())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700408 {
409 messages::resourceNotFound(asyncResp->res, "Drive",
410 driveId);
411 return;
412 }
413
Willy Tu03913172021-11-08 02:03:19 -0800414 const std::string& path = drive->first;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700415 const std::vector<
416 std::pair<std::string, std::vector<std::string>>>&
Willy Tu03913172021-11-08 02:03:19 -0800417 connectionNames = drive->second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700418
419 asyncResp->res.jsonValue["@odata.type"] =
420 "#Drive.v1_7_0.Drive";
421 asyncResp->res.jsonValue["@odata.id"] =
422 "/redfish/v1/Systems/system/Storage/1/Drives/" +
423 driveId;
424 asyncResp->res.jsonValue["Name"] = driveId;
425 asyncResp->res.jsonValue["Id"] = driveId;
426
427 if (connectionNames.size() != 1)
James Feiste284a7c2019-11-20 16:20:23 -0800428 {
429 BMCWEB_LOG_ERROR << "Connection size "
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700430 << connectionNames.size()
Willy Tu03913172021-11-08 02:03:19 -0800431 << ", not equal to 1";
James Feiste284a7c2019-11-20 16:20:23 -0800432 messages::internalError(asyncResp->res);
433 return;
434 }
435
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700436 getMainChassisId(
437 asyncResp,
438 [](const std::string& chassisId,
439 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
440 aRsp->res.jsonValue["Links"]["Chassis"] = {
441 {"@odata.id",
442 "/redfish/v1/Chassis/" + chassisId}};
443 });
444
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700445 // default it to Enabled
446 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
447
448 auto health = std::make_shared<HealthPopulate>(asyncResp);
James Feiste284a7c2019-11-20 16:20:23 -0800449 health->inventory.emplace_back(path);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700450 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700451
Willy Tu03913172021-11-08 02:03:19 -0800452 const std::string& connectionName =
453 connectionNames[0].first;
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700454
Willy Tu03913172021-11-08 02:03:19 -0800455 getDriveAsset(asyncResp, connectionName, path);
456 getDrivePresent(asyncResp, connectionName, path);
457 getDriveState(asyncResp, connectionName, path);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700458 },
459 "xyz.openbmc_project.ObjectMapper",
460 "/xyz/openbmc_project/object_mapper",
461 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
462 "/xyz/openbmc_project/inventory", int32_t(0),
463 std::array<const char*, 1>{
464 "xyz.openbmc_project.Inventory.Item.Drive"});
465 });
466}
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700467} // namespace redfish