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