xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 729dae72826e58134ca4e49587427703ad0286db)
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_data(CallbackFunc &&callback) {
27     crow::connections::system_bus->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 
35           std::vector<boost::container::flat_map<std::string, std::string>>
36               output;
37 
38           if (error_code) {
39             // Something wrong on DBus, the error_code is not important at this
40             // moment, just return success=false, and empty output. Since size
41             // of vector may vary depending on information from Entity Manager,
42             // and empty output could not be treated same way as error.
43             callback(false, output);
44             return;
45           }
46 
47           for (auto &obj : subtree) {
48             const std::vector<std::pair<std::string, std::vector<std::string>>>
49                 &connectionNames = obj.second;
50 
51             const std::string connectionName = connectionNames[0].first;
52 
53             crow::connections::system_bus->async_method_call(
54                 [&](const boost::system::error_code error_code,
55                     const std::vector<std::pair<std::string, VariantType>>
56                         &propertiesList) {
57                   for (auto &property : propertiesList) {
58                     boost::container::flat_map<std::string, std::string>
59                         single_sw_item_properties;
60                     single_sw_item_properties[property.first] =
61                         *(mapbox::get_ptr<const std::string>(property.second));
62                     output.emplace_back(single_sw_item_properties);
63                   }
64                 },
65                 connectionName, obj.first, "org.freedesktop.DBus.Properties",
66                 "GetAll", "xyz.openbmc_project.Software.Version");
67             // Finally make a callback with usefull data
68             callback(true, output);
69           }
70         },
71         "xyz.openbmc_project.ObjectMapper",
72         "/xyz/openbmc_project/object_mapper",
73         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
74         "/xyz/openbmc_project/software", int32_t(0),
75         std::array<const char *, 1>{"xyz.openbmc_project.Software.Version"});
76   }
77   /*
78    * Function that retrieves all SoftwareInventory available through
79    * Software.BMC.Updater.
80    * @param callback a function that shall be called to convert Dbus output into
81    * JSON.
82    */
83   template <typename CallbackFunc>
84   void get_software_inventory_list(CallbackFunc &&callback) {
85     get_all_software_inventory_data(
86         [callback](
87             const bool &success,
88             const std::vector<
89                 boost::container::flat_map<std::string, std::string>> &output) {
90           std::vector<std::string> sw_inv_list;
91           for (auto &i : output) {
92             boost::container::flat_map<std::string, std::string>::const_iterator
93                 p = i.find("Purpose");
94             if ((p != i.end())) {
95               const std::string &sw_inv_purpose =
96                   boost::get<std::string>(p->second);
97               std::size_t last_pos = sw_inv_purpose.rfind(".");
98               if (last_pos != std::string::npos) {
99                 // and put it into output vector.
100                 sw_inv_list.emplace_back(sw_inv_purpose.substr(last_pos + 1));
101               }
102             }
103           }
104           callback(true, sw_inv_list);
105         });
106   };
107 
108   template <typename CallbackFunc>
109   void get_software_inventory_data(const std::string &res_name,
110                                    CallbackFunc &&callback) {
111     get_all_software_inventory_data(
112         [res_name, callback](
113             const bool &success,
114             const std::vector<
115                 boost::container::flat_map<std::string, std::string>> &output) {
116           for (auto &i : output) {
117             boost::container::flat_map<std::string, std::string>::const_iterator
118                 p = i.find("Purpose");
119             // Find the one with Purpose matching res_name
120             if ((p != i.end()) &&
121                 boost::ends_with(boost::get<std::string>(p->second),
122                                  "." + res_name)) {
123               callback(true, i);
124             }
125           }
126         });
127   }
128 };
129 
130 class UpdateService : public Node {
131  public:
132   UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") {
133     Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
134     Node::json["@odata.id"] = "/redfish/v1/UpdateService";
135     Node::json["@odata.context"] =
136         "/redfish/v1/$metadata#UpdateService.UpdateService";
137     Node::json["Id"] = "UpdateService";
138     Node::json["Description"] = "Service for Software Update";
139     Node::json["Name"] = "Update Service";
140     Node::json["ServiceEnabled"] = true;  // UpdateService cannot be disabled
141     Node::json["SoftwareInventory"] = {
142         {"@odata.id", "/redfish/v1/UpdateService/SoftwareInventory"}};
143 
144     entityPrivileges = {
145         {boost::beast::http::verb::get, {{"Login"}}},
146         {boost::beast::http::verb::head, {{"Login"}}},
147         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
148         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
149         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
150         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
151   }
152 
153  private:
154   void doGet(crow::response &res, const crow::request &req,
155              const std::vector<std::string> &params) override {
156     res.json_value = Node::json;
157     res.end();
158   }
159 };
160 
161 class SoftwareInventoryCollection : public Node {
162  public:
163   /*
164    * Default Constructor
165    */
166   template <typename CrowApp>
167   SoftwareInventoryCollection(CrowApp &app)
168       : Node(app, "/redfish/v1/UpdateService/SoftwareInventory/") {
169     Node::json["@odata.type"] =
170         "#SoftwareInventoryCollection.SoftwareInventoryCollection";
171     Node::json["@odata.id"] = "/redfish/v1/UpdateService/SoftwareInventory";
172     Node::json["@odata.context"] =
173         "/redfish/v1/"
174         "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
175     Node::json["Name"] = "Software Inventory Collection";
176 
177     entityPrivileges = {
178         {boost::beast::http::verb::get, {{"Login"}}},
179         {boost::beast::http::verb::head, {{"Login"}}},
180         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
181         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
182         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
183         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
184   }
185 
186  private:
187   /**
188    * Functions triggers appropriate requests on DBus
189    */
190   void doGet(crow::response &res, const crow::request &req,
191              const std::vector<std::string> &params) override {
192     res.json_value = Node::json;
193     // Get sw inventory list, and call the below callback for JSON preparation
194     software_inventory_provider.get_software_inventory_list(
195         [&](const bool &success, const std::vector<std::string> &output) {
196 
197           if (success) {
198             // ... prepare json array with appropriate @odata.id links
199             nlohmann::json sw_inventory_array = nlohmann::json::array();
200             for (const std::string &sw_item : output) {
201               sw_inventory_array.push_back(
202                   {{"@odata.id",
203                     "/redfish/v1/UpdateService/SoftwareInventory/" + sw_item}});
204             }
205             // Then attach members, count size and return
206 
207             Node::json["Members"] = sw_inventory_array;
208             Node::json["Members@odata.count"] = sw_inventory_array.size();
209             res.json_value = Node::json;
210           } else {
211             // ... otherwise, return INTERNALL ERROR
212             res.result(boost::beast::http::status::internal_server_error);
213           }
214           res.end();
215         });
216     res.end();
217   }
218   OnDemandSoftwareInventoryProvider software_inventory_provider;
219 };
220 /**
221  * Chassis override class for delivering Chassis Schema
222  */
223 class SoftwareInventory : public Node {
224  public:
225   /*
226    * Default Constructor
227    */
228   template <typename CrowApp>
229   SoftwareInventory(CrowApp &app)
230       : Node(app, "/redfish/v1/UpdateService/SoftwareInventory/<str>/",
231              std::string()) {
232     Node::json["@odata.type"] = "#SoftwareInventory.v1_1_0.SoftwareInventory";
233     Node::json["@odata.id"] = "/redfish/v1/UpdateService/SoftwareInventory";
234     Node::json["@odata.context"] =
235         "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
236     Node::json["Name"] = "Software Inventory";
237     Node::json["Status"] = "OK";  // TODO
238     Node::json["Updateable"] = "No";
239 
240     entityPrivileges = {
241         {boost::beast::http::verb::get, {{"Login"}}},
242         {boost::beast::http::verb::head, {{"Login"}}},
243         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
244         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
245         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
246         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
247   }
248 
249  private:
250   /**
251    * Functions triggers appropriate requests on DBus
252    */
253   void doGet(crow::response &res, const crow::request &req,
254              const std::vector<std::string> &params) override {
255     if (params.size() != 1) {
256       res.result(boost::beast::http::status::internal_server_error);
257       res.end();
258       return;
259     }
260     const std::string &sw_id = params[0];
261     software_inventory_provider.get_software_inventory_data(
262         sw_id, [&, id{std::string(sw_id)} ](
263                    const bool &success,
264                    const boost::container::flat_map<std::string, std::string>
265                        &output) {
266           res.json_value = Node::json;
267           // If success...
268           if (success) {
269             // prepare all the schema required fields.
270             res.json_value["@odata.id"] =
271                 "/redfish/v1/UpdateService/SoftwareInventory/" + id;
272             // also the one from dbus
273             boost::container::flat_map<std::string, std::string>::const_iterator
274                 it = output.find("Version");
275             res.json_value["Version"] = boost::get<std::string>(it->second);
276 
277             res.json_value["Id"] = id;
278             // prepare respond, and send
279           } else {
280             res.result(boost::beast::http::status::not_found);
281           }
282           res.end();
283         });
284   }
285 
286   OnDemandSoftwareInventoryProvider software_inventory_provider;
287 };
288 
289 }  // namespace redfish
290