xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision 86adcd6d5cd159448c800a03c29485b93cdd2189)
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 
270e7de46fSAndrew Geissler // Match signals added on software path
28acb7cfb4SJennifer Lee static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
290e7de46fSAndrew Geissler // Only allow one update at a time
300e7de46fSAndrew Geissler static bool fwUpdateInProgress = false;
31*86adcd6dSAndrew Geissler // Timer for software available
32*86adcd6dSAndrew Geissler static std::unique_ptr<boost::asio::deadline_timer> fwAvailableTimer;
33*86adcd6dSAndrew Geissler 
34*86adcd6dSAndrew Geissler static void cleanUp()
35*86adcd6dSAndrew Geissler {
36*86adcd6dSAndrew Geissler     fwUpdateInProgress = false;
37*86adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
38*86adcd6dSAndrew Geissler }
39*86adcd6dSAndrew Geissler static void activateImage(const std::string &objPath,
40*86adcd6dSAndrew Geissler                           const std::string &service)
41*86adcd6dSAndrew Geissler {
42*86adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
43*86adcd6dSAndrew Geissler     crow::connections::systemBus->async_method_call(
44*86adcd6dSAndrew Geissler         [](const boost::system::error_code error_code) {
45*86adcd6dSAndrew Geissler             if (error_code)
46*86adcd6dSAndrew Geissler             {
47*86adcd6dSAndrew Geissler                 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
48*86adcd6dSAndrew Geissler                 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
49*86adcd6dSAndrew Geissler             }
50*86adcd6dSAndrew Geissler         },
51*86adcd6dSAndrew Geissler         service, objPath, "org.freedesktop.DBus.Properties", "Set",
52*86adcd6dSAndrew Geissler         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
53*86adcd6dSAndrew Geissler         std::variant<std::string>(
54*86adcd6dSAndrew Geissler             "xyz.openbmc_project.Software.Activation.RequestedActivations."
55*86adcd6dSAndrew Geissler             "Active"));
56*86adcd6dSAndrew Geissler }
57*86adcd6dSAndrew Geissler static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
58*86adcd6dSAndrew Geissler                                    sdbusplus::message::message &m)
59*86adcd6dSAndrew Geissler {
60*86adcd6dSAndrew Geissler     std::vector<std::pair<
61*86adcd6dSAndrew Geissler         std::string,
62*86adcd6dSAndrew Geissler         std::vector<std::pair<std::string, std::variant<std::string>>>>>
63*86adcd6dSAndrew Geissler         interfacesProperties;
64*86adcd6dSAndrew Geissler 
65*86adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
66*86adcd6dSAndrew Geissler 
67*86adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
68*86adcd6dSAndrew Geissler 
69*86adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
70*86adcd6dSAndrew Geissler     for (auto &interface : interfacesProperties)
71*86adcd6dSAndrew Geissler     {
72*86adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "interface = " << interface.first;
73*86adcd6dSAndrew Geissler 
74*86adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
75*86adcd6dSAndrew Geissler         {
76*86adcd6dSAndrew Geissler             // Found our interface, disable callbacks
77*86adcd6dSAndrew Geissler             fwUpdateMatcher = nullptr;
78*86adcd6dSAndrew Geissler 
79*86adcd6dSAndrew Geissler             // Retrieve service and activate
80*86adcd6dSAndrew Geissler             crow::connections::systemBus->async_method_call(
81*86adcd6dSAndrew Geissler                 [objPath, asyncResp](
82*86adcd6dSAndrew Geissler                     const boost::system::error_code error_code,
83*86adcd6dSAndrew Geissler                     const std::vector<std::pair<
84*86adcd6dSAndrew Geissler                         std::string, std::vector<std::string>>> &objInfo) {
85*86adcd6dSAndrew Geissler                     if (error_code)
86*86adcd6dSAndrew Geissler                     {
87*86adcd6dSAndrew Geissler                         BMCWEB_LOG_DEBUG << "error_code = " << error_code;
88*86adcd6dSAndrew Geissler                         BMCWEB_LOG_DEBUG << "error msg = "
89*86adcd6dSAndrew Geissler                                          << error_code.message();
90*86adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
91*86adcd6dSAndrew Geissler                         cleanUp();
92*86adcd6dSAndrew Geissler                         return;
93*86adcd6dSAndrew Geissler                     }
94*86adcd6dSAndrew Geissler                     // Ensure we only got one service back
95*86adcd6dSAndrew Geissler                     if (objInfo.size() != 1)
96*86adcd6dSAndrew Geissler                     {
97*86adcd6dSAndrew Geissler                         BMCWEB_LOG_ERROR << "Invalid Object Size "
98*86adcd6dSAndrew Geissler                                          << objInfo.size();
99*86adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
100*86adcd6dSAndrew Geissler                         cleanUp();
101*86adcd6dSAndrew Geissler                         return;
102*86adcd6dSAndrew Geissler                     }
103*86adcd6dSAndrew Geissler                     // cancel timer only when
104*86adcd6dSAndrew Geissler                     // xyz.openbmc_project.Software.Activation interface
105*86adcd6dSAndrew Geissler                     // is added
106*86adcd6dSAndrew Geissler                     fwAvailableTimer = nullptr;
107*86adcd6dSAndrew Geissler 
108*86adcd6dSAndrew Geissler                     activateImage(objPath.str, objInfo[0].first);
109*86adcd6dSAndrew Geissler                     redfish::messages::success(asyncResp->res);
110*86adcd6dSAndrew Geissler                     fwUpdateInProgress = false;
111*86adcd6dSAndrew Geissler                 },
112*86adcd6dSAndrew Geissler                 "xyz.openbmc_project.ObjectMapper",
113*86adcd6dSAndrew Geissler                 "/xyz/openbmc_project/object_mapper",
114*86adcd6dSAndrew Geissler                 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
115*86adcd6dSAndrew Geissler                 std::array<const char *, 1>{
116*86adcd6dSAndrew Geissler                     "xyz.openbmc_project.Software.Activation"});
117*86adcd6dSAndrew Geissler         }
118*86adcd6dSAndrew Geissler     }
119*86adcd6dSAndrew Geissler }
120*86adcd6dSAndrew Geissler 
121*86adcd6dSAndrew Geissler static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
122*86adcd6dSAndrew Geissler                                         const crow::Request &req)
123*86adcd6dSAndrew Geissler {
124*86adcd6dSAndrew Geissler     // Only allow one FW update at a time
125*86adcd6dSAndrew Geissler     if (fwUpdateInProgress != false)
126*86adcd6dSAndrew Geissler     {
127*86adcd6dSAndrew Geissler         asyncResp->res.addHeader("Retry-After", "30");
128*86adcd6dSAndrew Geissler         messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
129*86adcd6dSAndrew Geissler         return;
130*86adcd6dSAndrew Geissler     }
131*86adcd6dSAndrew Geissler 
132*86adcd6dSAndrew Geissler     fwAvailableTimer = std::make_unique<boost::asio::deadline_timer>(
133*86adcd6dSAndrew Geissler         *req.ioService, boost::posix_time::seconds(5));
134*86adcd6dSAndrew Geissler 
135*86adcd6dSAndrew Geissler     fwAvailableTimer->expires_from_now(boost::posix_time::seconds(5));
136*86adcd6dSAndrew Geissler 
137*86adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
138*86adcd6dSAndrew Geissler         [asyncResp](const boost::system::error_code &ec) {
139*86adcd6dSAndrew Geissler             cleanUp();
140*86adcd6dSAndrew Geissler             if (ec == boost::asio::error::operation_aborted)
141*86adcd6dSAndrew Geissler             {
142*86adcd6dSAndrew Geissler                 // expected, we were canceled before the timer completed.
143*86adcd6dSAndrew Geissler                 return;
144*86adcd6dSAndrew Geissler             }
145*86adcd6dSAndrew Geissler             BMCWEB_LOG_ERROR
146*86adcd6dSAndrew Geissler                 << "Timed out waiting for firmware object being created";
147*86adcd6dSAndrew Geissler             BMCWEB_LOG_ERROR
148*86adcd6dSAndrew Geissler                 << "FW image may has already been uploaded to server";
149*86adcd6dSAndrew Geissler             if (ec)
150*86adcd6dSAndrew Geissler             {
151*86adcd6dSAndrew Geissler                 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
152*86adcd6dSAndrew Geissler                 return;
153*86adcd6dSAndrew Geissler             }
154*86adcd6dSAndrew Geissler 
155*86adcd6dSAndrew Geissler             redfish::messages::internalError(asyncResp->res);
156*86adcd6dSAndrew Geissler         });
157*86adcd6dSAndrew Geissler 
158*86adcd6dSAndrew Geissler     auto callback = [asyncResp](sdbusplus::message::message &m) {
159*86adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "Match fired";
160*86adcd6dSAndrew Geissler         softwareInterfaceAdded(asyncResp, m);
161*86adcd6dSAndrew Geissler     };
162*86adcd6dSAndrew Geissler 
163*86adcd6dSAndrew Geissler     fwUpdateInProgress = true;
164*86adcd6dSAndrew Geissler 
165*86adcd6dSAndrew Geissler     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
166*86adcd6dSAndrew Geissler         *crow::connections::systemBus,
167*86adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
168*86adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
169*86adcd6dSAndrew Geissler         callback);
170*86adcd6dSAndrew Geissler }
171729dae72SJennifer Lee 
1721abe55efSEd Tanous class UpdateService : public Node
1731abe55efSEd Tanous {
174729dae72SJennifer Lee   public:
1751abe55efSEd Tanous     UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
1761abe55efSEd Tanous     {
177729dae72SJennifer Lee         entityPrivileges = {
178729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
179729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
180729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
181729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
182729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
183729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
184729dae72SJennifer Lee     }
185729dae72SJennifer Lee 
186729dae72SJennifer Lee   private:
18755c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
1881abe55efSEd Tanous                const std::vector<std::string> &params) override
1891abe55efSEd Tanous     {
1900f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
1910f74e643SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
1920f74e643SEd Tanous         res.jsonValue["@odata.context"] =
1930f74e643SEd Tanous             "/redfish/v1/$metadata#UpdateService.UpdateService";
1940f74e643SEd Tanous         res.jsonValue["Id"] = "UpdateService";
1950f74e643SEd Tanous         res.jsonValue["Description"] = "Service for Software Update";
1960f74e643SEd Tanous         res.jsonValue["Name"] = "Update Service";
1970f74e643SEd Tanous         res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
1980f74e643SEd Tanous         // UpdateService cannot be disabled
1990f74e643SEd Tanous         res.jsonValue["ServiceEnabled"] = true;
2000f74e643SEd Tanous         res.jsonValue["FirmwareInventory"] = {
2010f74e643SEd Tanous             {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
202729dae72SJennifer Lee         res.end();
203729dae72SJennifer Lee     }
2040e7de46fSAndrew Geissler 
205acb7cfb4SJennifer Lee     void doPost(crow::Response &res, const crow::Request &req,
2061abe55efSEd Tanous                 const std::vector<std::string> &params) override
2071abe55efSEd Tanous     {
208acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "doPost...";
209acb7cfb4SJennifer Lee 
2100e7de46fSAndrew Geissler         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
211acb7cfb4SJennifer Lee 
212*86adcd6dSAndrew Geissler         // Setup callback for when new software detected
213*86adcd6dSAndrew Geissler         monitorForSoftwareAvailable(asyncResp, req);
214acb7cfb4SJennifer Lee 
215acb7cfb4SJennifer Lee         std::string filepath(
216acb7cfb4SJennifer Lee             "/tmp/images/" +
217acb7cfb4SJennifer Lee             boost::uuids::to_string(boost::uuids::random_generator()()));
218acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
219acb7cfb4SJennifer Lee         std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
220acb7cfb4SJennifer Lee                                         std::ofstream::trunc);
221acb7cfb4SJennifer Lee         out << req.body;
222acb7cfb4SJennifer Lee         out.close();
223acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "file upload complete!!";
224acb7cfb4SJennifer Lee     }
225729dae72SJennifer Lee };
226729dae72SJennifer Lee 
2271abe55efSEd Tanous class SoftwareInventoryCollection : public Node
2281abe55efSEd Tanous {
229729dae72SJennifer Lee   public:
230729dae72SJennifer Lee     template <typename CrowApp>
2311abe55efSEd Tanous     SoftwareInventoryCollection(CrowApp &app) :
2321abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
2331abe55efSEd Tanous     {
234729dae72SJennifer Lee         entityPrivileges = {
235729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
236729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
237729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
238729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
239729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
240729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
241729dae72SJennifer Lee     }
242729dae72SJennifer Lee 
243729dae72SJennifer Lee   private:
24455c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
2451abe55efSEd Tanous                const std::vector<std::string> &params) override
2461abe55efSEd Tanous     {
247c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2480f74e643SEd Tanous         res.jsonValue["@odata.type"] =
2490f74e643SEd Tanous             "#SoftwareInventoryCollection.SoftwareInventoryCollection";
2500f74e643SEd Tanous         res.jsonValue["@odata.id"] =
2510f74e643SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
2520f74e643SEd Tanous         res.jsonValue["@odata.context"] =
2530f74e643SEd Tanous             "/redfish/v1/"
2540f74e643SEd Tanous             "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
2550f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory Collection";
256c711bf86SEd Tanous 
257c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
258c711bf86SEd Tanous             [asyncResp](
259c711bf86SEd Tanous                 const boost::system::error_code ec,
2606c4eb9deSJennifer Lee                 const std::vector<std::pair<
2611abe55efSEd Tanous                     std::string, std::vector<std::pair<
2621abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
2636c4eb9deSJennifer Lee                     &subtree) {
2641abe55efSEd Tanous                 if (ec)
2651abe55efSEd Tanous                 {
266f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
2676c4eb9deSJennifer Lee                     return;
268729dae72SJennifer Lee                 }
269c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
270c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2716c4eb9deSJennifer Lee 
2721abe55efSEd Tanous                 for (auto &obj : subtree)
2731abe55efSEd Tanous                 {
2741abe55efSEd Tanous                     const std::vector<
2751abe55efSEd Tanous                         std::pair<std::string, std::vector<std::string>>>
2766c4eb9deSJennifer Lee                         &connections = obj.second;
2776c4eb9deSJennifer Lee 
278f4b65ab1SJennifer Lee                     // if can't parse fw id then return
27927826b5fSEd Tanous                     std::size_t idPos;
28027826b5fSEd Tanous                     if ((idPos = obj.first.rfind("/")) == std::string::npos)
281f4b65ab1SJennifer Lee                     {
282f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
283f4b65ab1SJennifer Lee                         BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
284f4b65ab1SJennifer Lee                         return;
285f4b65ab1SJennifer Lee                     }
286f4b65ab1SJennifer Lee                     std::string swId = obj.first.substr(idPos + 1);
287f4b65ab1SJennifer Lee 
2881abe55efSEd Tanous                     for (auto &conn : connections)
2891abe55efSEd Tanous                     {
290c711bf86SEd Tanous                         const std::string &connectionName = conn.first;
2911abe55efSEd Tanous                         BMCWEB_LOG_DEBUG << "connectionName = "
2921abe55efSEd Tanous                                          << connectionName;
29355c7b7a2SEd Tanous                         BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
2946c4eb9deSJennifer Lee 
29555c7b7a2SEd Tanous                         crow::connections::systemBus->async_method_call(
296f4b65ab1SJennifer Lee                             [asyncResp,
297f4b65ab1SJennifer Lee                              swId](const boost::system::error_code error_code,
298c711bf86SEd Tanous                                    const VariantType &activation) {
2991abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
3001abe55efSEd Tanous                                     << "safe returned in lambda function";
3011abe55efSEd Tanous                                 if (error_code)
3021abe55efSEd Tanous                                 {
303f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
3046c4eb9deSJennifer Lee                                     return;
3056c4eb9deSJennifer Lee                                 }
306c711bf86SEd Tanous 
307f4b65ab1SJennifer Lee                                 const std::string *swActivationStatus =
308abf2add6SEd Tanous                                     std::get_if<std::string>(&activation);
309f4b65ab1SJennifer Lee                                 if (swActivationStatus == nullptr)
3101abe55efSEd Tanous                                 {
311f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
312acb7cfb4SJennifer Lee                                     return;
313acb7cfb4SJennifer Lee                                 }
314f4b65ab1SJennifer Lee                                 if (swActivationStatus != nullptr &&
315f4b65ab1SJennifer Lee                                     *swActivationStatus !=
316f4b65ab1SJennifer Lee                                         "xyz.openbmc_project.Software."
317f4b65ab1SJennifer Lee                                         "Activation."
318f4b65ab1SJennifer Lee                                         "Activations.Active")
3191abe55efSEd Tanous                                 {
320f4b65ab1SJennifer Lee                                     // The activation status of this software is
321f4b65ab1SJennifer Lee                                     // not currently active, so does not need to
322f4b65ab1SJennifer Lee                                     // be listed in the response
323c711bf86SEd Tanous                                     return;
324c711bf86SEd Tanous                                 }
325c711bf86SEd Tanous                                 nlohmann::json &members =
326c711bf86SEd Tanous                                     asyncResp->res.jsonValue["Members"];
327c711bf86SEd Tanous                                 members.push_back(
328f4b65ab1SJennifer Lee                                     {{"@odata.id", "/redfish/v1/UpdateService/"
3291abe55efSEd Tanous                                                    "FirmwareInventory/" +
330f4b65ab1SJennifer Lee                                                        swId}});
3311abe55efSEd Tanous                                 asyncResp->res
3321abe55efSEd Tanous                                     .jsonValue["Members@odata.count"] =
333c711bf86SEd Tanous                                     members.size();
3346c4eb9deSJennifer Lee                             },
3351abe55efSEd Tanous                             connectionName, obj.first,
3361abe55efSEd Tanous                             "org.freedesktop.DBus.Properties", "Get",
3371abe55efSEd Tanous                             "xyz.openbmc_project.Software.Activation",
338acb7cfb4SJennifer Lee                             "Activation");
3396c4eb9deSJennifer Lee                     }
3406c4eb9deSJennifer Lee                 }
341c711bf86SEd Tanous             },
342c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
343c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
344c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
345c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
3461abe55efSEd Tanous             std::array<const char *, 1>{
3471abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
348729dae72SJennifer Lee     }
349729dae72SJennifer Lee };
350c711bf86SEd Tanous 
3511abe55efSEd Tanous class SoftwareInventory : public Node
3521abe55efSEd Tanous {
353729dae72SJennifer Lee   public:
354729dae72SJennifer Lee     template <typename CrowApp>
3551abe55efSEd Tanous     SoftwareInventory(CrowApp &app) :
3561abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
3571abe55efSEd Tanous              std::string())
3581abe55efSEd Tanous     {
359729dae72SJennifer Lee         entityPrivileges = {
360729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
361729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
362729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
363729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
364729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
365729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
366729dae72SJennifer Lee     }
367729dae72SJennifer Lee 
368729dae72SJennifer Lee   private:
36987d84729SAndrew Geissler     /* Fill related item links (i.e. bmc, bios) in for inventory */
37087d84729SAndrew Geissler     static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
37187d84729SAndrew Geissler                                 const std::string &purpose)
37287d84729SAndrew Geissler     {
37387d84729SAndrew Geissler         if (purpose == fw_util::bmcPurpose)
37487d84729SAndrew Geissler         {
37587d84729SAndrew Geissler             nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
37687d84729SAndrew Geissler             members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
37787d84729SAndrew Geissler             aResp->res.jsonValue["Members@odata.count"] = members.size();
37887d84729SAndrew Geissler         }
37987d84729SAndrew Geissler         else if (purpose == fw_util::biosPurpose)
38087d84729SAndrew Geissler         {
38187d84729SAndrew Geissler             // TODO(geissonator) Need BIOS schema support added for this
38287d84729SAndrew Geissler             //                   to be valid
38387d84729SAndrew Geissler             // nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
38487d84729SAndrew Geissler             // members.push_back(
38587d84729SAndrew Geissler             //    {{"@odata.id", "/redfish/v1/Systems/system/BIOS"}});
38687d84729SAndrew Geissler             // aResp->res.jsonValue["Members@odata.count"] = members.size();
38787d84729SAndrew Geissler         }
38887d84729SAndrew Geissler         else
38987d84729SAndrew Geissler         {
39087d84729SAndrew Geissler             BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
39187d84729SAndrew Geissler         }
39287d84729SAndrew Geissler     }
39387d84729SAndrew Geissler 
39455c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
3951abe55efSEd Tanous                const std::vector<std::string> &params) override
3961abe55efSEd Tanous     {
397c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3980f74e643SEd Tanous         res.jsonValue["@odata.type"] =
3990f74e643SEd Tanous             "#SoftwareInventory.v1_1_0.SoftwareInventory";
4000f74e643SEd Tanous         res.jsonValue["@odata.context"] =
4010f74e643SEd Tanous             "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
4020f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory";
4030f74e643SEd Tanous         res.jsonValue["Updateable"] = false;
4040f74e643SEd Tanous         res.jsonValue["Status"]["Health"] = "OK";
4050f74e643SEd Tanous         res.jsonValue["Status"]["HealthRollup"] = "OK";
4060f74e643SEd Tanous         res.jsonValue["Status"]["State"] = "Enabled";
4076c4eb9deSJennifer Lee 
4081abe55efSEd Tanous         if (params.size() != 1)
4091abe55efSEd Tanous         {
410f12894f8SJason M. Bills             messages::internalError(res);
411729dae72SJennifer Lee             res.end();
412729dae72SJennifer Lee             return;
413729dae72SJennifer Lee         }
414729dae72SJennifer Lee 
4153ae837c9SEd Tanous         std::shared_ptr<std::string> swId =
416c711bf86SEd Tanous             std::make_shared<std::string>(params[0]);
417c711bf86SEd Tanous 
41855c7b7a2SEd Tanous         res.jsonValue["@odata.id"] =
4193ae837c9SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
420c711bf86SEd Tanous 
421c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
4223ae837c9SEd Tanous             [asyncResp, swId](
423c711bf86SEd Tanous                 const boost::system::error_code ec,
4246c4eb9deSJennifer Lee                 const std::vector<std::pair<
4251abe55efSEd Tanous                     std::string, std::vector<std::pair<
4261abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
4276c4eb9deSJennifer Lee                     &subtree) {
42855c7b7a2SEd Tanous                 BMCWEB_LOG_DEBUG << "doGet callback...";
4291abe55efSEd Tanous                 if (ec)
4301abe55efSEd Tanous                 {
431f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
4326c4eb9deSJennifer Lee                     return;
4336c4eb9deSJennifer Lee                 }
4346c4eb9deSJennifer Lee 
4351abe55efSEd Tanous                 for (const std::pair<
4361abe55efSEd Tanous                          std::string,
4371abe55efSEd Tanous                          std::vector<
4381abe55efSEd Tanous                              std::pair<std::string, std::vector<std::string>>>>
4391abe55efSEd Tanous                          &obj : subtree)
4401abe55efSEd Tanous                 {
4413ae837c9SEd Tanous                     if (boost::ends_with(obj.first, *swId) != true)
4421abe55efSEd Tanous                     {
443acb7cfb4SJennifer Lee                         continue;
444acb7cfb4SJennifer Lee                     }
445acb7cfb4SJennifer Lee 
446f4b65ab1SJennifer Lee                     if (obj.second.size() < 1)
4471abe55efSEd Tanous                     {
448acb7cfb4SJennifer Lee                         continue;
449acb7cfb4SJennifer Lee                     }
4506c4eb9deSJennifer Lee 
45155c7b7a2SEd Tanous                     crow::connections::systemBus->async_method_call(
4521abe55efSEd Tanous                         [asyncResp,
4533ae837c9SEd Tanous                          swId](const boost::system::error_code error_code,
4541abe55efSEd Tanous                                const boost::container::flat_map<
4551abe55efSEd Tanous                                    std::string, VariantType> &propertiesList) {
4561abe55efSEd Tanous                             if (error_code)
4571abe55efSEd Tanous                             {
458f12894f8SJason M. Bills                                 messages::internalError(asyncResp->res);
4596c4eb9deSJennifer Lee                                 return;
4606c4eb9deSJennifer Lee                             }
4611abe55efSEd Tanous                             boost::container::flat_map<
4621abe55efSEd Tanous                                 std::string, VariantType>::const_iterator it =
4636c4eb9deSJennifer Lee                                 propertiesList.find("Purpose");
4641abe55efSEd Tanous                             if (it == propertiesList.end())
4651abe55efSEd Tanous                             {
4661abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4671abe55efSEd Tanous                                     << "Can't find property \"Purpose\"!";
468f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
469f12894f8SJason M. Bills                                                           "Purpose");
4706c4eb9deSJennifer Lee                                 return;
4716c4eb9deSJennifer Lee                             }
4723ae837c9SEd Tanous                             const std::string *swInvPurpose =
473abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
4743ae837c9SEd Tanous                             if (swInvPurpose == nullptr)
4751abe55efSEd Tanous                             {
4761abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4771abe55efSEd Tanous                                     << "wrong types for property\"Purpose\"!";
478f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
479f12894f8SJason M. Bills                                                                  "", "Purpose");
480acb7cfb4SJennifer Lee                                 return;
481acb7cfb4SJennifer Lee                             }
482c711bf86SEd Tanous 
4833ae837c9SEd Tanous                             BMCWEB_LOG_DEBUG << "swInvPurpose = "
4843ae837c9SEd Tanous                                              << *swInvPurpose;
485c711bf86SEd Tanous                             it = propertiesList.find("Version");
4861abe55efSEd Tanous                             if (it == propertiesList.end())
4871abe55efSEd Tanous                             {
4881abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
4891abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
490f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
491f12894f8SJason M. Bills                                                           "Version");
492c711bf86SEd Tanous                                 return;
493acb7cfb4SJennifer Lee                             }
494acb7cfb4SJennifer Lee 
495f4b65ab1SJennifer Lee                             BMCWEB_LOG_DEBUG << "Version found!";
496c711bf86SEd Tanous 
497f4b65ab1SJennifer Lee                             const std::string *version =
498abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
499f4b65ab1SJennifer Lee 
500f4b65ab1SJennifer Lee                             if (version == nullptr)
5011abe55efSEd Tanous                             {
5021abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5031abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
504f12894f8SJason M. Bills 
505f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
506f12894f8SJason M. Bills                                                                  "", "Version");
5076c4eb9deSJennifer Lee                                 return;
5086c4eb9deSJennifer Lee                             }
509c711bf86SEd Tanous                             asyncResp->res.jsonValue["Version"] = *version;
5103ae837c9SEd Tanous                             asyncResp->res.jsonValue["Id"] = *swId;
51154daabe7SAndrew Geissler 
51254daabe7SAndrew Geissler                             // swInvPurpose is of format:
51354daabe7SAndrew Geissler                             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
51454daabe7SAndrew Geissler                             // Translate this to "ABC update"
51554daabe7SAndrew Geissler                             size_t endDesc = swInvPurpose->rfind(".");
51654daabe7SAndrew Geissler                             if (endDesc == std::string::npos)
51754daabe7SAndrew Geissler                             {
51854daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
51954daabe7SAndrew Geissler                                 return;
52054daabe7SAndrew Geissler                             }
52154daabe7SAndrew Geissler                             endDesc++;
52254daabe7SAndrew Geissler                             if (endDesc >= swInvPurpose->size())
52354daabe7SAndrew Geissler                             {
52454daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
52554daabe7SAndrew Geissler                                 return;
52654daabe7SAndrew Geissler                             }
52754daabe7SAndrew Geissler 
52854daabe7SAndrew Geissler                             std::string formatDesc =
52954daabe7SAndrew Geissler                                 swInvPurpose->substr(endDesc);
53054daabe7SAndrew Geissler                             asyncResp->res.jsonValue["Description"] =
53154daabe7SAndrew Geissler                                 formatDesc + " update";
53287d84729SAndrew Geissler                             getRelatedItems(asyncResp, *swInvPurpose);
5336c4eb9deSJennifer Lee                         },
534c711bf86SEd Tanous                         obj.second[0].first, obj.first,
535c711bf86SEd Tanous                         "org.freedesktop.DBus.Properties", "GetAll",
536c711bf86SEd Tanous                         "xyz.openbmc_project.Software.Version");
5376c4eb9deSJennifer Lee                 }
538c711bf86SEd Tanous             },
539c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
540c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
541c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
542c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
5431abe55efSEd Tanous             std::array<const char *, 1>{
5441abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
5456c4eb9deSJennifer Lee     }
546729dae72SJennifer Lee };
547729dae72SJennifer Lee 
548729dae72SJennifer Lee } // namespace redfish
549