blob: e9793ebe705b97c747af3812296c5669757a56e5 [file] [log] [blame]
Jennifer Lee729dae72018-04-24 15:59:34 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -070019
Jennifer Lee729dae72018-04-24 15:59:34 -070020#include <boost/container/flat_map.hpp>
Andrew Geissler87d84722019-02-28 14:28:39 -060021#include <utils/fw_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080022#include <variant>
Jennifer Lee729dae72018-04-24 15:59:34 -070023
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Ed Tanous27826b52018-10-29 11:40:58 -070026
Andrew Geissler0e7de462019-03-04 19:11:54 -060027// Match signals added on software path
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070028static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
Andrew Geissler0e7de462019-03-04 19:11:54 -060029// Only allow one update at a time
30static bool fwUpdateInProgress = false;
Andrew Geissler86adcd62019-04-18 10:58:05 -050031// Timer for software available
Ed Tanous271584a2019-07-09 16:24:22 -070032static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
Andrew Geissler86adcd62019-04-18 10:58:05 -050033
34static void cleanUp()
35{
36 fwUpdateInProgress = false;
37 fwUpdateMatcher = nullptr;
38}
39static void activateImage(const std::string &objPath,
40 const std::string &service)
41{
42 BMCWEB_LOG_DEBUG << "Activate image for " << objPath << " " << service;
43 crow::connections::systemBus->async_method_call(
44 [](const boost::system::error_code error_code) {
45 if (error_code)
46 {
47 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
48 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
49 }
50 },
51 service, objPath, "org.freedesktop.DBus.Properties", "Set",
52 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
53 std::variant<std::string>(
54 "xyz.openbmc_project.Software.Activation.RequestedActivations."
55 "Active"));
56}
Andrew Geissler0554c982019-04-23 14:40:12 -050057
58// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
59// then no asyncResp updates will occur
Andrew Geissler86adcd62019-04-18 10:58:05 -050060static void softwareInterfaceAdded(std::shared_ptr<AsyncResp> asyncResp,
James Feistfe306722020-03-12 16:32:08 -070061 sdbusplus::message::message &m,
62 const crow::Request &req)
Andrew Geissler86adcd62019-04-18 10:58:05 -050063{
64 std::vector<std::pair<
65 std::string,
66 std::vector<std::pair<std::string, std::variant<std::string>>>>>
67 interfacesProperties;
68
69 sdbusplus::message::object_path objPath;
70
71 m.read(objPath, interfacesProperties);
72
73 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str;
74 for (auto &interface : interfacesProperties)
75 {
76 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
77
78 if (interface.first == "xyz.openbmc_project.Software.Activation")
79 {
80 // Found our interface, disable callbacks
81 fwUpdateMatcher = nullptr;
82
83 // Retrieve service and activate
84 crow::connections::systemBus->async_method_call(
James Feistfe306722020-03-12 16:32:08 -070085 [objPath, asyncResp,
86 req](const boost::system::error_code error_code,
87 const std::vector<std::pair<
88 std::string, std::vector<std::string>>> &objInfo) {
Andrew Geissler86adcd62019-04-18 10:58:05 -050089 if (error_code)
90 {
91 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
92 BMCWEB_LOG_DEBUG << "error msg = "
93 << error_code.message();
Andrew Geissler0554c982019-04-23 14:40:12 -050094 if (asyncResp)
95 {
96 messages::internalError(asyncResp->res);
97 }
Andrew Geissler86adcd62019-04-18 10:58:05 -050098 cleanUp();
99 return;
100 }
101 // Ensure we only got one service back
102 if (objInfo.size() != 1)
103 {
104 BMCWEB_LOG_ERROR << "Invalid Object Size "
105 << objInfo.size();
Andrew Geissler0554c982019-04-23 14:40:12 -0500106 if (asyncResp)
107 {
108 messages::internalError(asyncResp->res);
109 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500110 cleanUp();
111 return;
112 }
113 // cancel timer only when
114 // xyz.openbmc_project.Software.Activation interface
115 // is added
116 fwAvailableTimer = nullptr;
117
118 activateImage(objPath.str, objInfo[0].first);
Andrew Geissler0554c982019-04-23 14:40:12 -0500119 if (asyncResp)
120 {
James Feist32898ce2020-03-10 16:16:52 -0700121 std::shared_ptr<task::TaskData> task =
122 task::TaskData::createTask(
123 [](boost::system::error_code ec,
124 sdbusplus::message::message &msg,
125 const std::shared_ptr<task::TaskData>
126 &taskData) {
127 if (ec)
128 {
129 return task::completed;
130 }
131
132 std::string iface;
133 boost::container::flat_map<
134 std::string, std::variant<std::string>>
135 values;
136 msg.read(iface, values);
137 auto findActivation =
138 values.find("Activation");
139 if (findActivation == values.end())
140 {
141 return !task::completed;
142 }
143 std::string *state =
144 std::get_if<std::string>(
145 &(findActivation->second));
146
147 if (state == nullptr)
148 {
149 taskData->messages.emplace_back(
150 messages::internalError());
151 return task::completed;
152 }
153
154 if (boost::ends_with(*state, "Invalid") ||
155 boost::ends_with(*state, "Failed"))
156 {
157 taskData->state = "Exception";
158 taskData->status = "Warning";
159 taskData->messages.emplace_back(
160 messages::invalidObject(
161 "/redfish/v1/UpdateService/"));
162 return task::completed;
163 }
164
165 if (boost::ends_with(*state, "Staged"))
166 {
167 taskData->state = "Pending";
168 return !task::completed;
169 }
170
171 if (boost::ends_with(*state, "Active"))
172 {
173 taskData->messages.emplace_back(
174 messages::success());
175 taskData->state = "Completed";
176 return task::completed;
177 }
178
179 // as firmware update often results in a
180 // reboot, the task may never "complete"
181 // unless it is an error
182
183 return !task::completed;
184 },
185 "type='signal',interface='org.freedesktop.DBus."
186 "Properties',"
187 "member='PropertiesChanged',arg0='xyz.openbmc_"
188 "project.Software.Activation',path='" +
189 objPath.str + "'");
190 task->startTimer(std::chrono::minutes(5));
191 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -0700192 task->payload.emplace(req);
Andrew Geissler0554c982019-04-23 14:40:12 -0500193 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500194 fwUpdateInProgress = false;
195 },
196 "xyz.openbmc_project.ObjectMapper",
197 "/xyz/openbmc_project/object_mapper",
198 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
199 std::array<const char *, 1>{
200 "xyz.openbmc_project.Software.Activation"});
201 }
202 }
203}
204
Andrew Geissler0554c982019-04-23 14:40:12 -0500205// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
206// then no asyncResp updates will occur
Andrew Geissler86adcd62019-04-18 10:58:05 -0500207static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
Andrew Geissler0554c982019-04-23 14:40:12 -0500208 const crow::Request &req,
209 int timeoutTimeSeconds = 5)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500210{
211 // Only allow one FW update at a time
212 if (fwUpdateInProgress != false)
213 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500214 if (asyncResp)
215 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500216 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
217 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500218 return;
219 }
220
Andrew Geissler0554c982019-04-23 14:40:12 -0500221 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700222 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500223
Ed Tanous271584a2019-07-09 16:24:22 -0700224 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500225
226 fwAvailableTimer->async_wait(
227 [asyncResp](const boost::system::error_code &ec) {
228 cleanUp();
229 if (ec == boost::asio::error::operation_aborted)
230 {
231 // expected, we were canceled before the timer completed.
232 return;
233 }
234 BMCWEB_LOG_ERROR
235 << "Timed out waiting for firmware object being created";
236 BMCWEB_LOG_ERROR
237 << "FW image may has already been uploaded to server";
238 if (ec)
239 {
240 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
241 return;
242 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500243 if (asyncResp)
244 {
245 redfish::messages::internalError(asyncResp->res);
246 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500247 });
248
James Feistfe306722020-03-12 16:32:08 -0700249 auto callback = [asyncResp, req](sdbusplus::message::message &m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500250 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700251 softwareInterfaceAdded(asyncResp, m, req);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500252 };
253
254 fwUpdateInProgress = true;
255
256 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
257 *crow::connections::systemBus,
258 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
259 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
260 callback);
261}
Jennifer Lee729dae72018-04-24 15:59:34 -0700262
Andrew Geissler0554c982019-04-23 14:40:12 -0500263/**
264 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
265 * SimpleUpdate action.
266 */
267class UpdateServiceActionsSimpleUpdate : public Node
268{
269 public:
270 UpdateServiceActionsSimpleUpdate(CrowApp &app) :
271 Node(app,
272 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
273 {
274 entityPrivileges = {
275 {boost::beast::http::verb::get, {{"Login"}}},
276 {boost::beast::http::verb::head, {{"Login"}}},
277 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
278 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
279 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
280 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
281 }
282
283 private:
284 void doPost(crow::Response &res, const crow::Request &req,
285 const std::vector<std::string> &params) override
286 {
287 std::optional<std::string> transferProtocol;
288 std::string imageURI;
289 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
290
291 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
292
293 // User can pass in both TransferProtocol and ImageURI parameters or
294 // they can pass in just the ImageURI with the transfer protocl embedded
295 // within it.
296 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
297 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
298
299 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
300 transferProtocol, "ImageURI", imageURI))
301 {
302 BMCWEB_LOG_DEBUG
303 << "Missing TransferProtocol or ImageURI parameter";
304 return;
305 }
306 if (!transferProtocol)
307 {
308 // Must be option 2
309 // Verify ImageURI has transfer protocol in it
310 size_t separator = imageURI.find(":");
311 if ((separator == std::string::npos) ||
312 ((separator + 1) > imageURI.size()))
313 {
314 messages::actionParameterValueTypeError(
315 asyncResp->res, imageURI, "ImageURI",
316 "UpdateService.SimpleUpdate");
317 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
318 << imageURI;
319 return;
320 }
321 transferProtocol = imageURI.substr(0, separator);
322 // Ensure protocol is upper case for a common comparison path below
323 boost::to_upper(*transferProtocol);
324 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
325 << *transferProtocol;
326
327 // Adjust imageURI to not have the protocol on it for parsing
328 // below
329 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
330 imageURI = imageURI.substr(separator + 3);
331 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
332 }
333
334 // OpenBMC currently only supports TFTP
335 if (*transferProtocol != "TFTP")
336 {
337 messages::actionParameterNotSupported(asyncResp->res,
338 "TransferProtocol",
339 "UpdateService.SimpleUpdate");
340 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
341 << *transferProtocol;
342 return;
343 }
344
345 // Format should be <IP or Hostname>/<file> for imageURI
346 size_t separator = imageURI.find("/");
347 if ((separator == std::string::npos) ||
348 ((separator + 1) > imageURI.size()))
349 {
350 messages::actionParameterValueTypeError(
351 asyncResp->res, imageURI, "ImageURI",
352 "UpdateService.SimpleUpdate");
353 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
354 return;
355 }
356
357 std::string tftpServer = imageURI.substr(0, separator);
358 std::string fwFile = imageURI.substr(separator + 1);
359 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
360
361 // Setup callback for when new software detected
362 // Give TFTP 2 minutes to complete
363 monitorForSoftwareAvailable(nullptr, req, 120);
364
365 // TFTP can take up to 2 minutes depending on image size and
366 // connection speed. Return to caller as soon as the TFTP operation
367 // has been started. The callback above will ensure the activate
368 // is started once the download has completed
369 redfish::messages::success(asyncResp->res);
370
371 // Call TFTP service
372 crow::connections::systemBus->async_method_call(
373 [](const boost::system::error_code ec) {
374 if (ec)
375 {
376 // messages::internalError(asyncResp->res);
377 cleanUp();
378 BMCWEB_LOG_DEBUG << "error_code = " << ec;
379 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
380 }
381 else
382 {
383 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
384 }
385 },
386 "xyz.openbmc_project.Software.Download",
387 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
388 "DownloadViaTFTP", fwFile, tftpServer);
389
390 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
391 }
392};
393
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394class UpdateService : public Node
395{
396 public:
397 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
398 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 entityPrivileges = {
400 {boost::beast::http::verb::get, {{"Login"}}},
401 {boost::beast::http::verb::head, {{"Login"}}},
402 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
403 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
404 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
405 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700406 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700407
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 private:
409 void doGet(crow::Response &res, const crow::Request &req,
410 const std::vector<std::string> &params) override
411 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530412 std::shared_ptr<AsyncResp> aResp = std::make_shared<AsyncResp>(res);
413 res.jsonValue["@odata.type"] = "#UpdateService.v1_4_0.UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800414 res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800415 res.jsonValue["Id"] = "UpdateService";
416 res.jsonValue["Description"] = "Service for Software Update";
417 res.jsonValue["Name"] = "Update Service";
418 res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
419 // UpdateService cannot be disabled
420 res.jsonValue["ServiceEnabled"] = true;
421 res.jsonValue["FirmwareInventory"] = {
422 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Andrew Geissler0554c982019-04-23 14:40:12 -0500423#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
424 // Update Actions object.
425 nlohmann::json &updateSvcSimpleUpdate =
426 res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
427 updateSvcSimpleUpdate["target"] =
428 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
429 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
430 "TFTP"};
431#endif
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530432 // Get the current ApplyTime value
433 crow::connections::systemBus->async_method_call(
434 [aResp](const boost::system::error_code ec,
435 const std::variant<std::string> &applyTime) {
436 if (ec)
437 {
438 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
439 messages::internalError(aResp->res);
440 return;
441 }
442
443 const std::string *s = std::get_if<std::string>(&applyTime);
444 if (s == nullptr)
445 {
446 return;
447 }
448 // Store the ApplyTime Value
449 if (*s == "xyz.openbmc_project.Software.ApplyTime."
450 "RequestedApplyTimes.Immediate")
451 {
452 aResp->res.jsonValue["HttpPushUriOptions"]
453 ["HttpPushUriApplyTime"]["ApplyTime"] =
454 "Immediate";
455 }
456 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
457 "RequestedApplyTimes.OnReset")
458 {
459 aResp->res.jsonValue["HttpPushUriOptions"]
460 ["HttpPushUriApplyTime"]["ApplyTime"] =
461 "OnReset";
462 }
463 },
464 "xyz.openbmc_project.Settings",
465 "/xyz/openbmc_project/software/apply_time",
466 "org.freedesktop.DBus.Properties", "Get",
467 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 }
Andrew Geissler0e7de462019-03-04 19:11:54 -0600469
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530470 void doPatch(crow::Response &res, const crow::Request &req,
471 const std::vector<std::string> &params) override
472 {
473 BMCWEB_LOG_DEBUG << "doPatch...";
474
475 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530476
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530477 std::optional<nlohmann::json> pushUriOptions;
478 if (!json_util::readJson(req, res, "HttpPushUriOptions",
479 pushUriOptions))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530480 {
481 return;
482 }
483
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530484 if (pushUriOptions)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530485 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530486 std::optional<nlohmann::json> pushUriApplyTime;
487 if (!json_util::readJson(*pushUriOptions, res,
488 "HttpPushUriApplyTime", pushUriApplyTime))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530489 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530490 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530491 }
492
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530493 if (pushUriApplyTime)
494 {
495 std::optional<std::string> applyTime;
496 if (!json_util::readJson(*pushUriApplyTime, res, "ApplyTime",
497 applyTime))
498 {
499 return;
500 }
501
502 if (applyTime)
503 {
504 std::string applyTimeNewVal;
505 if (applyTime == "Immediate")
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530506 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530507 applyTimeNewVal =
508 "xyz.openbmc_project.Software.ApplyTime."
509 "RequestedApplyTimes.Immediate";
510 }
511 else if (applyTime == "OnReset")
512 {
513 applyTimeNewVal =
514 "xyz.openbmc_project.Software.ApplyTime."
515 "RequestedApplyTimes.OnReset";
516 }
517 else
518 {
519 BMCWEB_LOG_INFO
520 << "ApplyTime value is not in the list of "
521 "acceptable values";
522 messages::propertyValueNotInList(
523 asyncResp->res, *applyTime, "ApplyTime");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530524 return;
525 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530526
527 // Set the requested image apply time value
528 crow::connections::systemBus->async_method_call(
529 [asyncResp](const boost::system::error_code ec) {
530 if (ec)
531 {
532 BMCWEB_LOG_ERROR << "D-Bus responses error: "
533 << ec;
534 messages::internalError(asyncResp->res);
535 return;
536 }
537 messages::success(asyncResp->res);
538 },
539 "xyz.openbmc_project.Settings",
540 "/xyz/openbmc_project/software/apply_time",
541 "org.freedesktop.DBus.Properties", "Set",
542 "xyz.openbmc_project.Software.ApplyTime",
543 "RequestedApplyTime",
544 std::variant<std::string>{applyTimeNewVal});
545 }
546 }
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530547 }
548 }
549
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 void doPost(crow::Response &res, const crow::Request &req,
551 const std::vector<std::string> &params) override
552 {
553 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700554
Andrew Geissler0e7de462019-03-04 19:11:54 -0600555 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556
Andrew Geissler86adcd62019-04-18 10:58:05 -0500557 // Setup callback for when new software detected
558 monitorForSoftwareAvailable(asyncResp, req);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700559
560 std::string filepath(
561 "/tmp/images/" +
562 boost::uuids::to_string(boost::uuids::random_generator()()));
563 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
564 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
565 std::ofstream::trunc);
566 out << req.body;
567 out.close();
568 BMCWEB_LOG_DEBUG << "file upload complete!!";
569 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700570};
Ed Tanousc711bf82018-07-30 16:31:33 -0700571
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572class SoftwareInventoryCollection : public Node
573{
574 public:
575 template <typename CrowApp>
576 SoftwareInventoryCollection(CrowApp &app) :
577 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
578 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 entityPrivileges = {
580 {boost::beast::http::verb::get, {{"Login"}}},
581 {boost::beast::http::verb::head, {{"Login"}}},
582 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
583 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
584 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
585 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700586 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700587
Ed Tanous1abe55e2018-09-05 08:30:59 -0700588 private:
589 void doGet(crow::Response &res, const crow::Request &req,
590 const std::vector<std::string> &params) override
591 {
592 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800593 res.jsonValue["@odata.type"] =
594 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
595 res.jsonValue["@odata.id"] =
596 "/redfish/v1/UpdateService/FirmwareInventory";
Ed Tanous0f74e642018-11-12 15:17:05 -0800597 res.jsonValue["Name"] = "Software Inventory Collection";
Ed Tanousc711bf82018-07-30 16:31:33 -0700598
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 crow::connections::systemBus->async_method_call(
600 [asyncResp](
601 const boost::system::error_code ec,
602 const std::vector<std::pair<
603 std::string, std::vector<std::pair<
604 std::string, std::vector<std::string>>>>>
605 &subtree) {
606 if (ec)
607 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700608 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700609 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 }
611 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
612 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700613
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 for (auto &obj : subtree)
615 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700616 // if can't parse fw id then return
Ed Tanous27826b52018-10-29 11:40:58 -0700617 std::size_t idPos;
618 if ((idPos = obj.first.rfind("/")) == std::string::npos)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700619 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700620 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700621 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
622 return;
623 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700624 std::string swId = obj.first.substr(idPos + 1);
625
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500626 nlohmann::json &members =
627 asyncResp->res.jsonValue["Members"];
628 members.push_back(
629 {{"@odata.id", "/redfish/v1/UpdateService/"
630 "FirmwareInventory/" +
631 swId}});
632 asyncResp->res.jsonValue["Members@odata.count"] =
633 members.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 }
635 },
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600636 // Note that only firmware levels associated with a device are
637 // stored under /xyz/openbmc_project/software therefore to ensure
638 // only real FirmwareInventory items are returned, this full object
639 // path must be used here as input to mapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 "xyz.openbmc_project.ObjectMapper",
641 "/xyz/openbmc_project/object_mapper",
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600642 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
643 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 std::array<const char *, 1>{
645 "xyz.openbmc_project.Software.Version"});
646 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700647};
648
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649class SoftwareInventory : public Node
650{
651 public:
652 template <typename CrowApp>
653 SoftwareInventory(CrowApp &app) :
654 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
655 std::string())
656 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 entityPrivileges = {
658 {boost::beast::http::verb::get, {{"Login"}}},
659 {boost::beast::http::verb::head, {{"Login"}}},
660 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
661 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
662 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
663 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
664 }
665
666 private:
Andrew Geissler87d84722019-02-28 14:28:39 -0600667 /* Fill related item links (i.e. bmc, bios) in for inventory */
668 static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
669 const std::string &purpose)
670 {
671 if (purpose == fw_util::bmcPurpose)
672 {
673 nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
674 members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
675 aResp->res.jsonValue["Members@odata.count"] = members.size();
676 }
677 else if (purpose == fw_util::biosPurpose)
678 {
Gunnar Millsf723d732020-02-26 11:20:49 -0600679 nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
680 members.push_back(
681 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
682 aResp->res.jsonValue["Members@odata.count"] = members.size();
Andrew Geissler87d84722019-02-28 14:28:39 -0600683 }
684 else
685 {
686 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
687 }
688 }
689
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 void doGet(crow::Response &res, const crow::Request &req,
691 const std::vector<std::string> &params) override
692 {
693 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694
695 if (params.size() != 1)
696 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700697 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 res.end();
699 return;
700 }
701
Ed Tanous3ae837c2018-08-07 14:41:19 -0700702 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 std::make_shared<std::string>(params[0]);
704
705 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700706 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707
708 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700709 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 const boost::system::error_code ec,
711 const std::vector<std::pair<
712 std::string, std::vector<std::pair<
713 std::string, std::vector<std::string>>>>>
714 &subtree) {
715 BMCWEB_LOG_DEBUG << "doGet callback...";
716 if (ec)
717 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700718 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 return;
720 }
721
Andrew Geissler69132282019-07-01 11:00:35 -0500722 // Ensure we find our input swId, otherwise return an error
723 bool found = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700724 for (const std::pair<
725 std::string,
726 std::vector<
727 std::pair<std::string, std::vector<std::string>>>>
728 &obj : subtree)
729 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700730 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
732 continue;
733 }
734
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700735 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 {
737 continue;
738 }
739
Andrew Geissler69132282019-07-01 11:00:35 -0500740 found = true;
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500741 fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
742
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 crow::connections::systemBus->async_method_call(
744 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700745 swId](const boost::system::error_code error_code,
746 const boost::container::flat_map<
747 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 if (error_code)
749 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700750 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 return;
752 }
753 boost::container::flat_map<
754 std::string, VariantType>::const_iterator it =
755 propertiesList.find("Purpose");
756 if (it == propertiesList.end())
757 {
758 BMCWEB_LOG_DEBUG
759 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700760 messages::propertyMissing(asyncResp->res,
761 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762 return;
763 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700764 const std::string *swInvPurpose =
Ed Tanousabf2add2019-01-22 16:40:12 -0800765 std::get_if<std::string>(&it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700766 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 {
768 BMCWEB_LOG_DEBUG
769 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700770 messages::propertyValueTypeError(asyncResp->res,
771 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 return;
773 }
774
Ed Tanous3ae837c2018-08-07 14:41:19 -0700775 BMCWEB_LOG_DEBUG << "swInvPurpose = "
776 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700777 it = propertiesList.find("Version");
778 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700780 BMCWEB_LOG_DEBUG
781 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700782 messages::propertyMissing(asyncResp->res,
783 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700784 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700786
787 BMCWEB_LOG_DEBUG << "Version found!";
788
789 const std::string *version =
Ed Tanousabf2add2019-01-22 16:40:12 -0800790 std::get_if<std::string>(&it->second);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700791
792 if (version == nullptr)
793 {
794 BMCWEB_LOG_DEBUG
795 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700796
797 messages::propertyValueTypeError(asyncResp->res,
798 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700799 return;
800 }
801 asyncResp->res.jsonValue["Version"] = *version;
802 asyncResp->res.jsonValue["Id"] = *swId;
Andrew Geissler54daabe2019-02-13 13:54:15 -0600803
804 // swInvPurpose is of format:
805 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
James Feiste2e96772019-10-03 10:51:43 -0700806 // Translate this to "ABC image"
Andrew Geissler54daabe2019-02-13 13:54:15 -0600807 size_t endDesc = swInvPurpose->rfind(".");
808 if (endDesc == std::string::npos)
809 {
810 messages::internalError(asyncResp->res);
811 return;
812 }
813 endDesc++;
814 if (endDesc >= swInvPurpose->size())
815 {
816 messages::internalError(asyncResp->res);
817 return;
818 }
819
820 std::string formatDesc =
821 swInvPurpose->substr(endDesc);
822 asyncResp->res.jsonValue["Description"] =
James Feiste2e96772019-10-03 10:51:43 -0700823 formatDesc + " image";
Andrew Geissler87d84722019-02-28 14:28:39 -0600824 getRelatedItems(asyncResp, *swInvPurpose);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 },
826 obj.second[0].first, obj.first,
827 "org.freedesktop.DBus.Properties", "GetAll",
828 "xyz.openbmc_project.Software.Version");
829 }
Andrew Geissler69132282019-07-01 11:00:35 -0500830 if (!found)
831 {
832 BMCWEB_LOG_ERROR << "Input swID " + *swId + " not found!";
833 messages::resourceMissingAtURI(
834 asyncResp->res,
835 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId);
836 return;
837 }
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530838 asyncResp->res.jsonValue["@odata.type"] =
839 "#SoftwareInventory.v1_1_0.SoftwareInventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530840 asyncResp->res.jsonValue["Name"] = "Software Inventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530841 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
AppaRao Puli3f8a7432020-01-29 02:36:32 +0530842
843 asyncResp->res.jsonValue["Updateable"] = false;
844 fw_util::getFwUpdateableStatus(asyncResp, swId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 },
846 "xyz.openbmc_project.ObjectMapper",
847 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700848 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
849 static_cast<int32_t>(0),
Ed Tanous1abe55e2018-09-05 08:30:59 -0700850 std::array<const char *, 1>{
851 "xyz.openbmc_project.Software.Version"});
852 }
853};
854
855} // namespace redfish