xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 735ef6d8)
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"
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 
267 inline void requestRoutesDrive(App& app)
268 {
269     BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
270         .privileges(redfish::privileges::getDrive)
271         .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)
315                     {
316                         BMCWEB_LOG_ERROR << "Connection size "
317                                          << connectionNames.size()
318                                          << ", greater than 1";
319                         messages::internalError(asyncResp->res);
320                         return;
321                     }
322 
323                     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 
332                     const std::string& connectionName =
333                         connectionNames[0].first;
334                     crow::connections::systemBus->async_method_call(
335                         [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) {
341                             if (ec2)
342                             {
343                                 // this interface isn't necessary
344                                 return;
345                             }
346                             for (const std::pair<
347                                      std::string,
348                                      std::variant<bool, std::string, uint64_t>>&
349                                      property : propertiesList)
350                             {
351                                 // Store DBus properties that are also
352                                 // Redfish properties with same name and a
353                                 // string value
354                                 const std::string& propertyName =
355                                     property.first;
356                                 if ((propertyName == "PartNumber") ||
357                                     (propertyName == "SerialNumber") ||
358                                     (propertyName == "Manufacturer") ||
359                                     (propertyName == "Model"))
360                                 {
361                                     const std::string* value =
362                                         std::get_if<std::string>(
363                                             &property.second);
364                                     if (value == nullptr)
365                                     {
366                                         // illegal property
367                                         messages::internalError(asyncResp->res);
368                                         return;
369                                     }
370                                     asyncResp->res.jsonValue[propertyName] =
371                                         *value;
372                                 }
373                             }
374                         },
375                         connectionName, path, "org.freedesktop.DBus.Properties",
376                         "GetAll",
377                         "xyz.openbmc_project.Inventory.Decorator.Asset");
378 
379                     // default it to Enabled
380                     asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
381 
382                     auto health = std::make_shared<HealthPopulate>(asyncResp);
383                     health->inventory.emplace_back(path);
384                     health->populate();
385 
386                     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)
392                             {
393                                 return;
394                             }
395                             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");
410 
411                     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                             }
428 
429                             // 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 }
450 } // namespace redfish
451