xref: /openbmc/bmcweb/features/redfish/lib/storage.hpp (revision 2ad9c2f694b9a75b5f14f485ebab28bd32d0f575)
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 
20 #include <node.hpp>
21 
22 namespace redfish
23 {
24 class StorageCollection : public Node
25 {
26   public:
27     StorageCollection(CrowApp &app) :
28         Node(app, "/redfish/v1/Systems/system/Storage/")
29     {
30         entityPrivileges = {
31             {boost::beast::http::verb::get, {{"Login"}}},
32             {boost::beast::http::verb::head, {{"Login"}}},
33             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
34             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
35             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
36             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
37     }
38 
39   private:
40     void doGet(crow::Response &res, const crow::Request &req,
41                const std::vector<std::string> &params) override
42     {
43         res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection";
44         res.jsonValue["@odata.context"] =
45             "/redfish/v1/$metadata#StorageCollection.StorageCollection";
46         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage";
47         res.jsonValue["Name"] = "Storage Collection";
48         res.jsonValue["Members"] = {
49             {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
50         res.jsonValue["Members@odata.count"] = 1;
51         res.end();
52     }
53 };
54 
55 class Storage : public Node
56 {
57   public:
58     Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1")
59     {
60         entityPrivileges = {
61             {boost::beast::http::verb::get, {{"Login"}}},
62             {boost::beast::http::verb::head, {{"Login"}}},
63             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
64             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
65             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
66             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
67     }
68 
69   private:
70     void doGet(crow::Response &res, const crow::Request &req,
71                const std::vector<std::string> &params) override
72     {
73         res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
74         res.jsonValue["@odata.context"] =
75             "/redfish/v1/$metadata#Storage.Storage";
76         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
77         res.jsonValue["Name"] = "Storage Controller";
78         res.jsonValue["Id"] = "1";
79         res.jsonValue["Status"]["State"] = "Enabled";
80 
81         auto asyncResp = std::make_shared<AsyncResp>(res);
82         crow::connections::systemBus->async_method_call(
83             [asyncResp](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                 asyncResp->res.jsonValue["Drives@odata.count"] = 0;
89                 auto health = std::make_shared<HealthPopulate>(asyncResp);
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 = storageList;
99                 health->populate();
100 
101                 for (const std::string &objpath : storageList)
102                 {
103                     std::size_t lastPos = objpath.rfind("/");
104                     if (lastPos == std::string::npos ||
105                         (objpath.size() <= lastPos + 1))
106                     {
107                         BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
108                         continue;
109                     }
110 
111                     storageArray.push_back(
112                         {{"@odata.id",
113                           "/redfish/v1/Systems/system/Storage/1/Drives/" +
114                               objpath.substr(lastPos + 1)}});
115                 }
116 
117                 asyncResp->res.jsonValue["Drives@odata.count"] =
118                     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 };
128 
129 class Drive : public Node
130 {
131   public:
132     Drive(CrowApp &app) :
133         Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/",
134              std::string())
135     {
136         entityPrivileges = {
137             {boost::beast::http::verb::get, {{"Login"}}},
138             {boost::beast::http::verb::head, {{"Login"}}},
139             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
140             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
141             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
142             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
143     }
144 
145   private:
146     void doGet(crow::Response &res, const crow::Request &req,
147                const std::vector<std::string> &params) override
148     {
149         const std::string &driveId = params[0];
150 
151         auto asyncResp = std::make_shared<AsyncResp>(res);
152 
153         crow::connections::systemBus->async_method_call(
154             [asyncResp, driveId](
155                 const boost::system::error_code ec,
156                 const std::vector<std::pair<
157                     std::string, std::vector<std::pair<
158                                      std::string, std::vector<std::string>>>>>
159                     &subtree) {
160                 if (ec)
161                 {
162                     BMCWEB_LOG_ERROR << "Drive mapper call error";
163                     messages::internalError(asyncResp->res);
164                     return;
165                 }
166 
167                 auto object = std::find_if(
168                     subtree.begin(), subtree.end(), [&driveId](auto &object) {
169                         const std::string &path = object.first;
170                         return boost::ends_with(path, "/" + driveId);
171                     });
172 
173                 if (object == subtree.end())
174                 {
175                     messages::resourceNotFound(asyncResp->res, "Drive",
176                                                driveId);
177                     return;
178                 }
179 
180                 const std::string &path = object->first;
181                 const std::vector<
182                     std::pair<std::string, std::vector<std::string>>>
183                     &connectionNames = object->second;
184 
185                 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
186                 asyncResp->res.jsonValue["@odata.context"] =
187                     "/redfish/v1/$metadata#Drive.Drive";
188                 asyncResp->res.jsonValue["@odata.id"] =
189                     "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
190                 asyncResp->res.jsonValue["Name"] = driveId;
191                 asyncResp->res.jsonValue["Id"] = driveId;
192 
193                 if (connectionNames.size() != 1)
194                 {
195                     BMCWEB_LOG_ERROR << "Connection size "
196                                      << connectionNames.size()
197                                      << ", greater than 1";
198                     messages::internalError(asyncResp->res);
199                     return;
200                 }
201 
202                 getMainChassisId(
203                     asyncResp, [](const std::string &chassisId,
204                                   std::shared_ptr<AsyncResp> aRsp) {
205                         aRsp->res.jsonValue["Links"]["Chassis"] = {
206                             {"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
207                     });
208 
209                 const std::string &connectionName = connectionNames[0].first;
210                 crow::connections::systemBus->async_method_call(
211                     [asyncResp](const boost::system::error_code ec,
212                                 const std::vector<std::pair<
213                                     std::string,
214                                     std::variant<bool, std::string, uint64_t>>>
215                                     &propertiesList) {
216                         if (ec)
217                         {
218                             // this interface isn't necessary
219                             return;
220                         }
221                         for (const std::pair<std::string,
222                                              std::variant<bool, std::string,
223                                                           uint64_t>> &property :
224                              propertiesList)
225                         {
226                             // Store DBus properties that are also
227                             // Redfish properties with same name and a
228                             // string value
229                             const std::string &propertyName = property.first;
230                             if ((propertyName == "PartNumber") ||
231                                 (propertyName == "SerialNumber") ||
232                                 (propertyName == "Manufacturer") ||
233                                 (propertyName == "Model"))
234                             {
235                                 const std::string *value =
236                                     std::get_if<std::string>(&property.second);
237                                 if (value == nullptr)
238                                 {
239                                     // illegal property
240                                     messages::internalError(asyncResp->res);
241                                     continue;
242                                 }
243                                 asyncResp->res.jsonValue[propertyName] = *value;
244                             }
245                         }
246                     },
247                     connectionName, path, "org.freedesktop.DBus.Properties",
248                     "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
249 
250                 // default it to Enabled
251                 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
252 
253                 auto health = std::make_shared<HealthPopulate>(asyncResp);
254                 health->inventory = std::vector<std::string>{path};
255 
256                 health->populate();
257 
258                 crow::connections::systemBus->async_method_call(
259                     [asyncResp, path](const boost::system::error_code ec,
260                                       const std::variant<bool> present) {
261                         // this interface isn't necessary, only check it if we
262                         // get a good return
263                         if (ec)
264                         {
265                             return;
266                         }
267                         const bool *enabled = std::get_if<bool>(&present);
268                         if (enabled == nullptr)
269                         {
270                             BMCWEB_LOG_DEBUG << "Illegal property present";
271                             messages::internalError(asyncResp->res);
272                             return;
273                         }
274                         if (!(*enabled))
275                         {
276                             asyncResp->res.jsonValue["Status"]["State"] =
277                                 "Disabled";
278                         }
279                     },
280                     connectionName, path, "org.freedesktop.DBus.Properties",
281                     "Get", "xyz.openbmc_project.Inventory.Item", "Present");
282 
283                 crow::connections::systemBus->async_method_call(
284                     [asyncResp](const boost::system::error_code ec,
285                                 const std::variant<bool> rebuilding) {
286                         // this interface isn't necessary, only check it if we
287                         // get a good return
288                         if (ec)
289                         {
290                             return;
291                         }
292                         const bool *updating = std::get_if<bool>(&rebuilding);
293                         if (updating == nullptr)
294                         {
295                             BMCWEB_LOG_DEBUG << "Illegal property present";
296                             messages::internalError(asyncResp->res);
297                             return;
298                         }
299 
300                         // updating and disabled in the backend shouldn't be
301                         // able to be set at the same time, so we don't need to
302                         // check for the race condition of these two calls
303                         if ((*updating))
304                         {
305                             asyncResp->res.jsonValue["Status"]["State"] =
306                                 "Updating";
307                         }
308                     },
309                     connectionName, path, "org.freedesktop.DBus.Properties",
310                     "Get", "xyz.openbmc_project.State.Drive", "Rebuilding");
311             },
312             "xyz.openbmc_project.ObjectMapper",
313             "/xyz/openbmc_project/object_mapper",
314             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
315             "/xyz/openbmc_project/inventory", int32_t(0),
316             std::array<const char *, 1>{
317                 "xyz.openbmc_project.Inventory.Item.Drive"});
318     }
319 };
320 } // namespace redfish
321