xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 60c922df)
1 /*
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 
18 #include "health.hpp"
19 #include "openbmc_dbus_rest.hpp"
20 
21 #include <app.hpp>
22 #include <registries/privilege_registry.hpp>
23 
24 namespace redfish
25 {
26 inline void requestRoutesStorageCollection(App& app)
27 {
28     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/")
29         .privileges(redfish::privileges::getStorageCollection)
30         .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 }
43 
44 inline void requestRoutesStorage(App& app)
45 {
46     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
47         .privileges(redfish::privileges::getStorage)
48         .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";
58 
59             auto health = std::make_shared<HealthPopulate>(asyncResp);
60             health->populate();
61 
62             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;
72 
73                     if (ec)
74                     {
75                         BMCWEB_LOG_ERROR << "Drive mapper call error";
76                         messages::internalError(asyncResp->res);
77                         return;
78                     }
79 
80                     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#/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 
266 inline 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 
310 inline 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 
340 inline 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 
375 inline void requestRoutesDrive(App& app)
376 {
377     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
378         .privileges(redfish::privileges::getDrive)
379         .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 
395                     auto drive = std::find_if(
396                         subtree.begin(), subtree.end(),
397                         [&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;
404                         });
405 
406                     if (drive == subtree.end())
407                     {
408                         messages::resourceNotFound(asyncResp->res, "Drive",
409                                                    driveId);
410                         return;
411                     }
412 
413                     const std::string& path = drive->first;
414                     const std::vector<
415                         std::pair<std::string, std::vector<std::string>>>&
416                         connectionNames = drive->second;
417 
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)
427                     {
428                         BMCWEB_LOG_ERROR << "Connection size "
429                                          << connectionNames.size()
430                                          << ", not equal to 1";
431                         messages::internalError(asyncResp->res);
432                         return;
433                     }
434 
435                     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 
444                     // default it to Enabled
445                     asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
446 
447                     auto health = std::make_shared<HealthPopulate>(asyncResp);
448                     health->inventory.emplace_back(path);
449                     health->populate();
450 
451                     const std::string& connectionName =
452                         connectionNames[0].first;
453 
454                     getDriveAsset(asyncResp, connectionName, path);
455                     getDrivePresent(asyncResp, connectionName, path);
456                     getDriveState(asyncResp, connectionName, path);
457                 },
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 }
466 } // namespace redfish
467