xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 0e7de46f9b6365bad4e79a3933112750c5bf7853)
1729dae72SJennifer Lee /*
2729dae72SJennifer Lee // Copyright (c) 2018 Intel Corporation
3729dae72SJennifer Lee //
4729dae72SJennifer Lee // Licensed under the Apache License, Version 2.0 (the "License");
5729dae72SJennifer Lee // you may not use this file except in compliance with the License.
6729dae72SJennifer Lee // You may obtain a copy of the License at
7729dae72SJennifer Lee //
8729dae72SJennifer Lee //      http://www.apache.org/licenses/LICENSE-2.0
9729dae72SJennifer Lee //
10729dae72SJennifer Lee // Unless required by applicable law or agreed to in writing, software
11729dae72SJennifer Lee // distributed under the License is distributed on an "AS IS" BASIS,
12729dae72SJennifer Lee // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13729dae72SJennifer Lee // See the License for the specific language governing permissions and
14729dae72SJennifer Lee // limitations under the License.
15729dae72SJennifer Lee */
16729dae72SJennifer Lee #pragma once
17729dae72SJennifer Lee 
18729dae72SJennifer Lee #include "node.hpp"
191abe55efSEd Tanous 
20729dae72SJennifer Lee #include <boost/container/flat_map.hpp>
2187d84729SAndrew Geissler #include <utils/fw_utils.hpp>
22abf2add6SEd Tanous #include <variant>
23729dae72SJennifer Lee 
241abe55efSEd Tanous namespace redfish
251abe55efSEd Tanous {
2627826b5fSEd Tanous 
27*0e7de46fSAndrew Geissler // Match signals added on software path
28acb7cfb4SJennifer Lee static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
29*0e7de46fSAndrew Geissler // Only allow one update at a time
30*0e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
31729dae72SJennifer Lee 
321abe55efSEd Tanous class UpdateService : public Node
331abe55efSEd Tanous {
34729dae72SJennifer Lee   public:
351abe55efSEd Tanous     UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
361abe55efSEd Tanous     {
37729dae72SJennifer Lee         entityPrivileges = {
38729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
39729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
40729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
41729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
42729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
43729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
44729dae72SJennifer Lee     }
45729dae72SJennifer Lee 
46729dae72SJennifer Lee   private:
4755c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
481abe55efSEd Tanous                const std::vector<std::string> &params) override
491abe55efSEd Tanous     {
500f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
510f74e643SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
520f74e643SEd Tanous         res.jsonValue["@odata.context"] =
530f74e643SEd Tanous             "/redfish/v1/$metadata#UpdateService.UpdateService";
540f74e643SEd Tanous         res.jsonValue["Id"] = "UpdateService";
550f74e643SEd Tanous         res.jsonValue["Description"] = "Service for Software Update";
560f74e643SEd Tanous         res.jsonValue["Name"] = "Update Service";
570f74e643SEd Tanous         res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
580f74e643SEd Tanous         // UpdateService cannot be disabled
590f74e643SEd Tanous         res.jsonValue["ServiceEnabled"] = true;
600f74e643SEd Tanous         res.jsonValue["FirmwareInventory"] = {
610f74e643SEd Tanous             {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
62729dae72SJennifer Lee         res.end();
63729dae72SJennifer Lee     }
64*0e7de46fSAndrew Geissler     static void cleanUp()
651abe55efSEd Tanous     {
66*0e7de46fSAndrew Geissler         fwUpdateInProgress = false;
67*0e7de46fSAndrew Geissler         fwUpdateMatcher = nullptr;
68*0e7de46fSAndrew Geissler     }
69*0e7de46fSAndrew Geissler     static void activateImage(const std::string &objPath,
70*0e7de46fSAndrew Geissler                               const std::string &service)
71*0e7de46fSAndrew Geissler     {
72*0e7de46fSAndrew Geissler         BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
73acb7cfb4SJennifer Lee         crow::connections::systemBus->async_method_call(
74*0e7de46fSAndrew Geissler             [](const boost::system::error_code error_code) {
751abe55efSEd Tanous                 if (error_code)
761abe55efSEd Tanous                 {
77acb7cfb4SJennifer Lee                     BMCWEB_LOG_DEBUG << "error_code = " << error_code;
78acb7cfb4SJennifer Lee                     BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
79acb7cfb4SJennifer Lee                 }
80acb7cfb4SJennifer Lee             },
81*0e7de46fSAndrew Geissler             service, objPath, "org.freedesktop.DBus.Properties", "Set",
82acb7cfb4SJennifer Lee             "xyz.openbmc_project.Software.Activation", "RequestedActivation",
83abf2add6SEd Tanous             std::variant<std::string>(
84acb7cfb4SJennifer Lee                 "xyz.openbmc_project.Software.Activation.RequestedActivations."
85acb7cfb4SJennifer Lee                 "Active"));
86acb7cfb4SJennifer Lee     }
87*0e7de46fSAndrew Geissler     static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
88*0e7de46fSAndrew Geissler                                        boost::asio::deadline_timer &timeout,
89*0e7de46fSAndrew Geissler                                        sdbusplus::message::message &m)
90*0e7de46fSAndrew Geissler     {
91*0e7de46fSAndrew Geissler         std::vector<std::pair<
92*0e7de46fSAndrew Geissler             std::string,
93*0e7de46fSAndrew Geissler             std::vector<std::pair<std::string, std::variant<std::string>>>>>
94*0e7de46fSAndrew Geissler             interfacesProperties;
95*0e7de46fSAndrew Geissler 
96*0e7de46fSAndrew Geissler         sdbusplus::message::object_path objPath;
97*0e7de46fSAndrew Geissler 
98*0e7de46fSAndrew Geissler         m.read(objPath, interfacesProperties);
99*0e7de46fSAndrew Geissler 
100*0e7de46fSAndrew Geissler         BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
101*0e7de46fSAndrew Geissler         for (auto &interface : interfacesProperties)
102*0e7de46fSAndrew Geissler         {
103*0e7de46fSAndrew Geissler             BMCWEB_LOG_DEBUG << "interface = " << interface.first;
104*0e7de46fSAndrew Geissler 
105*0e7de46fSAndrew Geissler             if (interface.first == "xyz.openbmc_project.Software.Activation")
106*0e7de46fSAndrew Geissler             {
107*0e7de46fSAndrew Geissler                 // Found our interface, disable callbacks
108*0e7de46fSAndrew Geissler                 fwUpdateMatcher = nullptr;
109*0e7de46fSAndrew Geissler 
110*0e7de46fSAndrew Geissler                 // Retrieve service and activate
111*0e7de46fSAndrew Geissler                 crow::connections::systemBus->async_method_call(
112*0e7de46fSAndrew Geissler                     [objPath, asyncResp, &timeout](
113*0e7de46fSAndrew Geissler                         const boost::system::error_code error_code,
114*0e7de46fSAndrew Geissler                         const std::vector<std::pair<
115*0e7de46fSAndrew Geissler                             std::string, std::vector<std::string>>> &objInfo) {
116*0e7de46fSAndrew Geissler                         if (error_code)
117*0e7de46fSAndrew Geissler                         {
118*0e7de46fSAndrew Geissler                             BMCWEB_LOG_DEBUG << "error_code = " << error_code;
119*0e7de46fSAndrew Geissler                             BMCWEB_LOG_DEBUG << "error msg = "
120*0e7de46fSAndrew Geissler                                              << error_code.message();
121*0e7de46fSAndrew Geissler                             messages::internalError(asyncResp->res);
122*0e7de46fSAndrew Geissler                             cleanUp();
123*0e7de46fSAndrew Geissler                             return;
124*0e7de46fSAndrew Geissler                         }
125*0e7de46fSAndrew Geissler                         // Ensure we only got one service back
126*0e7de46fSAndrew Geissler                         if (objInfo.size() != 1)
127*0e7de46fSAndrew Geissler                         {
128*0e7de46fSAndrew Geissler                             BMCWEB_LOG_ERROR << "Invalid Object Size "
129*0e7de46fSAndrew Geissler                                              << objInfo.size();
130*0e7de46fSAndrew Geissler                             messages::internalError(asyncResp->res);
131*0e7de46fSAndrew Geissler                             cleanUp();
132*0e7de46fSAndrew Geissler                             return;
133*0e7de46fSAndrew Geissler                         }
134*0e7de46fSAndrew Geissler                         // cancel timer only when
135*0e7de46fSAndrew Geissler                         // xyz.openbmc_project.Software.Activation interface
136*0e7de46fSAndrew Geissler                         // is added
137*0e7de46fSAndrew Geissler                         boost::system::error_code ec;
138*0e7de46fSAndrew Geissler                         timeout.cancel(ec);
139*0e7de46fSAndrew Geissler                         if (ec)
140*0e7de46fSAndrew Geissler                         {
141*0e7de46fSAndrew Geissler                             BMCWEB_LOG_ERROR << "error canceling timer " << ec;
142*0e7de46fSAndrew Geissler                         }
143*0e7de46fSAndrew Geissler                         UpdateService::activateImage(objPath.str,
144*0e7de46fSAndrew Geissler                                                      objInfo[0].first);
145*0e7de46fSAndrew Geissler                         redfish::messages::success(asyncResp->res);
146*0e7de46fSAndrew Geissler                         fwUpdateInProgress = false;
147*0e7de46fSAndrew Geissler                     },
148*0e7de46fSAndrew Geissler                     "xyz.openbmc_project.ObjectMapper",
149*0e7de46fSAndrew Geissler                     "/xyz/openbmc_project/object_mapper",
150*0e7de46fSAndrew Geissler                     "xyz.openbmc_project.ObjectMapper", "GetObject",
151*0e7de46fSAndrew Geissler                     objPath.str,
152*0e7de46fSAndrew Geissler                     std::array<const char *, 1>{
153*0e7de46fSAndrew Geissler                         "xyz.openbmc_project.Software.Activation"});
154*0e7de46fSAndrew Geissler             }
155*0e7de46fSAndrew Geissler         }
156*0e7de46fSAndrew Geissler     }
157acb7cfb4SJennifer Lee     void doPost(crow::Response &res, const crow::Request &req,
1581abe55efSEd Tanous                 const std::vector<std::string> &params) override
1591abe55efSEd Tanous     {
160acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "doPost...";
161acb7cfb4SJennifer Lee 
162acb7cfb4SJennifer Lee         // Only allow one FW update at a time
163*0e7de46fSAndrew Geissler         if (fwUpdateInProgress != false)
1641abe55efSEd Tanous         {
165acb7cfb4SJennifer Lee             res.addHeader("Retry-After", "30");
166*0e7de46fSAndrew Geissler             messages::serviceTemporarilyUnavailable(res, "30");
167acb7cfb4SJennifer Lee             res.end();
168acb7cfb4SJennifer Lee             return;
169acb7cfb4SJennifer Lee         }
170*0e7de46fSAndrew Geissler 
171acb7cfb4SJennifer Lee         // Make this const static so it survives outside this method
1721abe55efSEd Tanous         static boost::asio::deadline_timer timeout(
1731abe55efSEd Tanous             *req.ioService, boost::posix_time::seconds(5));
174acb7cfb4SJennifer Lee 
175acb7cfb4SJennifer Lee         timeout.expires_from_now(boost::posix_time::seconds(5));
176acb7cfb4SJennifer Lee 
177acb7cfb4SJennifer Lee         timeout.async_wait([&res](const boost::system::error_code &ec) {
178*0e7de46fSAndrew Geissler             cleanUp();
1791abe55efSEd Tanous             if (ec == boost::asio::error::operation_aborted)
1801abe55efSEd Tanous             {
181acb7cfb4SJennifer Lee                 // expected, we were canceled before the timer completed.
182acb7cfb4SJennifer Lee                 return;
183acb7cfb4SJennifer Lee             }
1841abe55efSEd Tanous             BMCWEB_LOG_ERROR
1851abe55efSEd Tanous                 << "Timed out waiting for firmware object being created";
1861abe55efSEd Tanous             BMCWEB_LOG_ERROR
1871abe55efSEd Tanous                 << "FW image may has already been uploaded to server";
1881abe55efSEd Tanous             if (ec)
1891abe55efSEd Tanous             {
190acb7cfb4SJennifer Lee                 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
191acb7cfb4SJennifer Lee                 return;
192acb7cfb4SJennifer Lee             }
193acb7cfb4SJennifer Lee 
194f12894f8SJason M. Bills             redfish::messages::internalError(res);
195acb7cfb4SJennifer Lee             res.end();
196acb7cfb4SJennifer Lee         });
197acb7cfb4SJennifer Lee 
198*0e7de46fSAndrew Geissler         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
199*0e7de46fSAndrew Geissler         auto callback = [asyncResp](sdbusplus::message::message &m) {
200acb7cfb4SJennifer Lee             BMCWEB_LOG_DEBUG << "Match fired";
201*0e7de46fSAndrew Geissler             softwareInterfaceAdded(asyncResp, timeout, m);
202acb7cfb4SJennifer Lee         };
203acb7cfb4SJennifer Lee 
204*0e7de46fSAndrew Geissler         fwUpdateInProgress = true;
205*0e7de46fSAndrew Geissler 
206acb7cfb4SJennifer Lee         fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
207acb7cfb4SJennifer Lee             *crow::connections::systemBus,
208acb7cfb4SJennifer Lee             "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
209acb7cfb4SJennifer Lee             "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
210acb7cfb4SJennifer Lee             callback);
211acb7cfb4SJennifer Lee 
212acb7cfb4SJennifer Lee         std::string filepath(
213acb7cfb4SJennifer Lee             "/tmp/images/" +
214acb7cfb4SJennifer Lee             boost::uuids::to_string(boost::uuids::random_generator()()));
215acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
216acb7cfb4SJennifer Lee         std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
217acb7cfb4SJennifer Lee                                         std::ofstream::trunc);
218acb7cfb4SJennifer Lee         out << req.body;
219acb7cfb4SJennifer Lee         out.close();
220acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "file upload complete!!";
221acb7cfb4SJennifer Lee     }
222729dae72SJennifer Lee };
223729dae72SJennifer Lee 
2241abe55efSEd Tanous class SoftwareInventoryCollection : public Node
2251abe55efSEd Tanous {
226729dae72SJennifer Lee   public:
227729dae72SJennifer Lee     template <typename CrowApp>
2281abe55efSEd Tanous     SoftwareInventoryCollection(CrowApp &app) :
2291abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
2301abe55efSEd Tanous     {
231729dae72SJennifer Lee         entityPrivileges = {
232729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
233729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
234729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
235729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
236729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
237729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
238729dae72SJennifer Lee     }
239729dae72SJennifer Lee 
240729dae72SJennifer Lee   private:
24155c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
2421abe55efSEd Tanous                const std::vector<std::string> &params) override
2431abe55efSEd Tanous     {
244c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2450f74e643SEd Tanous         res.jsonValue["@odata.type"] =
2460f74e643SEd Tanous             "#SoftwareInventoryCollection.SoftwareInventoryCollection";
2470f74e643SEd Tanous         res.jsonValue["@odata.id"] =
2480f74e643SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
2490f74e643SEd Tanous         res.jsonValue["@odata.context"] =
2500f74e643SEd Tanous             "/redfish/v1/"
2510f74e643SEd Tanous             "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
2520f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory Collection";
253c711bf86SEd Tanous 
254c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
255c711bf86SEd Tanous             [asyncResp](
256c711bf86SEd Tanous                 const boost::system::error_code ec,
2576c4eb9deSJennifer Lee                 const std::vector<std::pair<
2581abe55efSEd Tanous                     std::string, std::vector<std::pair<
2591abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
2606c4eb9deSJennifer Lee                     &subtree) {
2611abe55efSEd Tanous                 if (ec)
2621abe55efSEd Tanous                 {
263f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
2646c4eb9deSJennifer Lee                     return;
265729dae72SJennifer Lee                 }
266c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
267c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2686c4eb9deSJennifer Lee 
2691abe55efSEd Tanous                 for (auto &obj : subtree)
2701abe55efSEd Tanous                 {
2711abe55efSEd Tanous                     const std::vector<
2721abe55efSEd Tanous                         std::pair<std::string, std::vector<std::string>>>
2736c4eb9deSJennifer Lee                         &connections = obj.second;
2746c4eb9deSJennifer Lee 
275f4b65ab1SJennifer Lee                     // if can't parse fw id then return
27627826b5fSEd Tanous                     std::size_t idPos;
27727826b5fSEd Tanous                     if ((idPos = obj.first.rfind("/")) == std::string::npos)
278f4b65ab1SJennifer Lee                     {
279f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
280f4b65ab1SJennifer Lee                         BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
281f4b65ab1SJennifer Lee                         return;
282f4b65ab1SJennifer Lee                     }
283f4b65ab1SJennifer Lee                     std::string swId = obj.first.substr(idPos + 1);
284f4b65ab1SJennifer Lee 
2851abe55efSEd Tanous                     for (auto &conn : connections)
2861abe55efSEd Tanous                     {
287c711bf86SEd Tanous                         const std::string &connectionName = conn.first;
2881abe55efSEd Tanous                         BMCWEB_LOG_DEBUG << "connectionName = "
2891abe55efSEd Tanous                                          << connectionName;
29055c7b7a2SEd Tanous                         BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
2916c4eb9deSJennifer Lee 
29255c7b7a2SEd Tanous                         crow::connections::systemBus->async_method_call(
293f4b65ab1SJennifer Lee                             [asyncResp,
294f4b65ab1SJennifer Lee                              swId](const boost::system::error_code error_code,
295c711bf86SEd Tanous                                    const VariantType &activation) {
2961abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
2971abe55efSEd Tanous                                     << "safe returned in lambda function";
2981abe55efSEd Tanous                                 if (error_code)
2991abe55efSEd Tanous                                 {
300f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
3016c4eb9deSJennifer Lee                                     return;
3026c4eb9deSJennifer Lee                                 }
303c711bf86SEd Tanous 
304f4b65ab1SJennifer Lee                                 const std::string *swActivationStatus =
305abf2add6SEd Tanous                                     std::get_if<std::string>(&activation);
306f4b65ab1SJennifer Lee                                 if (swActivationStatus == nullptr)
3071abe55efSEd Tanous                                 {
308f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
309acb7cfb4SJennifer Lee                                     return;
310acb7cfb4SJennifer Lee                                 }
311f4b65ab1SJennifer Lee                                 if (swActivationStatus != nullptr &&
312f4b65ab1SJennifer Lee                                     *swActivationStatus !=
313f4b65ab1SJennifer Lee                                         "xyz.openbmc_project.Software."
314f4b65ab1SJennifer Lee                                         "Activation."
315f4b65ab1SJennifer Lee                                         "Activations.Active")
3161abe55efSEd Tanous                                 {
317f4b65ab1SJennifer Lee                                     // The activation status of this software is
318f4b65ab1SJennifer Lee                                     // not currently active, so does not need to
319f4b65ab1SJennifer Lee                                     // be listed in the response
320c711bf86SEd Tanous                                     return;
321c711bf86SEd Tanous                                 }
322c711bf86SEd Tanous                                 nlohmann::json &members =
323c711bf86SEd Tanous                                     asyncResp->res.jsonValue["Members"];
324c711bf86SEd Tanous                                 members.push_back(
325f4b65ab1SJennifer Lee                                     {{"@odata.id", "/redfish/v1/UpdateService/"
3261abe55efSEd Tanous                                                    "FirmwareInventory/" +
327f4b65ab1SJennifer Lee                                                        swId}});
3281abe55efSEd Tanous                                 asyncResp->res
3291abe55efSEd Tanous                                     .jsonValue["Members@odata.count"] =
330c711bf86SEd Tanous                                     members.size();
3316c4eb9deSJennifer Lee                             },
3321abe55efSEd Tanous                             connectionName, obj.first,
3331abe55efSEd Tanous                             "org.freedesktop.DBus.Properties", "Get",
3341abe55efSEd Tanous                             "xyz.openbmc_project.Software.Activation",
335acb7cfb4SJennifer Lee                             "Activation");
3366c4eb9deSJennifer Lee                     }
3376c4eb9deSJennifer Lee                 }
338c711bf86SEd Tanous             },
339c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
340c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
341c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
342c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
3431abe55efSEd Tanous             std::array<const char *, 1>{
3441abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
345729dae72SJennifer Lee     }
346729dae72SJennifer Lee };
347c711bf86SEd Tanous 
3481abe55efSEd Tanous class SoftwareInventory : public Node
3491abe55efSEd Tanous {
350729dae72SJennifer Lee   public:
351729dae72SJennifer Lee     template <typename CrowApp>
3521abe55efSEd Tanous     SoftwareInventory(CrowApp &app) :
3531abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
3541abe55efSEd Tanous              std::string())
3551abe55efSEd Tanous     {
356729dae72SJennifer Lee         entityPrivileges = {
357729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
358729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
359729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
360729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
361729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
362729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
363729dae72SJennifer Lee     }
364729dae72SJennifer Lee 
365729dae72SJennifer Lee   private:
36687d84729SAndrew Geissler     /* Fill related item links (i.e. bmc, bios) in for inventory */
36787d84729SAndrew Geissler     static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
36887d84729SAndrew Geissler                                 const std::string &purpose)
36987d84729SAndrew Geissler     {
37087d84729SAndrew Geissler         if (purpose == fw_util::bmcPurpose)
37187d84729SAndrew Geissler         {
37287d84729SAndrew Geissler             nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
37387d84729SAndrew Geissler             members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
37487d84729SAndrew Geissler             aResp->res.jsonValue["Members@odata.count"] = members.size();
37587d84729SAndrew Geissler         }
37687d84729SAndrew Geissler         else if (purpose == fw_util::biosPurpose)
37787d84729SAndrew Geissler         {
37887d84729SAndrew Geissler             // TODO(geissonator) Need BIOS schema support added for this
37987d84729SAndrew Geissler             //                   to be valid
38087d84729SAndrew Geissler             // nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
38187d84729SAndrew Geissler             // members.push_back(
38287d84729SAndrew Geissler             //    {{"@odata.id", "/redfish/v1/Systems/system/BIOS"}});
38387d84729SAndrew Geissler             // aResp->res.jsonValue["Members@odata.count"] = members.size();
38487d84729SAndrew Geissler         }
38587d84729SAndrew Geissler         else
38687d84729SAndrew Geissler         {
38787d84729SAndrew Geissler             BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
38887d84729SAndrew Geissler         }
38987d84729SAndrew Geissler     }
39087d84729SAndrew Geissler 
39155c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
3921abe55efSEd Tanous                const std::vector<std::string> &params) override
3931abe55efSEd Tanous     {
394c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3950f74e643SEd Tanous         res.jsonValue["@odata.type"] =
3960f74e643SEd Tanous             "#SoftwareInventory.v1_1_0.SoftwareInventory";
3970f74e643SEd Tanous         res.jsonValue["@odata.context"] =
3980f74e643SEd Tanous             "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
3990f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory";
4000f74e643SEd Tanous         res.jsonValue["Updateable"] = false;
4010f74e643SEd Tanous         res.jsonValue["Status"]["Health"] = "OK";
4020f74e643SEd Tanous         res.jsonValue["Status"]["HealthRollup"] = "OK";
4030f74e643SEd Tanous         res.jsonValue["Status"]["State"] = "Enabled";
4046c4eb9deSJennifer Lee 
4051abe55efSEd Tanous         if (params.size() != 1)
4061abe55efSEd Tanous         {
407f12894f8SJason M. Bills             messages::internalError(res);
408729dae72SJennifer Lee             res.end();
409729dae72SJennifer Lee             return;
410729dae72SJennifer Lee         }
411729dae72SJennifer Lee 
4123ae837c9SEd Tanous         std::shared_ptr<std::string> swId =
413c711bf86SEd Tanous             std::make_shared<std::string>(params[0]);
414c711bf86SEd Tanous 
41555c7b7a2SEd Tanous         res.jsonValue["@odata.id"] =
4163ae837c9SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
417c711bf86SEd Tanous 
418c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
4193ae837c9SEd Tanous             [asyncResp, swId](
420c711bf86SEd Tanous                 const boost::system::error_code ec,
4216c4eb9deSJennifer Lee                 const std::vector<std::pair<
4221abe55efSEd Tanous                     std::string, std::vector<std::pair<
4231abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
4246c4eb9deSJennifer Lee                     &subtree) {
42555c7b7a2SEd Tanous                 BMCWEB_LOG_DEBUG << "doGet callback...";
4261abe55efSEd Tanous                 if (ec)
4271abe55efSEd Tanous                 {
428f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
4296c4eb9deSJennifer Lee                     return;
4306c4eb9deSJennifer Lee                 }
4316c4eb9deSJennifer Lee 
4321abe55efSEd Tanous                 for (const std::pair<
4331abe55efSEd Tanous                          std::string,
4341abe55efSEd Tanous                          std::vector<
4351abe55efSEd Tanous                              std::pair<std::string, std::vector<std::string>>>>
4361abe55efSEd Tanous                          &obj : subtree)
4371abe55efSEd Tanous                 {
4383ae837c9SEd Tanous                     if (boost::ends_with(obj.first, *swId) != true)
4391abe55efSEd Tanous                     {
440acb7cfb4SJennifer Lee                         continue;
441acb7cfb4SJennifer Lee                     }
442acb7cfb4SJennifer Lee 
443f4b65ab1SJennifer Lee                     if (obj.second.size() < 1)
4441abe55efSEd Tanous                     {
445acb7cfb4SJennifer Lee                         continue;
446acb7cfb4SJennifer Lee                     }
4476c4eb9deSJennifer Lee 
44855c7b7a2SEd Tanous                     crow::connections::systemBus->async_method_call(
4491abe55efSEd Tanous                         [asyncResp,
4503ae837c9SEd Tanous                          swId](const boost::system::error_code error_code,
4511abe55efSEd Tanous                                const boost::container::flat_map<
4521abe55efSEd Tanous                                    std::string, VariantType> &propertiesList) {
4531abe55efSEd Tanous                             if (error_code)
4541abe55efSEd Tanous                             {
455f12894f8SJason M. Bills                                 messages::internalError(asyncResp->res);
4566c4eb9deSJennifer Lee                                 return;
4576c4eb9deSJennifer Lee                             }
4581abe55efSEd Tanous                             boost::container::flat_map<
4591abe55efSEd Tanous                                 std::string, VariantType>::const_iterator it =
4606c4eb9deSJennifer Lee                                 propertiesList.find("Purpose");
4611abe55efSEd Tanous                             if (it == propertiesList.end())
4621abe55efSEd Tanous                             {
4631abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4641abe55efSEd Tanous                                     << "Can't find property \"Purpose\"!";
465f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
466f12894f8SJason M. Bills                                                           "Purpose");
4676c4eb9deSJennifer Lee                                 return;
4686c4eb9deSJennifer Lee                             }
4693ae837c9SEd Tanous                             const std::string *swInvPurpose =
470abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
4713ae837c9SEd Tanous                             if (swInvPurpose == nullptr)
4721abe55efSEd Tanous                             {
4731abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4741abe55efSEd Tanous                                     << "wrong types for property\"Purpose\"!";
475f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
476f12894f8SJason M. Bills                                                                  "", "Purpose");
477acb7cfb4SJennifer Lee                                 return;
478acb7cfb4SJennifer Lee                             }
479c711bf86SEd Tanous 
4803ae837c9SEd Tanous                             BMCWEB_LOG_DEBUG << "swInvPurpose = "
4813ae837c9SEd Tanous                                              << *swInvPurpose;
482c711bf86SEd Tanous                             it = propertiesList.find("Version");
4831abe55efSEd Tanous                             if (it == propertiesList.end())
4841abe55efSEd Tanous                             {
4851abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4861abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
487f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
488f12894f8SJason M. Bills                                                           "Version");
489c711bf86SEd Tanous                                 return;
490acb7cfb4SJennifer Lee                             }
491acb7cfb4SJennifer Lee 
492f4b65ab1SJennifer Lee                             BMCWEB_LOG_DEBUG << "Version found!";
493c711bf86SEd Tanous 
494f4b65ab1SJennifer Lee                             const std::string *version =
495abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
496f4b65ab1SJennifer Lee 
497f4b65ab1SJennifer Lee                             if (version == nullptr)
4981abe55efSEd Tanous                             {
4991abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5001abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
501f12894f8SJason M. Bills 
502f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
503f12894f8SJason M. Bills                                                                  "", "Version");
5046c4eb9deSJennifer Lee                                 return;
5056c4eb9deSJennifer Lee                             }
506c711bf86SEd Tanous                             asyncResp->res.jsonValue["Version"] = *version;
5073ae837c9SEd Tanous                             asyncResp->res.jsonValue["Id"] = *swId;
50854daabe7SAndrew Geissler 
50954daabe7SAndrew Geissler                             // swInvPurpose is of format:
51054daabe7SAndrew Geissler                             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
51154daabe7SAndrew Geissler                             // Translate this to "ABC update"
51254daabe7SAndrew Geissler                             size_t endDesc = swInvPurpose->rfind(".");
51354daabe7SAndrew Geissler                             if (endDesc == std::string::npos)
51454daabe7SAndrew Geissler                             {
51554daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
51654daabe7SAndrew Geissler                                 return;
51754daabe7SAndrew Geissler                             }
51854daabe7SAndrew Geissler                             endDesc++;
51954daabe7SAndrew Geissler                             if (endDesc >= swInvPurpose->size())
52054daabe7SAndrew Geissler                             {
52154daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
52254daabe7SAndrew Geissler                                 return;
52354daabe7SAndrew Geissler                             }
52454daabe7SAndrew Geissler 
52554daabe7SAndrew Geissler                             std::string formatDesc =
52654daabe7SAndrew Geissler                                 swInvPurpose->substr(endDesc);
52754daabe7SAndrew Geissler                             asyncResp->res.jsonValue["Description"] =
52854daabe7SAndrew Geissler                                 formatDesc + " update";
52987d84729SAndrew Geissler                             getRelatedItems(asyncResp, *swInvPurpose);
5306c4eb9deSJennifer Lee                         },
531c711bf86SEd Tanous                         obj.second[0].first, obj.first,
532c711bf86SEd Tanous                         "org.freedesktop.DBus.Properties", "GetAll",
533c711bf86SEd Tanous                         "xyz.openbmc_project.Software.Version");
5346c4eb9deSJennifer Lee                 }
535c711bf86SEd Tanous             },
536c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
537c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
538c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
539c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
5401abe55efSEd Tanous             std::array<const char *, 1>{
5411abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
5426c4eb9deSJennifer Lee     }
543729dae72SJennifer Lee };
544729dae72SJennifer Lee 
545729dae72SJennifer Lee } // namespace redfish
546