xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 262d7d4b)
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 <node.hpp>
22 
23 namespace redfish
24 {
25 class StorageCollection : public Node
26 {
27   public:
28     StorageCollection(App& app) :
29         Node(app, "/redfish/v1/Systems/system/Storage/")
30     {
31         entityPrivileges = {
32             {boost::beast::http::verb::get, {{"Login"}}},
33             {boost::beast::http::verb::head, {{"Login"}}},
34             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
35             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
36             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
37             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
38     }
39 
40   private:
41     void doGet(crow::Response& res, const crow::Request&,
42                const std::vector<std::string>&) override
43     {
44         res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection";
45         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage";
46         res.jsonValue["Name"] = "Storage Collection";
47         res.jsonValue["Members"] = {
48             {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
49         res.jsonValue["Members@odata.count"] = 1;
50         res.end();
51     }
52 };
53 
54 class Storage : public Node
55 {
56   public:
57     Storage(App& app) : Node(app, "/redfish/v1/Systems/system/Storage/1/")
58     {
59         entityPrivileges = {
60             {boost::beast::http::verb::get, {{"Login"}}},
61             {boost::beast::http::verb::head, {{"Login"}}},
62             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
63             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
64             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
65             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
66     }
67 
68   private:
69     void doGet(crow::Response& res, const crow::Request&,
70                const std::vector<std::string>&) override
71     {
72         res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
73         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
74         res.jsonValue["Name"] = "Storage";
75         res.jsonValue["Id"] = "1";
76         res.jsonValue["Status"]["State"] = "Enabled";
77 
78         auto asyncResp = std::make_shared<AsyncResp>(res);
79         auto health = std::make_shared<HealthPopulate>(asyncResp);
80         health->populate();
81 
82         crow::connections::systemBus->async_method_call(
83             [asyncResp, health](const boost::system::error_code ec,
84                                 const std::vector<std::string>& storageList) {
85                 nlohmann::json& storageArray =
86                     asyncResp->res.jsonValue["Drives"];
87                 storageArray = nlohmann::json::array();
88                 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
89                 count = 0;
90 
91                 if (ec)
92                 {
93                     BMCWEB_LOG_ERROR << "Drive mapper call error";
94                     messages::internalError(asyncResp->res);
95                     return;
96                 }
97 
98                 health->inventory.insert(health->inventory.end(),
99                                          storageList.begin(),
100                                          storageList.end());
101 
102                 for (const std::string& objpath : storageList)
103                 {
104                     std::size_t lastPos = objpath.rfind('/');
105                     if (lastPos == std::string::npos ||
106                         (objpath.size() <= lastPos + 1))
107                     {
108                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
109                         continue;
110                     }
111 
112                     storageArray.push_back(
113                         {{"@odata.id",
114                           "/redfish/v1/Systems/system/Storage/1/Drives/" +
115                               objpath.substr(lastPos + 1)}});
116                 }
117 
118                 count = storageArray.size();
119             },
120             "xyz.openbmc_project.ObjectMapper",
121             "/xyz/openbmc_project/object_mapper",
122             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
123             "/xyz/openbmc_project/inventory", int32_t(0),
124             std::array<const char*, 1>{
125                 "xyz.openbmc_project.Inventory.Item.Drive"});
126 
127         crow::connections::systemBus->async_method_call(
128             [asyncResp,
129              health](const boost::system::error_code ec,
130                      const crow::openbmc_mapper::GetSubTreeType& subtree) {
131                 if (ec || !subtree.size())
132                 {
133                     // doesn't have to be there
134                     return;
135                 }
136 
137                 nlohmann::json& root =
138                     asyncResp->res.jsonValue["StorageControllers"];
139                 root = nlohmann::json::array();
140                 for (const auto& [path, interfaceDict] : subtree)
141                 {
142                     std::size_t lastPos = path.rfind('/');
143                     if (lastPos == std::string::npos ||
144                         (path.size() <= lastPos + 1))
145                     {
146                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
147                         return;
148                     }
149 
150                     if (interfaceDict.size() != 1)
151                     {
152                         BMCWEB_LOG_ERROR << "Connection size "
153                                          << interfaceDict.size()
154                                          << ", greater than 1";
155                         messages::internalError(asyncResp->res);
156                         return;
157                     }
158 
159                     const std::string& connectionName =
160                         interfaceDict.front().first;
161 
162                     size_t index = root.size();
163                     nlohmann::json& storageController =
164                         root.emplace_back(nlohmann::json::object());
165 
166                     std::string id = path.substr(lastPos + 1);
167 
168                     storageController["@odata.type"] =
169                         "#Storage.v1_7_0.StorageController";
170                     storageController["@odata.id"] =
171                         "/redfish/v1/Systems/system/Storage/1"
172                         "#/StorageControllers/" +
173                         std::to_string(index);
174                     storageController["Name"] = id;
175                     storageController["MemberId"] = id;
176                     storageController["Status"]["State"] = "Enabled";
177 
178                     crow::connections::systemBus->async_method_call(
179                         [asyncResp, index](const boost::system::error_code ec2,
180                                            const std::variant<bool> present) {
181                             // this interface isn't necessary, only check it if
182                             // we get a good return
183                             if (ec2)
184                             {
185                                 return;
186                             }
187                             const bool* enabled = std::get_if<bool>(&present);
188                             if (enabled == nullptr)
189                             {
190                                 BMCWEB_LOG_DEBUG << "Illegal property present";
191                                 messages::internalError(asyncResp->res);
192                                 return;
193                             }
194                             if (!(*enabled))
195                             {
196                                 asyncResp->res
197                                     .jsonValue["StorageControllers"][index]
198                                               ["Status"]["State"] = "Disabled";
199                             }
200                         },
201                         connectionName, path, "org.freedesktop.DBus.Properties",
202                         "Get", "xyz.openbmc_project.Inventory.Item", "Present");
203 
204                     crow::connections::systemBus->async_method_call(
205                         [asyncResp,
206                          index](const boost::system::error_code ec2,
207                                 const std::vector<std::pair<
208                                     std::string,
209                                     std::variant<bool, std::string, uint64_t>>>&
210                                     propertiesList) {
211                             if (ec2)
212                             {
213                                 // this interface isn't necessary
214                                 return;
215                             }
216                             for (const std::pair<
217                                      std::string,
218                                      std::variant<bool, std::string, uint64_t>>&
219                                      property : propertiesList)
220                             {
221                                 // Store DBus properties that are also
222                                 // Redfish properties with same name and a
223                                 // string value
224                                 const std::string& propertyName =
225                                     property.first;
226                                 nlohmann::json& object =
227                                     asyncResp->res
228                                         .jsonValue["StorageControllers"][index];
229                                 if ((propertyName == "PartNumber") ||
230                                     (propertyName == "SerialNumber") ||
231                                     (propertyName == "Manufacturer") ||
232                                     (propertyName == "Model"))
233                                 {
234                                     const std::string* value =
235                                         std::get_if<std::string>(
236                                             &property.second);
237                                     if (value == nullptr)
238                                     {
239                                         // illegal property
240                                         messages::internalError(asyncResp->res);
241                                         continue;
242                                     }
243                                     object[propertyName] = *value;
244                                 }
245                             }
246                         },
247                         connectionName, path, "org.freedesktop.DBus.Properties",
248                         "GetAll",
249                         "xyz.openbmc_project.Inventory.Decorator.Asset");
250                 }
251 
252                 // this is done after we know the json array will no longer be
253                 // resized, as json::array uses vector underneath and we need
254                 // references to its members that won't change
255                 size_t count = 0;
256                 for (const auto& [path, interfaceDict] : subtree)
257                 {
258                     auto subHealth = std::make_shared<HealthPopulate>(
259                         asyncResp, root[count]["Status"]);
260                     subHealth->inventory.emplace_back(path);
261                     health->inventory.emplace_back(path);
262                     health->children.emplace_back(subHealth);
263                     count++;
264                 }
265             },
266             "xyz.openbmc_project.ObjectMapper",
267             "/xyz/openbmc_project/object_mapper",
268             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
269             "/xyz/openbmc_project/inventory", int32_t(0),
270             std::array<const char*, 1>{
271                 "xyz.openbmc_project.Inventory.Item.StorageController"});
272     }
273 };
274 
275 class Drive : public Node
276 {
277   public:
278     Drive(App& app) :
279         Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/",
280              std::string())
281     {
282         entityPrivileges = {
283             {boost::beast::http::verb::get, {{"Login"}}},
284             {boost::beast::http::verb::head, {{"Login"}}},
285             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
286             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
287             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
288             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
289     }
290 
291   private:
292     void doGet(crow::Response& res, const crow::Request&,
293                const std::vector<std::string>& params) override
294     {
295         auto asyncResp = std::make_shared<AsyncResp>(res);
296         if (params.size() != 1)
297         {
298             messages::internalError(asyncResp->res);
299             return;
300         }
301         const std::string& driveId = params[0];
302 
303         crow::connections::systemBus->async_method_call(
304             [asyncResp,
305              driveId](const boost::system::error_code ec,
306                       const crow::openbmc_mapper::GetSubTreeType& subtree) {
307                 if (ec)
308                 {
309                     BMCWEB_LOG_ERROR << "Drive mapper call error";
310                     messages::internalError(asyncResp->res);
311                     return;
312                 }
313 
314                 auto object2 = std::find_if(
315                     subtree.begin(), subtree.end(), [&driveId](auto& object) {
316                         const std::string& path = object.first;
317                         return boost::ends_with(path, "/" + driveId);
318                     });
319 
320                 if (object2 == subtree.end())
321                 {
322                     messages::resourceNotFound(asyncResp->res, "Drive",
323                                                driveId);
324                     return;
325                 }
326 
327                 const std::string& path = object2->first;
328                 const std::vector<
329                     std::pair<std::string, std::vector<std::string>>>&
330                     connectionNames = object2->second;
331 
332                 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
333                 asyncResp->res.jsonValue["@odata.id"] =
334                     "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
335                 asyncResp->res.jsonValue["Name"] = driveId;
336                 asyncResp->res.jsonValue["Id"] = driveId;
337 
338                 if (connectionNames.size() != 1)
339                 {
340                     BMCWEB_LOG_ERROR << "Connection size "
341                                      << connectionNames.size()
342                                      << ", greater than 1";
343                     messages::internalError(asyncResp->res);
344                     return;
345                 }
346 
347                 getMainChassisId(
348                     asyncResp, [](const std::string& chassisId,
349                                   const std::shared_ptr<AsyncResp>& aRsp) {
350                         aRsp->res.jsonValue["Links"]["Chassis"] = {
351                             {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
352                     });
353 
354                 const std::string& connectionName = connectionNames[0].first;
355                 crow::connections::systemBus->async_method_call(
356                     [asyncResp](const boost::system::error_code ec2,
357                                 const std::vector<std::pair<
358                                     std::string,
359                                     std::variant<bool, std::string, uint64_t>>>&
360                                     propertiesList) {
361                         if (ec2)
362                         {
363                             // this interface isn't necessary
364                             return;
365                         }
366                         for (const std::pair<std::string,
367                                              std::variant<bool, std::string,
368                                                           uint64_t>>& property :
369                              propertiesList)
370                         {
371                             // Store DBus properties that are also
372                             // Redfish properties with same name and a
373                             // string value
374                             const std::string& propertyName = property.first;
375                             if ((propertyName == "PartNumber") ||
376                                 (propertyName == "SerialNumber") ||
377                                 (propertyName == "Manufacturer") ||
378                                 (propertyName == "Model"))
379                             {
380                                 const std::string* value =
381                                     std::get_if<std::string>(&property.second);
382                                 if (value == nullptr)
383                                 {
384                                     // illegal property
385                                     messages::internalError(asyncResp->res);
386                                     continue;
387                                 }
388                                 asyncResp->res.jsonValue[propertyName] = *value;
389                             }
390                         }
391                     },
392                     connectionName, path, "org.freedesktop.DBus.Properties",
393                     "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
394 
395                 // default it to Enabled
396                 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
397 
398                 auto health = std::make_shared<HealthPopulate>(asyncResp);
399                 health->inventory.emplace_back(path);
400                 health->populate();
401 
402                 crow::connections::systemBus->async_method_call(
403                     [asyncResp, path](const boost::system::error_code ec2,
404                                       const std::variant<bool> present) {
405                         // this interface isn't necessary, only check it if we
406                         // get a good return
407                         if (ec2)
408                         {
409                             return;
410                         }
411                         const bool* enabled = std::get_if<bool>(&present);
412                         if (enabled == nullptr)
413                         {
414                             BMCWEB_LOG_DEBUG << "Illegal property present";
415                             messages::internalError(asyncResp->res);
416                             return;
417                         }
418                         if (!(*enabled))
419                         {
420                             asyncResp->res.jsonValue["Status"]["State"] =
421                                 "Disabled";
422                         }
423                     },
424                     connectionName, path, "org.freedesktop.DBus.Properties",
425                     "Get", "xyz.openbmc_project.Inventory.Item", "Present");
426 
427                 crow::connections::systemBus->async_method_call(
428                     [asyncResp](const boost::system::error_code ec2,
429                                 const std::variant<bool> rebuilding) {
430                         // this interface isn't necessary, only check it if we
431                         // get a good return
432                         if (ec2)
433                         {
434                             return;
435                         }
436                         const bool* updating = std::get_if<bool>(&rebuilding);
437                         if (updating == nullptr)
438                         {
439                             BMCWEB_LOG_DEBUG << "Illegal property present";
440                             messages::internalError(asyncResp->res);
441                             return;
442                         }
443 
444                         // updating and disabled in the backend shouldn't be
445                         // able to be set at the same time, so we don't need to
446                         // check for the race condition of these two calls
447                         if ((*updating))
448                         {
449                             asyncResp->res.jsonValue["Status"]["State"] =
450                                 "Updating";
451                         }
452                     },
453                     connectionName, path, "org.freedesktop.DBus.Properties",
454                     "Get", "xyz.openbmc_project.State.Drive", "Rebuilding");
455             },
456             "xyz.openbmc_project.ObjectMapper",
457             "/xyz/openbmc_project/object_mapper",
458             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
459             "/xyz/openbmc_project/inventory", int32_t(0),
460             std::array<const char*, 1>{
461                 "xyz.openbmc_project.Inventory.Item.Drive"});
462     }
463 };
464 } // namespace redfish
465