xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 90e97e1d26b78d899a543831a8051dacbbdde71a)
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 
23 namespace redfish
24 {
25 inline void requestRoutesStorageCollection(App& app)
26 {
27     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 }
42 
43 inline void requestRoutesStorage(App& app)
44 {
45     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";
57 
58             auto health = std::make_shared<HealthPopulate>(asyncResp);
59             health->populate();
60 
61             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;
71 
72                     if (ec)
73                     {
74                         BMCWEB_LOG_ERROR << "Drive mapper call error";
75                         messages::internalError(asyncResp->res);
76                         return;
77                     }
78 
79                     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 
266 inline 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)
314                     {
315                         BMCWEB_LOG_ERROR << "Connection size "
316                                          << connectionNames.size()
317                                          << ", greater than 1";
318                         messages::internalError(asyncResp->res);
319                         return;
320                     }
321 
322                     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 
331                     const std::string& connectionName =
332                         connectionNames[0].first;
333                     crow::connections::systemBus->async_method_call(
334                         [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) {
340                             if (ec2)
341                             {
342                                 // this interface isn't necessary
343                                 return;
344                             }
345                             for (const std::pair<
346                                      std::string,
347                                      std::variant<bool, std::string, uint64_t>>&
348                                      property : propertiesList)
349                             {
350                                 // Store DBus properties that are also
351                                 // Redfish properties with same name and a
352                                 // string value
353                                 const std::string& propertyName =
354                                     property.first;
355                                 if ((propertyName == "PartNumber") ||
356                                     (propertyName == "SerialNumber") ||
357                                     (propertyName == "Manufacturer") ||
358                                     (propertyName == "Model"))
359                                 {
360                                     const std::string* value =
361                                         std::get_if<std::string>(
362                                             &property.second);
363                                     if (value == nullptr)
364                                     {
365                                         // illegal property
366                                         messages::internalError(asyncResp->res);
367                                         return;
368                                     }
369                                     asyncResp->res.jsonValue[propertyName] =
370                                         *value;
371                                 }
372                             }
373                         },
374                         connectionName, path, "org.freedesktop.DBus.Properties",
375                         "GetAll",
376                         "xyz.openbmc_project.Inventory.Decorator.Asset");
377 
378                     // default it to Enabled
379                     asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
380 
381                     auto health = std::make_shared<HealthPopulate>(asyncResp);
382                     health->inventory.emplace_back(path);
383                     health->populate();
384 
385                     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)
391                             {
392                                 return;
393                             }
394                             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");
409 
410                     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                             }
427 
428                             // 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 }
449 } // namespace redfish
450