xref: /openbmc/bmcweb/features/redfish/lib/update_service.hpp (revision fa1a5a38551bd1b9f04ad2d4f9fea2e5ade5cc4c)
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;
3186adcd6dSAndrew Geissler // Timer for software available
3286adcd6dSAndrew Geissler static std::unique_ptr<boost::asio::deadline_timer> fwAvailableTimer;
3386adcd6dSAndrew Geissler 
3486adcd6dSAndrew Geissler static void cleanUp()
3586adcd6dSAndrew Geissler {
3686adcd6dSAndrew Geissler     fwUpdateInProgress = false;
3786adcd6dSAndrew Geissler     fwUpdateMatcher = nullptr;
3886adcd6dSAndrew Geissler }
3986adcd6dSAndrew Geissler static void activateImage(const std::string &objPath,
4086adcd6dSAndrew Geissler                           const std::string &service)
4186adcd6dSAndrew Geissler {
4286adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
4386adcd6dSAndrew Geissler     crow::connections::systemBus->async_method_call(
4486adcd6dSAndrew Geissler         [](const boost::system::error_code error_code) {
4586adcd6dSAndrew Geissler             if (error_code)
4686adcd6dSAndrew Geissler             {
4786adcd6dSAndrew Geissler                 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
4886adcd6dSAndrew Geissler                 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
4986adcd6dSAndrew Geissler             }
5086adcd6dSAndrew Geissler         },
5186adcd6dSAndrew Geissler         service, objPath, "org.freedesktop.DBus.Properties", "Set",
5286adcd6dSAndrew Geissler         "xyz.openbmc_project.Software.Activation", "RequestedActivation",
5386adcd6dSAndrew Geissler         std::variant<std::string>(
5486adcd6dSAndrew Geissler             "xyz.openbmc_project.Software.Activation.RequestedActivations."
5586adcd6dSAndrew Geissler             "Active"));
5686adcd6dSAndrew Geissler }
5786adcd6dSAndrew Geissler static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
5886adcd6dSAndrew Geissler                                    sdbusplus::message::message &m)
5986adcd6dSAndrew Geissler {
6086adcd6dSAndrew Geissler     std::vector<std::pair<
6186adcd6dSAndrew Geissler         std::string,
6286adcd6dSAndrew Geissler         std::vector<std::pair<std::string, std::variant<std::string>>>>>
6386adcd6dSAndrew Geissler         interfacesProperties;
6486adcd6dSAndrew Geissler 
6586adcd6dSAndrew Geissler     sdbusplus::message::object_path objPath;
6686adcd6dSAndrew Geissler 
6786adcd6dSAndrew Geissler     m.read(objPath, interfacesProperties);
6886adcd6dSAndrew Geissler 
6986adcd6dSAndrew Geissler     BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
7086adcd6dSAndrew Geissler     for (auto &interface : interfacesProperties)
7186adcd6dSAndrew Geissler     {
7286adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "interface = " << interface.first;
7386adcd6dSAndrew Geissler 
7486adcd6dSAndrew Geissler         if (interface.first == "xyz.openbmc_project.Software.Activation")
7586adcd6dSAndrew Geissler         {
7686adcd6dSAndrew Geissler             // Found our interface, disable callbacks
7786adcd6dSAndrew Geissler             fwUpdateMatcher = nullptr;
7886adcd6dSAndrew Geissler 
7986adcd6dSAndrew Geissler             // Retrieve service and activate
8086adcd6dSAndrew Geissler             crow::connections::systemBus->async_method_call(
8186adcd6dSAndrew Geissler                 [objPath, asyncResp](
8286adcd6dSAndrew Geissler                     const boost::system::error_code error_code,
8386adcd6dSAndrew Geissler                     const std::vector<std::pair<
8486adcd6dSAndrew Geissler                         std::string, std::vector<std::string>>> &objInfo) {
8586adcd6dSAndrew Geissler                     if (error_code)
8686adcd6dSAndrew Geissler                     {
8786adcd6dSAndrew Geissler                         BMCWEB_LOG_DEBUG << "error_code = " << error_code;
8886adcd6dSAndrew Geissler                         BMCWEB_LOG_DEBUG << "error msg = "
8986adcd6dSAndrew Geissler                                          << error_code.message();
9086adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
9186adcd6dSAndrew Geissler                         cleanUp();
9286adcd6dSAndrew Geissler                         return;
9386adcd6dSAndrew Geissler                     }
9486adcd6dSAndrew Geissler                     // Ensure we only got one service back
9586adcd6dSAndrew Geissler                     if (objInfo.size() != 1)
9686adcd6dSAndrew Geissler                     {
9786adcd6dSAndrew Geissler                         BMCWEB_LOG_ERROR << "Invalid Object Size "
9886adcd6dSAndrew Geissler                                          << objInfo.size();
9986adcd6dSAndrew Geissler                         messages::internalError(asyncResp->res);
10086adcd6dSAndrew Geissler                         cleanUp();
10186adcd6dSAndrew Geissler                         return;
10286adcd6dSAndrew Geissler                     }
10386adcd6dSAndrew Geissler                     // cancel timer only when
10486adcd6dSAndrew Geissler                     // xyz.openbmc_project.Software.Activation interface
10586adcd6dSAndrew Geissler                     // is added
10686adcd6dSAndrew Geissler                     fwAvailableTimer = nullptr;
10786adcd6dSAndrew Geissler 
10886adcd6dSAndrew Geissler                     activateImage(objPath.str, objInfo[0].first);
10986adcd6dSAndrew Geissler                     redfish::messages::success(asyncResp->res);
11086adcd6dSAndrew Geissler                     fwUpdateInProgress = false;
11186adcd6dSAndrew Geissler                 },
11286adcd6dSAndrew Geissler                 "xyz.openbmc_project.ObjectMapper",
11386adcd6dSAndrew Geissler                 "/xyz/openbmc_project/object_mapper",
11486adcd6dSAndrew Geissler                 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
11586adcd6dSAndrew Geissler                 std::array<const char *, 1>{
11686adcd6dSAndrew Geissler                     "xyz.openbmc_project.Software.Activation"});
11786adcd6dSAndrew Geissler         }
11886adcd6dSAndrew Geissler     }
11986adcd6dSAndrew Geissler }
12086adcd6dSAndrew Geissler 
12186adcd6dSAndrew Geissler static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
12286adcd6dSAndrew Geissler                                         const crow::Request &req)
12386adcd6dSAndrew Geissler {
12486adcd6dSAndrew Geissler     // Only allow one FW update at a time
12586adcd6dSAndrew Geissler     if (fwUpdateInProgress != false)
12686adcd6dSAndrew Geissler     {
12786adcd6dSAndrew Geissler         asyncResp->res.addHeader("Retry-After", "30");
12886adcd6dSAndrew Geissler         messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
12986adcd6dSAndrew Geissler         return;
13086adcd6dSAndrew Geissler     }
13186adcd6dSAndrew Geissler 
13286adcd6dSAndrew Geissler     fwAvailableTimer = std::make_unique<boost::asio::deadline_timer>(
13386adcd6dSAndrew Geissler         *req.ioService, boost::posix_time::seconds(5));
13486adcd6dSAndrew Geissler 
13586adcd6dSAndrew Geissler     fwAvailableTimer->expires_from_now(boost::posix_time::seconds(5));
13686adcd6dSAndrew Geissler 
13786adcd6dSAndrew Geissler     fwAvailableTimer->async_wait(
13886adcd6dSAndrew Geissler         [asyncResp](const boost::system::error_code &ec) {
13986adcd6dSAndrew Geissler             cleanUp();
14086adcd6dSAndrew Geissler             if (ec == boost::asio::error::operation_aborted)
14186adcd6dSAndrew Geissler             {
14286adcd6dSAndrew Geissler                 // expected, we were canceled before the timer completed.
14386adcd6dSAndrew Geissler                 return;
14486adcd6dSAndrew Geissler             }
14586adcd6dSAndrew Geissler             BMCWEB_LOG_ERROR
14686adcd6dSAndrew Geissler                 << "Timed out waiting for firmware object being created";
14786adcd6dSAndrew Geissler             BMCWEB_LOG_ERROR
14886adcd6dSAndrew Geissler                 << "FW image may has already been uploaded to server";
14986adcd6dSAndrew Geissler             if (ec)
15086adcd6dSAndrew Geissler             {
15186adcd6dSAndrew Geissler                 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
15286adcd6dSAndrew Geissler                 return;
15386adcd6dSAndrew Geissler             }
15486adcd6dSAndrew Geissler 
15586adcd6dSAndrew Geissler             redfish::messages::internalError(asyncResp->res);
15686adcd6dSAndrew Geissler         });
15786adcd6dSAndrew Geissler 
15886adcd6dSAndrew Geissler     auto callback = [asyncResp](sdbusplus::message::message &m) {
15986adcd6dSAndrew Geissler         BMCWEB_LOG_DEBUG << "Match fired";
16086adcd6dSAndrew Geissler         softwareInterfaceAdded(asyncResp, m);
16186adcd6dSAndrew Geissler     };
16286adcd6dSAndrew Geissler 
16386adcd6dSAndrew Geissler     fwUpdateInProgress = true;
16486adcd6dSAndrew Geissler 
16586adcd6dSAndrew Geissler     fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
16686adcd6dSAndrew Geissler         *crow::connections::systemBus,
16786adcd6dSAndrew Geissler         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
16886adcd6dSAndrew Geissler         "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
16986adcd6dSAndrew Geissler         callback);
17086adcd6dSAndrew 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 
205*fa1a5a38SJayashankar Padath     void doPatch(crow::Response &res, const crow::Request &req,
206*fa1a5a38SJayashankar Padath                  const std::vector<std::string> &params) override
207*fa1a5a38SJayashankar Padath     {
208*fa1a5a38SJayashankar Padath         BMCWEB_LOG_DEBUG << "doPatch...";
209*fa1a5a38SJayashankar Padath 
210*fa1a5a38SJayashankar Padath         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
211*fa1a5a38SJayashankar Padath         std::string applyTime;
212*fa1a5a38SJayashankar Padath 
213*fa1a5a38SJayashankar Padath         if (!json_util::readJson(req, res, "ApplyTime", applyTime))
214*fa1a5a38SJayashankar Padath         {
215*fa1a5a38SJayashankar Padath             return;
216*fa1a5a38SJayashankar Padath         }
217*fa1a5a38SJayashankar Padath 
218*fa1a5a38SJayashankar Padath         if ((applyTime == "Immediate") || (applyTime == "OnReset"))
219*fa1a5a38SJayashankar Padath         {
220*fa1a5a38SJayashankar Padath             std::string applyTimeNewVal;
221*fa1a5a38SJayashankar Padath             if (applyTime == "Immediate")
222*fa1a5a38SJayashankar Padath             {
223*fa1a5a38SJayashankar Padath                 applyTimeNewVal = "xyz.openbmc_project.Software.ApplyTime."
224*fa1a5a38SJayashankar Padath                                   "RequestedApplyTimes.Immediate";
225*fa1a5a38SJayashankar Padath             }
226*fa1a5a38SJayashankar Padath             else
227*fa1a5a38SJayashankar Padath             {
228*fa1a5a38SJayashankar Padath                 applyTimeNewVal = "xyz.openbmc_project.Software.ApplyTime."
229*fa1a5a38SJayashankar Padath                                   "RequestedApplyTimes.OnReset";
230*fa1a5a38SJayashankar Padath             }
231*fa1a5a38SJayashankar Padath 
232*fa1a5a38SJayashankar Padath             // Set the requested image apply time value
233*fa1a5a38SJayashankar Padath             crow::connections::systemBus->async_method_call(
234*fa1a5a38SJayashankar Padath                 [asyncResp](const boost::system::error_code ec) {
235*fa1a5a38SJayashankar Padath                     if (ec)
236*fa1a5a38SJayashankar Padath                     {
237*fa1a5a38SJayashankar Padath                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
238*fa1a5a38SJayashankar Padath                         messages::internalError(asyncResp->res);
239*fa1a5a38SJayashankar Padath                         return;
240*fa1a5a38SJayashankar Padath                     }
241*fa1a5a38SJayashankar Padath                     messages::success(asyncResp->res);
242*fa1a5a38SJayashankar Padath                 },
243*fa1a5a38SJayashankar Padath                 "xyz.openbmc_project.Settings",
244*fa1a5a38SJayashankar Padath                 "/xyz/openbmc_project/software/apply_time",
245*fa1a5a38SJayashankar Padath                 "org.freedesktop.DBus.Properties", "Set",
246*fa1a5a38SJayashankar Padath                 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime",
247*fa1a5a38SJayashankar Padath                 std::variant<std::string>{applyTimeNewVal});
248*fa1a5a38SJayashankar Padath         }
249*fa1a5a38SJayashankar Padath         else
250*fa1a5a38SJayashankar Padath         {
251*fa1a5a38SJayashankar Padath             BMCWEB_LOG_INFO << "ApplyTime value is not in the list of "
252*fa1a5a38SJayashankar Padath                                "acceptable values";
253*fa1a5a38SJayashankar Padath             messages::propertyValueNotInList(asyncResp->res, applyTime,
254*fa1a5a38SJayashankar Padath                                              "ApplyTime");
255*fa1a5a38SJayashankar Padath         }
256*fa1a5a38SJayashankar Padath     }
257*fa1a5a38SJayashankar Padath 
258acb7cfb4SJennifer Lee     void doPost(crow::Response &res, const crow::Request &req,
2591abe55efSEd Tanous                 const std::vector<std::string> &params) override
2601abe55efSEd Tanous     {
261acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "doPost...";
262acb7cfb4SJennifer Lee 
2630e7de46fSAndrew Geissler         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
264acb7cfb4SJennifer Lee 
26586adcd6dSAndrew Geissler         // Setup callback for when new software detected
26686adcd6dSAndrew Geissler         monitorForSoftwareAvailable(asyncResp, req);
267acb7cfb4SJennifer Lee 
268acb7cfb4SJennifer Lee         std::string filepath(
269acb7cfb4SJennifer Lee             "/tmp/images/" +
270acb7cfb4SJennifer Lee             boost::uuids::to_string(boost::uuids::random_generator()()));
271acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
272acb7cfb4SJennifer Lee         std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
273acb7cfb4SJennifer Lee                                         std::ofstream::trunc);
274acb7cfb4SJennifer Lee         out << req.body;
275acb7cfb4SJennifer Lee         out.close();
276acb7cfb4SJennifer Lee         BMCWEB_LOG_DEBUG << "file upload complete!!";
277acb7cfb4SJennifer Lee     }
278729dae72SJennifer Lee };
279729dae72SJennifer Lee 
2801abe55efSEd Tanous class SoftwareInventoryCollection : public Node
2811abe55efSEd Tanous {
282729dae72SJennifer Lee   public:
283729dae72SJennifer Lee     template <typename CrowApp>
2841abe55efSEd Tanous     SoftwareInventoryCollection(CrowApp &app) :
2851abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
2861abe55efSEd Tanous     {
287729dae72SJennifer Lee         entityPrivileges = {
288729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
289729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
290729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
291729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
292729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
293729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
294729dae72SJennifer Lee     }
295729dae72SJennifer Lee 
296729dae72SJennifer Lee   private:
29755c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
2981abe55efSEd Tanous                const std::vector<std::string> &params) override
2991abe55efSEd Tanous     {
300c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3010f74e643SEd Tanous         res.jsonValue["@odata.type"] =
3020f74e643SEd Tanous             "#SoftwareInventoryCollection.SoftwareInventoryCollection";
3030f74e643SEd Tanous         res.jsonValue["@odata.id"] =
3040f74e643SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory";
3050f74e643SEd Tanous         res.jsonValue["@odata.context"] =
3060f74e643SEd Tanous             "/redfish/v1/"
3070f74e643SEd Tanous             "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
3080f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory Collection";
309c711bf86SEd Tanous 
310c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
311c711bf86SEd Tanous             [asyncResp](
312c711bf86SEd Tanous                 const boost::system::error_code ec,
3136c4eb9deSJennifer Lee                 const std::vector<std::pair<
3141abe55efSEd Tanous                     std::string, std::vector<std::pair<
3151abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
3166c4eb9deSJennifer Lee                     &subtree) {
3171abe55efSEd Tanous                 if (ec)
3181abe55efSEd Tanous                 {
319f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
3206c4eb9deSJennifer Lee                     return;
321729dae72SJennifer Lee                 }
322c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
323c711bf86SEd Tanous                 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3246c4eb9deSJennifer Lee 
3251abe55efSEd Tanous                 for (auto &obj : subtree)
3261abe55efSEd Tanous                 {
3271abe55efSEd Tanous                     const std::vector<
3281abe55efSEd Tanous                         std::pair<std::string, std::vector<std::string>>>
3296c4eb9deSJennifer Lee                         &connections = obj.second;
3306c4eb9deSJennifer Lee 
331f4b65ab1SJennifer Lee                     // if can't parse fw id then return
33227826b5fSEd Tanous                     std::size_t idPos;
33327826b5fSEd Tanous                     if ((idPos = obj.first.rfind("/")) == std::string::npos)
334f4b65ab1SJennifer Lee                     {
335f12894f8SJason M. Bills                         messages::internalError(asyncResp->res);
336f4b65ab1SJennifer Lee                         BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
337f4b65ab1SJennifer Lee                         return;
338f4b65ab1SJennifer Lee                     }
339f4b65ab1SJennifer Lee                     std::string swId = obj.first.substr(idPos + 1);
340f4b65ab1SJennifer Lee 
3411abe55efSEd Tanous                     for (auto &conn : connections)
3421abe55efSEd Tanous                     {
343c711bf86SEd Tanous                         const std::string &connectionName = conn.first;
3441abe55efSEd Tanous                         BMCWEB_LOG_DEBUG << "connectionName = "
3451abe55efSEd Tanous                                          << connectionName;
34655c7b7a2SEd Tanous                         BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
3476c4eb9deSJennifer Lee 
34855c7b7a2SEd Tanous                         crow::connections::systemBus->async_method_call(
349f4b65ab1SJennifer Lee                             [asyncResp,
350f4b65ab1SJennifer Lee                              swId](const boost::system::error_code error_code,
351c711bf86SEd Tanous                                    const VariantType &activation) {
3521abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
3531abe55efSEd Tanous                                     << "safe returned in lambda function";
3541abe55efSEd Tanous                                 if (error_code)
3551abe55efSEd Tanous                                 {
356f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
3576c4eb9deSJennifer Lee                                     return;
3586c4eb9deSJennifer Lee                                 }
359c711bf86SEd Tanous 
360f4b65ab1SJennifer Lee                                 const std::string *swActivationStatus =
361abf2add6SEd Tanous                                     std::get_if<std::string>(&activation);
362f4b65ab1SJennifer Lee                                 if (swActivationStatus == nullptr)
3631abe55efSEd Tanous                                 {
364f12894f8SJason M. Bills                                     messages::internalError(asyncResp->res);
365acb7cfb4SJennifer Lee                                     return;
366acb7cfb4SJennifer Lee                                 }
367f4b65ab1SJennifer Lee                                 if (swActivationStatus != nullptr &&
368f4b65ab1SJennifer Lee                                     *swActivationStatus !=
369f4b65ab1SJennifer Lee                                         "xyz.openbmc_project.Software."
370f4b65ab1SJennifer Lee                                         "Activation."
371f4b65ab1SJennifer Lee                                         "Activations.Active")
3721abe55efSEd Tanous                                 {
373f4b65ab1SJennifer Lee                                     // The activation status of this software is
374f4b65ab1SJennifer Lee                                     // not currently active, so does not need to
375f4b65ab1SJennifer Lee                                     // be listed in the response
376c711bf86SEd Tanous                                     return;
377c711bf86SEd Tanous                                 }
378c711bf86SEd Tanous                                 nlohmann::json &members =
379c711bf86SEd Tanous                                     asyncResp->res.jsonValue["Members"];
380c711bf86SEd Tanous                                 members.push_back(
381f4b65ab1SJennifer Lee                                     {{"@odata.id", "/redfish/v1/UpdateService/"
3821abe55efSEd Tanous                                                    "FirmwareInventory/" +
383f4b65ab1SJennifer Lee                                                        swId}});
3841abe55efSEd Tanous                                 asyncResp->res
3851abe55efSEd Tanous                                     .jsonValue["Members@odata.count"] =
386c711bf86SEd Tanous                                     members.size();
3876c4eb9deSJennifer Lee                             },
3881abe55efSEd Tanous                             connectionName, obj.first,
3891abe55efSEd Tanous                             "org.freedesktop.DBus.Properties", "Get",
3901abe55efSEd Tanous                             "xyz.openbmc_project.Software.Activation",
391acb7cfb4SJennifer Lee                             "Activation");
3926c4eb9deSJennifer Lee                     }
3936c4eb9deSJennifer Lee                 }
394c711bf86SEd Tanous             },
395c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
396c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
397c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
398c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
3991abe55efSEd Tanous             std::array<const char *, 1>{
4001abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
401729dae72SJennifer Lee     }
402729dae72SJennifer Lee };
403c711bf86SEd Tanous 
4041abe55efSEd Tanous class SoftwareInventory : public Node
4051abe55efSEd Tanous {
406729dae72SJennifer Lee   public:
407729dae72SJennifer Lee     template <typename CrowApp>
4081abe55efSEd Tanous     SoftwareInventory(CrowApp &app) :
4091abe55efSEd Tanous         Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
4101abe55efSEd Tanous              std::string())
4111abe55efSEd Tanous     {
412729dae72SJennifer Lee         entityPrivileges = {
413729dae72SJennifer Lee             {boost::beast::http::verb::get, {{"Login"}}},
414729dae72SJennifer Lee             {boost::beast::http::verb::head, {{"Login"}}},
415729dae72SJennifer Lee             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
416729dae72SJennifer Lee             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
417729dae72SJennifer Lee             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
418729dae72SJennifer Lee             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
419729dae72SJennifer Lee     }
420729dae72SJennifer Lee 
421729dae72SJennifer Lee   private:
42287d84729SAndrew Geissler     /* Fill related item links (i.e. bmc, bios) in for inventory */
42387d84729SAndrew Geissler     static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
42487d84729SAndrew Geissler                                 const std::string &purpose)
42587d84729SAndrew Geissler     {
42687d84729SAndrew Geissler         if (purpose == fw_util::bmcPurpose)
42787d84729SAndrew Geissler         {
42887d84729SAndrew Geissler             nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
42987d84729SAndrew Geissler             members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
43087d84729SAndrew Geissler             aResp->res.jsonValue["Members@odata.count"] = members.size();
43187d84729SAndrew Geissler         }
43287d84729SAndrew Geissler         else if (purpose == fw_util::biosPurpose)
43387d84729SAndrew Geissler         {
43487d84729SAndrew Geissler             // TODO(geissonator) Need BIOS schema support added for this
43587d84729SAndrew Geissler             //                   to be valid
43687d84729SAndrew Geissler             // nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
43787d84729SAndrew Geissler             // members.push_back(
43887d84729SAndrew Geissler             //    {{"@odata.id", "/redfish/v1/Systems/system/BIOS"}});
43987d84729SAndrew Geissler             // aResp->res.jsonValue["Members@odata.count"] = members.size();
44087d84729SAndrew Geissler         }
44187d84729SAndrew Geissler         else
44287d84729SAndrew Geissler         {
44387d84729SAndrew Geissler             BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
44487d84729SAndrew Geissler         }
44587d84729SAndrew Geissler     }
44687d84729SAndrew Geissler 
44755c7b7a2SEd Tanous     void doGet(crow::Response &res, const crow::Request &req,
4481abe55efSEd Tanous                const std::vector<std::string> &params) override
4491abe55efSEd Tanous     {
450c711bf86SEd Tanous         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
4510f74e643SEd Tanous         res.jsonValue["@odata.type"] =
4520f74e643SEd Tanous             "#SoftwareInventory.v1_1_0.SoftwareInventory";
4530f74e643SEd Tanous         res.jsonValue["@odata.context"] =
4540f74e643SEd Tanous             "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
4550f74e643SEd Tanous         res.jsonValue["Name"] = "Software Inventory";
4560f74e643SEd Tanous         res.jsonValue["Updateable"] = false;
4570f74e643SEd Tanous         res.jsonValue["Status"]["Health"] = "OK";
4580f74e643SEd Tanous         res.jsonValue["Status"]["HealthRollup"] = "OK";
4590f74e643SEd Tanous         res.jsonValue["Status"]["State"] = "Enabled";
4606c4eb9deSJennifer Lee 
4611abe55efSEd Tanous         if (params.size() != 1)
4621abe55efSEd Tanous         {
463f12894f8SJason M. Bills             messages::internalError(res);
464729dae72SJennifer Lee             res.end();
465729dae72SJennifer Lee             return;
466729dae72SJennifer Lee         }
467729dae72SJennifer Lee 
4683ae837c9SEd Tanous         std::shared_ptr<std::string> swId =
469c711bf86SEd Tanous             std::make_shared<std::string>(params[0]);
470c711bf86SEd Tanous 
47155c7b7a2SEd Tanous         res.jsonValue["@odata.id"] =
4723ae837c9SEd Tanous             "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
473c711bf86SEd Tanous 
474c711bf86SEd Tanous         crow::connections::systemBus->async_method_call(
4753ae837c9SEd Tanous             [asyncResp, swId](
476c711bf86SEd Tanous                 const boost::system::error_code ec,
4776c4eb9deSJennifer Lee                 const std::vector<std::pair<
4781abe55efSEd Tanous                     std::string, std::vector<std::pair<
4791abe55efSEd Tanous                                      std::string, std::vector<std::string>>>>>
4806c4eb9deSJennifer Lee                     &subtree) {
48155c7b7a2SEd Tanous                 BMCWEB_LOG_DEBUG << "doGet callback...";
4821abe55efSEd Tanous                 if (ec)
4831abe55efSEd Tanous                 {
484f12894f8SJason M. Bills                     messages::internalError(asyncResp->res);
4856c4eb9deSJennifer Lee                     return;
4866c4eb9deSJennifer Lee                 }
4876c4eb9deSJennifer Lee 
4881abe55efSEd Tanous                 for (const std::pair<
4891abe55efSEd Tanous                          std::string,
4901abe55efSEd Tanous                          std::vector<
4911abe55efSEd Tanous                              std::pair<std::string, std::vector<std::string>>>>
4921abe55efSEd Tanous                          &obj : subtree)
4931abe55efSEd Tanous                 {
4943ae837c9SEd Tanous                     if (boost::ends_with(obj.first, *swId) != true)
4951abe55efSEd Tanous                     {
496acb7cfb4SJennifer Lee                         continue;
497acb7cfb4SJennifer Lee                     }
498acb7cfb4SJennifer Lee 
499f4b65ab1SJennifer Lee                     if (obj.second.size() < 1)
5001abe55efSEd Tanous                     {
501acb7cfb4SJennifer Lee                         continue;
502acb7cfb4SJennifer Lee                     }
5036c4eb9deSJennifer Lee 
50455c7b7a2SEd Tanous                     crow::connections::systemBus->async_method_call(
5051abe55efSEd Tanous                         [asyncResp,
5063ae837c9SEd Tanous                          swId](const boost::system::error_code error_code,
5071abe55efSEd Tanous                                const boost::container::flat_map<
5081abe55efSEd Tanous                                    std::string, VariantType> &propertiesList) {
5091abe55efSEd Tanous                             if (error_code)
5101abe55efSEd Tanous                             {
511f12894f8SJason M. Bills                                 messages::internalError(asyncResp->res);
5126c4eb9deSJennifer Lee                                 return;
5136c4eb9deSJennifer Lee                             }
5141abe55efSEd Tanous                             boost::container::flat_map<
5151abe55efSEd Tanous                                 std::string, VariantType>::const_iterator it =
5166c4eb9deSJennifer Lee                                 propertiesList.find("Purpose");
5171abe55efSEd Tanous                             if (it == propertiesList.end())
5181abe55efSEd Tanous                             {
5191abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5201abe55efSEd Tanous                                     << "Can't find property \"Purpose\"!";
521f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
522f12894f8SJason M. Bills                                                           "Purpose");
5236c4eb9deSJennifer Lee                                 return;
5246c4eb9deSJennifer Lee                             }
5253ae837c9SEd Tanous                             const std::string *swInvPurpose =
526abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
5273ae837c9SEd Tanous                             if (swInvPurpose == nullptr)
5281abe55efSEd Tanous                             {
5291abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5301abe55efSEd Tanous                                     << "wrong types for property\"Purpose\"!";
531f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
532f12894f8SJason M. Bills                                                                  "", "Purpose");
533acb7cfb4SJennifer Lee                                 return;
534acb7cfb4SJennifer Lee                             }
535c711bf86SEd Tanous 
5363ae837c9SEd Tanous                             BMCWEB_LOG_DEBUG << "swInvPurpose = "
5373ae837c9SEd Tanous                                              << *swInvPurpose;
538c711bf86SEd Tanous                             it = propertiesList.find("Version");
5391abe55efSEd Tanous                             if (it == propertiesList.end())
5401abe55efSEd Tanous                             {
5411abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5421abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
543f12894f8SJason M. Bills                                 messages::propertyMissing(asyncResp->res,
544f12894f8SJason M. Bills                                                           "Version");
545c711bf86SEd Tanous                                 return;
546acb7cfb4SJennifer Lee                             }
547acb7cfb4SJennifer Lee 
548f4b65ab1SJennifer Lee                             BMCWEB_LOG_DEBUG << "Version found!";
549c711bf86SEd Tanous 
550f4b65ab1SJennifer Lee                             const std::string *version =
551abf2add6SEd Tanous                                 std::get_if<std::string>(&it->second);
552f4b65ab1SJennifer Lee 
553f4b65ab1SJennifer Lee                             if (version == nullptr)
5541abe55efSEd Tanous                             {
5551abe55efSEd Tanous                                 BMCWEB_LOG_DEBUG
5561abe55efSEd Tanous                                     << "Can't find property \"Version\"!";
557f12894f8SJason M. Bills 
558f12894f8SJason M. Bills                                 messages::propertyValueTypeError(asyncResp->res,
559f12894f8SJason M. Bills                                                                  "", "Version");
5606c4eb9deSJennifer Lee                                 return;
5616c4eb9deSJennifer Lee                             }
562c711bf86SEd Tanous                             asyncResp->res.jsonValue["Version"] = *version;
5633ae837c9SEd Tanous                             asyncResp->res.jsonValue["Id"] = *swId;
56454daabe7SAndrew Geissler 
56554daabe7SAndrew Geissler                             // swInvPurpose is of format:
56654daabe7SAndrew Geissler                             // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
56754daabe7SAndrew Geissler                             // Translate this to "ABC update"
56854daabe7SAndrew Geissler                             size_t endDesc = swInvPurpose->rfind(".");
56954daabe7SAndrew Geissler                             if (endDesc == std::string::npos)
57054daabe7SAndrew Geissler                             {
57154daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
57254daabe7SAndrew Geissler                                 return;
57354daabe7SAndrew Geissler                             }
57454daabe7SAndrew Geissler                             endDesc++;
57554daabe7SAndrew Geissler                             if (endDesc >= swInvPurpose->size())
57654daabe7SAndrew Geissler                             {
57754daabe7SAndrew Geissler                                 messages::internalError(asyncResp->res);
57854daabe7SAndrew Geissler                                 return;
57954daabe7SAndrew Geissler                             }
58054daabe7SAndrew Geissler 
58154daabe7SAndrew Geissler                             std::string formatDesc =
58254daabe7SAndrew Geissler                                 swInvPurpose->substr(endDesc);
58354daabe7SAndrew Geissler                             asyncResp->res.jsonValue["Description"] =
58454daabe7SAndrew Geissler                                 formatDesc + " update";
58587d84729SAndrew Geissler                             getRelatedItems(asyncResp, *swInvPurpose);
5866c4eb9deSJennifer Lee                         },
587c711bf86SEd Tanous                         obj.second[0].first, obj.first,
588c711bf86SEd Tanous                         "org.freedesktop.DBus.Properties", "GetAll",
589c711bf86SEd Tanous                         "xyz.openbmc_project.Software.Version");
5906c4eb9deSJennifer Lee                 }
591c711bf86SEd Tanous             },
592c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper",
593c711bf86SEd Tanous             "/xyz/openbmc_project/object_mapper",
594c711bf86SEd Tanous             "xyz.openbmc_project.ObjectMapper", "GetSubTree",
595c711bf86SEd Tanous             "/xyz/openbmc_project/software", int32_t(1),
5961abe55efSEd Tanous             std::array<const char *, 1>{
5971abe55efSEd Tanous                 "xyz.openbmc_project.Software.Version"});
5986c4eb9deSJennifer Lee     }
599729dae72SJennifer Lee };
600729dae72SJennifer Lee 
601729dae72SJennifer Lee } // namespace redfish
602