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