xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 55c7b7a2e58779580f33046d2dd8649243776700)
1 /*
2 // Copyright (c) 2018 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 #include <boost/container/flat_map.hpp>
20 
21 namespace redfish {
22 
23 class OnDemandSoftwareInventoryProvider {
24  public:
25   template <typename CallbackFunc>
26   void get_all_software_inventory_object(CallbackFunc &&callback) {
27     crow::connections::systemBus->async_method_call(
28         [callback{std::move(callback)}](
29             const boost::system::error_code error_code,
30             const std::vector<std::pair<
31                 std::string,
32                 std::vector<std::pair<std::string, std::vector<std::string>>>>>
33                 &subtree) {
34           BMCWEB_LOG_DEBUG << "get all software inventory object callback...";
35           if (error_code) {
36             // Something wrong on DBus, the error_code is not important at this
37             // moment, just return success=false, and empty output. Since size
38             // of vector may vary depending on information from Entity Manager,
39             // and empty output could not be treated same way as error.
40             callback(false, subtree);
41             return;
42           }
43 
44           if (subtree.empty()) {
45             BMCWEB_LOG_DEBUG << "subtree empty";
46             callback(false, subtree);
47           } else {
48             BMCWEB_LOG_DEBUG << "subtree has something";
49             callback(true, subtree);
50           }
51         },
52         "xyz.openbmc_project.ObjectMapper",
53         "/xyz/openbmc_project/object_mapper",
54         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
55         "/xyz/openbmc_project/software", int32_t(1),
56         std::array<const char *, 1>{"xyz.openbmc_project.Software.Version"});
57   }
58 };
59 
60 class UpdateService : public Node {
61  public:
62   UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") {
63     Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
64     Node::json["@odata.id"] = "/redfish/v1/UpdateService";
65     Node::json["@odata.context"] =
66         "/redfish/v1/$metadata#UpdateService.UpdateService";
67     Node::json["Id"] = "UpdateService";
68     Node::json["Description"] = "Service for Software Update";
69     Node::json["Name"] = "Update Service";
70     Node::json["ServiceEnabled"] = true;  // UpdateService cannot be disabled
71     Node::json["FirmwareInventory"] = {
72         {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
73 
74     entityPrivileges = {
75         {boost::beast::http::verb::get, {{"Login"}}},
76         {boost::beast::http::verb::head, {{"Login"}}},
77         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
78         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
79         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
80         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
81   }
82 
83  private:
84   void doGet(crow::Response &res, const crow::Request &req,
85              const std::vector<std::string> &params) override {
86     res.jsonValue = Node::json;
87     res.end();
88   }
89 };
90 
91 class SoftwareInventoryCollection : public Node {
92  public:
93   /*
94    * Default Constructor
95    */
96   template <typename CrowApp>
97   SoftwareInventoryCollection(CrowApp &app)
98       : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") {
99     Node::json["@odata.type"] =
100         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
101     Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory";
102     Node::json["@odata.context"] =
103         "/redfish/v1/"
104         "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
105     Node::json["Name"] = "Software Inventory Collection";
106 
107     entityPrivileges = {
108         {boost::beast::http::verb::get, {{"Login"}}},
109         {boost::beast::http::verb::head, {{"Login"}}},
110         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
111         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
112         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
113         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
114   }
115 
116  private:
117   /**
118    * Functions triggers appropriate requests on DBus
119    */
120   void doGet(crow::Response &res, const crow::Request &req,
121              const std::vector<std::string> &params) override {
122     res.jsonValue = Node::json;
123     softwareInventoryProvider.get_all_software_inventory_object(
124         [&](const bool &success,
125             const std::vector<std::pair<
126                 std::string,
127                 std::vector<std::pair<std::string, std::vector<std::string>>>>>
128                 &subtree) {
129           if (!success) {
130             res.result(boost::beast::http::status::internal_server_error);
131             res.end();
132             return;
133           }
134 
135           if (subtree.empty()) {
136             BMCWEB_LOG_DEBUG << "subtree empty!!";
137             res.end();
138             return;
139           }
140 
141           res.jsonValue["Members"] = nlohmann::json::array();
142 
143           for (auto &obj : subtree) {
144             const std::vector<std::pair<std::string, std::vector<std::string>>>
145                 &connections = obj.second;
146 
147             for (auto &conn : connections) {
148               const std::string connectionName = conn.first;
149               BMCWEB_LOG_DEBUG << "connectionName = " << connectionName;
150               BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
151 
152               crow::connections::systemBus->async_method_call(
153                   [&](const boost::system::error_code error_code,
154                       const boost::container::flat_map<std::string, VariantType>
155                           &propertiesList) {
156                     BMCWEB_LOG_DEBUG << "safe returned in lambda function";
157                     if (error_code) {
158                       res.result(
159                           boost::beast::http::status::internal_server_error);
160                       res.end();
161                       return;
162                     }
163                     boost::container::flat_map<std::string,
164                                                VariantType>::const_iterator it =
165                         propertiesList.find("Purpose");
166                     const std::string &sw_inv_purpose =
167                         *(mapbox::getPtr<const std::string>(it->second));
168                     std::size_t last_pos = sw_inv_purpose.rfind(".");
169                     if (last_pos != std::string::npos) {
170                       res.jsonValue["Members"].push_back(
171                           {{"@odata.id",
172                             "/redfish/v1/UpdateService/FirmwareInventory/" +
173                                 sw_inv_purpose.substr(last_pos + 1)}});
174                       res.jsonValue["Members@odata.count"] =
175                           res.jsonValue["Members"].size();
176                       res.end();
177                     }
178 
179                   },
180                   connectionName, obj.first, "org.freedesktop.DBus.Properties",
181                   "GetAll", "xyz.openbmc_project.Software.Version");
182             }
183           }
184         });
185   }
186   OnDemandSoftwareInventoryProvider softwareInventoryProvider;
187 };
188 /**
189  * Chassis override class for delivering Chassis Schema
190  */
191 class SoftwareInventory : public Node {
192  public:
193   /*
194    * Default Constructor
195    */
196   template <typename CrowApp>
197   SoftwareInventory(CrowApp &app)
198       : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
199              std::string()) {
200     Node::json["@odata.type"] = "#SoftwareInventory.v1_1_0.SoftwareInventory";
201     Node::json["@odata.context"] =
202         "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
203     Node::json["Name"] = "Software Inventory";
204     Node::json["Status"] = "OK";  // TODO
205     Node::json["Updateable"] = false;
206 
207     entityPrivileges = {
208         {boost::beast::http::verb::get, {{"Login"}}},
209         {boost::beast::http::verb::head, {{"Login"}}},
210         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
211         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
212         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
213         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
214   }
215 
216  private:
217   /**
218    * Functions triggers appropriate requests on DBus
219    */
220   void doGet(crow::Response &res, const crow::Request &req,
221              const std::vector<std::string> &params) override {
222     res.jsonValue = Node::json;
223 
224     if (params.size() != 1) {
225       res.result(boost::beast::http::status::internal_server_error);
226       res.end();
227       return;
228     }
229 
230     const std::string &sw_id = params[0];
231     res.jsonValue["@odata.id"] =
232         "/redfish/v1/UpdateService/FirmwareInventory/" + sw_id;
233     softwareInventoryProvider.get_all_software_inventory_object(
234         [&, id{std::string(sw_id)} ](
235             const bool &success,
236             const std::vector<std::pair<
237                 std::string,
238                 std::vector<std::pair<std::string, std::vector<std::string>>>>>
239                 &subtree) {
240           BMCWEB_LOG_DEBUG << "doGet callback...";
241           if (!success) {
242             res.result(boost::beast::http::status::internal_server_error);
243             res.end();
244             return;
245           }
246 
247           if (subtree.empty()) {
248             BMCWEB_LOG_DEBUG << "subtree empty!!";
249             res.end();
250             return;
251           }
252 
253           for (auto &obj : subtree) {
254             const std::vector<std::pair<std::string, std::vector<std::string>>>
255                 &connections = obj.second;
256 
257             for (auto &conn : connections) {
258               const std::string connectionName = conn.first;
259               BMCWEB_LOG_DEBUG << "connectionName = " << connectionName;
260               BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
261 
262               crow::connections::systemBus->async_method_call(
263                   [&, id{std::string(id)} ](
264                       const boost::system::error_code error_code,
265                       const boost::container::flat_map<std::string, VariantType>
266                           &propertiesList) {
267                     if (error_code) {
268                       res.result(
269                           boost::beast::http::status::internal_server_error);
270                       res.end();
271                       return;
272                     }
273                     boost::container::flat_map<std::string,
274                                                VariantType>::const_iterator it =
275                         propertiesList.find("Purpose");
276                     if (it == propertiesList.end()) {
277                       BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
278                       return;
279                     }
280                     const std::string &sw_inv_purpose =
281                         *(mapbox::getPtr<const std::string>(it->second));
282                     BMCWEB_LOG_DEBUG << "sw_inv_purpose = " << sw_inv_purpose;
283                     if (boost::ends_with(sw_inv_purpose, "." + id)) {
284                       it = propertiesList.find("Version");
285                       if (it == propertiesList.end()) {
286                         BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
287                         return;
288                       }
289                       res.jsonValue["Version"] =
290                           *(mapbox::getPtr<const std::string>(it->second));
291                       res.jsonValue["Id"] = id;
292                       res.end();
293                     }
294 
295                   },
296                   connectionName, obj.first, "org.freedesktop.DBus.Properties",
297                   "GetAll", "xyz.openbmc_project.Software.Version");
298             }
299           }
300         });
301   }
302 
303   OnDemandSoftwareInventoryProvider softwareInventoryProvider;
304 };
305 
306 }  // namespace redfish
307