blob: 9dacbcd922ccfda44fce0cfc2136f286d17296de [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<
James Feistfd9ab9e2020-05-19 13:48:07 -0700134 std::string,
135 std::variant<std::string, uint8_t>>
James Feist32898ce2020-03-10 16:16:52 -0700136 values;
James Feist32898ce2020-03-10 16:16:52 -0700137
James Feiste5d50062020-05-11 17:29:00 -0700138 std::string index =
139 std::to_string(taskData->index);
James Feistfd9ab9e2020-05-19 13:48:07 -0700140 msg.read(iface, values);
James Feiste5d50062020-05-11 17:29:00 -0700141
James Feistfd9ab9e2020-05-19 13:48:07 -0700142 if (iface == "xyz.openbmc_project.Software."
143 "Activation")
James Feist32898ce2020-03-10 16:16:52 -0700144 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700145 auto findActivation =
146 values.find("Activation");
147 if (findActivation == values.end())
148 {
149 return !task::completed;
150 }
151 std::string *state =
152 std::get_if<std::string>(
153 &(findActivation->second));
154
155 if (state == nullptr)
156 {
157 taskData->messages.emplace_back(
158 messages::internalError());
159 return task::completed;
160 }
161
162 if (boost::ends_with(*state,
163 "Invalid") ||
164 boost::ends_with(*state, "Failed"))
165 {
166 taskData->state = "Exception";
167 taskData->status = "Warning";
168 taskData->messages.emplace_back(
169 messages::taskAborted(index));
170 return task::completed;
171 }
172
173 if (boost::ends_with(*state, "Staged"))
174 {
175 taskData->state = "Stopping";
176 taskData->messages.emplace_back(
177 messages::taskPaused(index));
178
179 // its staged, set a long timer to
180 // allow them time to complete the
181 // update (probably cycle the
182 // system) if this expires then
183 // task will be cancelled
184 taskData->extendTimer(
185 std::chrono::hours(5));
186 return !task::completed;
187 }
188
189 if (boost::ends_with(*state, "Active"))
190 {
191 taskData->messages.emplace_back(
192 messages::taskCompletedOK(
193 index));
194 taskData->state = "Completed";
195 return task::completed;
196 }
James Feist32898ce2020-03-10 16:16:52 -0700197 }
James Feistfd9ab9e2020-05-19 13:48:07 -0700198 else if (iface ==
199 "xyz.openbmc_project.Software."
200 "ActivationProgress")
James Feist32898ce2020-03-10 16:16:52 -0700201 {
James Feistfd9ab9e2020-05-19 13:48:07 -0700202 auto findProgress =
203 values.find("Progress");
204 if (findProgress == values.end())
205 {
206 return !task::completed;
207 }
208 uint8_t *progress =
209 std::get_if<uint8_t>(
210 &(findProgress->second));
James Feist32898ce2020-03-10 16:16:52 -0700211
James Feistfd9ab9e2020-05-19 13:48:07 -0700212 if (progress == nullptr)
213 {
214 taskData->messages.emplace_back(
215 messages::internalError());
216 return task::completed;
217 }
James Feist32898ce2020-03-10 16:16:52 -0700218 taskData->messages.emplace_back(
James Feistfd9ab9e2020-05-19 13:48:07 -0700219 messages::taskProgressChanged(
220 index, static_cast<size_t>(
221 *progress)));
222
223 // if we're getting status updates it's
224 // still alive, update timer
225 taskData->extendTimer(
226 std::chrono::minutes(5));
James Feist32898ce2020-03-10 16:16:52 -0700227 }
228
229 // as firmware update often results in a
230 // reboot, the task may never "complete"
231 // unless it is an error
232
233 return !task::completed;
234 },
235 "type='signal',interface='org.freedesktop.DBus."
236 "Properties',"
James Feistfd9ab9e2020-05-19 13:48:07 -0700237 "member='PropertiesChanged',path='" +
James Feist32898ce2020-03-10 16:16:52 -0700238 objPath.str + "'");
239 task->startTimer(std::chrono::minutes(5));
240 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -0700241 task->payload.emplace(req);
Andrew Geissler0554c982019-04-23 14:40:12 -0500242 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500243 fwUpdateInProgress = false;
244 },
245 "xyz.openbmc_project.ObjectMapper",
246 "/xyz/openbmc_project/object_mapper",
247 "xyz.openbmc_project.ObjectMapper", "GetObject", objPath.str,
248 std::array<const char *, 1>{
249 "xyz.openbmc_project.Software.Activation"});
250 }
251 }
252}
253
Andrew Geissler0554c982019-04-23 14:40:12 -0500254// Note that asyncResp can be either a valid pointer or nullptr. If nullptr
255// then no asyncResp updates will occur
Andrew Geissler86adcd62019-04-18 10:58:05 -0500256static void monitorForSoftwareAvailable(std::shared_ptr<AsyncResp> asyncResp,
Andrew Geissler0554c982019-04-23 14:40:12 -0500257 const crow::Request &req,
258 int timeoutTimeSeconds = 5)
Andrew Geissler86adcd62019-04-18 10:58:05 -0500259{
260 // Only allow one FW update at a time
261 if (fwUpdateInProgress != false)
262 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500263 if (asyncResp)
264 {
Andrew Geissler0554c982019-04-23 14:40:12 -0500265 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
266 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500267 return;
268 }
269
Andrew Geissler0554c982019-04-23 14:40:12 -0500270 fwAvailableTimer =
Ed Tanous271584a2019-07-09 16:24:22 -0700271 std::make_unique<boost::asio::steady_timer>(*req.ioService);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500272
Ed Tanous271584a2019-07-09 16:24:22 -0700273 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
Andrew Geissler86adcd62019-04-18 10:58:05 -0500274
275 fwAvailableTimer->async_wait(
276 [asyncResp](const boost::system::error_code &ec) {
277 cleanUp();
278 if (ec == boost::asio::error::operation_aborted)
279 {
280 // expected, we were canceled before the timer completed.
281 return;
282 }
283 BMCWEB_LOG_ERROR
284 << "Timed out waiting for firmware object being created";
285 BMCWEB_LOG_ERROR
286 << "FW image may has already been uploaded to server";
287 if (ec)
288 {
289 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
290 return;
291 }
Andrew Geissler0554c982019-04-23 14:40:12 -0500292 if (asyncResp)
293 {
294 redfish::messages::internalError(asyncResp->res);
295 }
Andrew Geissler86adcd62019-04-18 10:58:05 -0500296 });
297
James Feistfe306722020-03-12 16:32:08 -0700298 auto callback = [asyncResp, req](sdbusplus::message::message &m) {
Andrew Geissler86adcd62019-04-18 10:58:05 -0500299 BMCWEB_LOG_DEBUG << "Match fired";
James Feistfe306722020-03-12 16:32:08 -0700300 softwareInterfaceAdded(asyncResp, m, req);
Andrew Geissler86adcd62019-04-18 10:58:05 -0500301 };
302
303 fwUpdateInProgress = true;
304
305 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
306 *crow::connections::systemBus,
307 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
308 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
309 callback);
310}
Jennifer Lee729dae72018-04-24 15:59:34 -0700311
Andrew Geissler0554c982019-04-23 14:40:12 -0500312/**
313 * UpdateServiceActionsSimpleUpdate class supports handle POST method for
314 * SimpleUpdate action.
315 */
316class UpdateServiceActionsSimpleUpdate : public Node
317{
318 public:
319 UpdateServiceActionsSimpleUpdate(CrowApp &app) :
320 Node(app,
321 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
322 {
323 entityPrivileges = {
324 {boost::beast::http::verb::get, {{"Login"}}},
325 {boost::beast::http::verb::head, {{"Login"}}},
326 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
327 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
328 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
329 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
330 }
331
332 private:
333 void doPost(crow::Response &res, const crow::Request &req,
334 const std::vector<std::string> &params) override
335 {
336 std::optional<std::string> transferProtocol;
337 std::string imageURI;
338 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
339
340 BMCWEB_LOG_DEBUG << "Enter UpdateService.SimpleUpdate doPost";
341
342 // User can pass in both TransferProtocol and ImageURI parameters or
343 // they can pass in just the ImageURI with the transfer protocl embedded
344 // within it.
345 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
346 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
347
348 if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
349 transferProtocol, "ImageURI", imageURI))
350 {
351 BMCWEB_LOG_DEBUG
352 << "Missing TransferProtocol or ImageURI parameter";
353 return;
354 }
355 if (!transferProtocol)
356 {
357 // Must be option 2
358 // Verify ImageURI has transfer protocol in it
359 size_t separator = imageURI.find(":");
360 if ((separator == std::string::npos) ||
361 ((separator + 1) > imageURI.size()))
362 {
363 messages::actionParameterValueTypeError(
364 asyncResp->res, imageURI, "ImageURI",
365 "UpdateService.SimpleUpdate");
366 BMCWEB_LOG_ERROR << "ImageURI missing transfer protocol: "
367 << imageURI;
368 return;
369 }
370 transferProtocol = imageURI.substr(0, separator);
371 // Ensure protocol is upper case for a common comparison path below
372 boost::to_upper(*transferProtocol);
373 BMCWEB_LOG_DEBUG << "Encoded transfer protocol "
374 << *transferProtocol;
375
376 // Adjust imageURI to not have the protocol on it for parsing
377 // below
378 // ex. tftp://1.1.1.1/myfile.bin -> 1.1.1.1/myfile.bin
379 imageURI = imageURI.substr(separator + 3);
380 BMCWEB_LOG_DEBUG << "Adjusted imageUri " << imageURI;
381 }
382
383 // OpenBMC currently only supports TFTP
384 if (*transferProtocol != "TFTP")
385 {
386 messages::actionParameterNotSupported(asyncResp->res,
387 "TransferProtocol",
388 "UpdateService.SimpleUpdate");
389 BMCWEB_LOG_ERROR << "Request incorrect protocol parameter: "
390 << *transferProtocol;
391 return;
392 }
393
394 // Format should be <IP or Hostname>/<file> for imageURI
395 size_t separator = imageURI.find("/");
396 if ((separator == std::string::npos) ||
397 ((separator + 1) > imageURI.size()))
398 {
399 messages::actionParameterValueTypeError(
400 asyncResp->res, imageURI, "ImageURI",
401 "UpdateService.SimpleUpdate");
402 BMCWEB_LOG_ERROR << "Invalid ImageURI: " << imageURI;
403 return;
404 }
405
406 std::string tftpServer = imageURI.substr(0, separator);
407 std::string fwFile = imageURI.substr(separator + 1);
408 BMCWEB_LOG_DEBUG << "Server: " << tftpServer + " File: " << fwFile;
409
410 // Setup callback for when new software detected
411 // Give TFTP 2 minutes to complete
412 monitorForSoftwareAvailable(nullptr, req, 120);
413
414 // TFTP can take up to 2 minutes depending on image size and
415 // connection speed. Return to caller as soon as the TFTP operation
416 // has been started. The callback above will ensure the activate
417 // is started once the download has completed
418 redfish::messages::success(asyncResp->res);
419
420 // Call TFTP service
421 crow::connections::systemBus->async_method_call(
422 [](const boost::system::error_code ec) {
423 if (ec)
424 {
425 // messages::internalError(asyncResp->res);
426 cleanUp();
427 BMCWEB_LOG_DEBUG << "error_code = " << ec;
428 BMCWEB_LOG_DEBUG << "error msg = " << ec.message();
429 }
430 else
431 {
432 BMCWEB_LOG_DEBUG << "Call to DownloaViaTFTP Success";
433 }
434 },
435 "xyz.openbmc_project.Software.Download",
436 "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP",
437 "DownloadViaTFTP", fwFile, tftpServer);
438
439 BMCWEB_LOG_DEBUG << "Exit UpdateService.SimpleUpdate doPost";
440 }
441};
442
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443class UpdateService : public Node
444{
445 public:
446 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
447 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 entityPrivileges = {
449 {boost::beast::http::verb::get, {{"Login"}}},
450 {boost::beast::http::verb::head, {{"Login"}}},
451 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
452 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
453 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
454 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700455 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700456
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 private:
458 void doGet(crow::Response &res, const crow::Request &req,
459 const std::vector<std::string> &params) override
460 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530461 std::shared_ptr<AsyncResp> aResp = std::make_shared<AsyncResp>(res);
462 res.jsonValue["@odata.type"] = "#UpdateService.v1_4_0.UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800463 res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
Ed Tanous0f74e642018-11-12 15:17:05 -0800464 res.jsonValue["Id"] = "UpdateService";
465 res.jsonValue["Description"] = "Service for Software Update";
466 res.jsonValue["Name"] = "Update Service";
467 res.jsonValue["HttpPushUri"] = "/redfish/v1/UpdateService";
468 // UpdateService cannot be disabled
469 res.jsonValue["ServiceEnabled"] = true;
470 res.jsonValue["FirmwareInventory"] = {
471 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Andrew Geissler0554c982019-04-23 14:40:12 -0500472#ifdef BMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE
473 // Update Actions object.
474 nlohmann::json &updateSvcSimpleUpdate =
475 res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
476 updateSvcSimpleUpdate["target"] =
477 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
478 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = {
479 "TFTP"};
480#endif
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530481 // Get the current ApplyTime value
482 crow::connections::systemBus->async_method_call(
483 [aResp](const boost::system::error_code ec,
484 const std::variant<std::string> &applyTime) {
485 if (ec)
486 {
487 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
488 messages::internalError(aResp->res);
489 return;
490 }
491
492 const std::string *s = std::get_if<std::string>(&applyTime);
493 if (s == nullptr)
494 {
495 return;
496 }
497 // Store the ApplyTime Value
498 if (*s == "xyz.openbmc_project.Software.ApplyTime."
499 "RequestedApplyTimes.Immediate")
500 {
501 aResp->res.jsonValue["HttpPushUriOptions"]
502 ["HttpPushUriApplyTime"]["ApplyTime"] =
503 "Immediate";
504 }
505 else if (*s == "xyz.openbmc_project.Software.ApplyTime."
506 "RequestedApplyTimes.OnReset")
507 {
508 aResp->res.jsonValue["HttpPushUriOptions"]
509 ["HttpPushUriApplyTime"]["ApplyTime"] =
510 "OnReset";
511 }
512 },
513 "xyz.openbmc_project.Settings",
514 "/xyz/openbmc_project/software/apply_time",
515 "org.freedesktop.DBus.Properties", "Get",
516 "xyz.openbmc_project.Software.ApplyTime", "RequestedApplyTime");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 }
Andrew Geissler0e7de462019-03-04 19:11:54 -0600518
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530519 void doPatch(crow::Response &res, const crow::Request &req,
520 const std::vector<std::string> &params) override
521 {
522 BMCWEB_LOG_DEBUG << "doPatch...";
523
524 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530525
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530526 std::optional<nlohmann::json> pushUriOptions;
527 if (!json_util::readJson(req, res, "HttpPushUriOptions",
528 pushUriOptions))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530529 {
530 return;
531 }
532
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530533 if (pushUriOptions)
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530534 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530535 std::optional<nlohmann::json> pushUriApplyTime;
536 if (!json_util::readJson(*pushUriOptions, res,
537 "HttpPushUriApplyTime", pushUriApplyTime))
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530538 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530539 return;
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530540 }
541
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530542 if (pushUriApplyTime)
543 {
544 std::optional<std::string> applyTime;
545 if (!json_util::readJson(*pushUriApplyTime, res, "ApplyTime",
546 applyTime))
547 {
548 return;
549 }
550
551 if (applyTime)
552 {
553 std::string applyTimeNewVal;
554 if (applyTime == "Immediate")
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530555 {
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530556 applyTimeNewVal =
557 "xyz.openbmc_project.Software.ApplyTime."
558 "RequestedApplyTimes.Immediate";
559 }
560 else if (applyTime == "OnReset")
561 {
562 applyTimeNewVal =
563 "xyz.openbmc_project.Software.ApplyTime."
564 "RequestedApplyTimes.OnReset";
565 }
566 else
567 {
568 BMCWEB_LOG_INFO
569 << "ApplyTime value is not in the list of "
570 "acceptable values";
571 messages::propertyValueNotInList(
572 asyncResp->res, *applyTime, "ApplyTime");
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530573 return;
574 }
Jayashankar Padath274dfe62019-08-23 12:30:57 +0530575
576 // Set the requested image apply time value
577 crow::connections::systemBus->async_method_call(
578 [asyncResp](const boost::system::error_code ec) {
579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "D-Bus responses error: "
582 << ec;
583 messages::internalError(asyncResp->res);
584 return;
585 }
586 messages::success(asyncResp->res);
587 },
588 "xyz.openbmc_project.Settings",
589 "/xyz/openbmc_project/software/apply_time",
590 "org.freedesktop.DBus.Properties", "Set",
591 "xyz.openbmc_project.Software.ApplyTime",
592 "RequestedApplyTime",
593 std::variant<std::string>{applyTimeNewVal});
594 }
595 }
Jayashankar Padathfa1a5a32019-05-28 23:54:37 +0530596 }
597 }
598
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 void doPost(crow::Response &res, const crow::Request &req,
600 const std::vector<std::string> &params) override
601 {
602 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -0700603
Andrew Geissler0e7de462019-03-04 19:11:54 -0600604 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605
Andrew Geissler86adcd62019-04-18 10:58:05 -0500606 // Setup callback for when new software detected
607 monitorForSoftwareAvailable(asyncResp, req);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700608
609 std::string filepath(
610 "/tmp/images/" +
611 boost::uuids::to_string(boost::uuids::random_generator()()));
612 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
613 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
614 std::ofstream::trunc);
615 out << req.body;
616 out.close();
617 BMCWEB_LOG_DEBUG << "file upload complete!!";
618 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700619};
Ed Tanousc711bf82018-07-30 16:31:33 -0700620
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621class SoftwareInventoryCollection : public Node
622{
623 public:
624 template <typename CrowApp>
625 SoftwareInventoryCollection(CrowApp &app) :
626 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
627 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 entityPrivileges = {
629 {boost::beast::http::verb::get, {{"Login"}}},
630 {boost::beast::http::verb::head, {{"Login"}}},
631 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
632 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
633 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
634 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700635 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700636
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 private:
638 void doGet(crow::Response &res, const crow::Request &req,
639 const std::vector<std::string> &params) override
640 {
641 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800642 res.jsonValue["@odata.type"] =
643 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
644 res.jsonValue["@odata.id"] =
645 "/redfish/v1/UpdateService/FirmwareInventory";
Ed Tanous0f74e642018-11-12 15:17:05 -0800646 res.jsonValue["Name"] = "Software Inventory Collection";
Ed Tanousc711bf82018-07-30 16:31:33 -0700647
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 crow::connections::systemBus->async_method_call(
649 [asyncResp](
650 const boost::system::error_code ec,
651 const std::vector<std::pair<
652 std::string, std::vector<std::pair<
653 std::string, std::vector<std::string>>>>>
654 &subtree) {
655 if (ec)
656 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700657 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700658 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 }
660 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
661 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700662
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 for (auto &obj : subtree)
664 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700665 // if can't parse fw id then return
Ed Tanous27826b52018-10-29 11:40:58 -0700666 std::size_t idPos;
667 if ((idPos = obj.first.rfind("/")) == std::string::npos)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700668 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700669 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700670 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
671 return;
672 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700673 std::string swId = obj.first.substr(idPos + 1);
674
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500675 nlohmann::json &members =
676 asyncResp->res.jsonValue["Members"];
677 members.push_back(
678 {{"@odata.id", "/redfish/v1/UpdateService/"
679 "FirmwareInventory/" +
680 swId}});
681 asyncResp->res.jsonValue["Members@odata.count"] =
682 members.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 }
684 },
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600685 // Note that only firmware levels associated with a device are
686 // stored under /xyz/openbmc_project/software therefore to ensure
687 // only real FirmwareInventory items are returned, this full object
688 // path must be used here as input to mapper
Ed Tanous1abe55e2018-09-05 08:30:59 -0700689 "xyz.openbmc_project.ObjectMapper",
690 "/xyz/openbmc_project/object_mapper",
Andrew Geissler2830a9c2020-01-06 10:18:11 -0600691 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
692 "/xyz/openbmc_project/software", static_cast<int32_t>(0),
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 std::array<const char *, 1>{
694 "xyz.openbmc_project.Software.Version"});
695 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700696};
697
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698class SoftwareInventory : public Node
699{
700 public:
701 template <typename CrowApp>
702 SoftwareInventory(CrowApp &app) :
703 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
704 std::string())
705 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 entityPrivileges = {
707 {boost::beast::http::verb::get, {{"Login"}}},
708 {boost::beast::http::verb::head, {{"Login"}}},
709 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
710 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
711 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
712 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
713 }
714
715 private:
Andrew Geissler87d84722019-02-28 14:28:39 -0600716 /* Fill related item links (i.e. bmc, bios) in for inventory */
717 static void getRelatedItems(std::shared_ptr<AsyncResp> aResp,
718 const std::string &purpose)
719 {
720 if (purpose == fw_util::bmcPurpose)
721 {
722 nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
723 members.push_back({{"@odata.id", "/redfish/v1/Managers/bmc"}});
724 aResp->res.jsonValue["Members@odata.count"] = members.size();
725 }
726 else if (purpose == fw_util::biosPurpose)
727 {
Gunnar Millsf723d732020-02-26 11:20:49 -0600728 nlohmann::json &members = aResp->res.jsonValue["RelatedItem"];
729 members.push_back(
730 {{"@odata.id", "/redfish/v1/Systems/system/Bios"}});
731 aResp->res.jsonValue["Members@odata.count"] = members.size();
Andrew Geissler87d84722019-02-28 14:28:39 -0600732 }
733 else
734 {
735 BMCWEB_LOG_ERROR << "Unknown software purpose " << purpose;
736 }
737 }
738
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 void doGet(crow::Response &res, const crow::Request &req,
740 const std::vector<std::string> &params) override
741 {
742 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743
744 if (params.size() != 1)
745 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700746 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 res.end();
748 return;
749 }
750
Ed Tanous3ae837c2018-08-07 14:41:19 -0700751 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 std::make_shared<std::string>(params[0]);
753
754 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700755 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756
757 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700758 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 const boost::system::error_code ec,
760 const std::vector<std::pair<
761 std::string, std::vector<std::pair<
762 std::string, std::vector<std::string>>>>>
763 &subtree) {
764 BMCWEB_LOG_DEBUG << "doGet callback...";
765 if (ec)
766 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700767 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 return;
769 }
770
Andrew Geissler69132282019-07-01 11:00:35 -0500771 // Ensure we find our input swId, otherwise return an error
772 bool found = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 for (const std::pair<
774 std::string,
775 std::vector<
776 std::pair<std::string, std::vector<std::string>>>>
777 &obj : subtree)
778 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700779 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700780 {
781 continue;
782 }
783
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700784 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
786 continue;
787 }
788
Andrew Geissler69132282019-07-01 11:00:35 -0500789 found = true;
Andrew Geisslere0dd8052019-06-18 16:05:10 -0500790 fw_util::getFwStatus(asyncResp, swId, obj.second[0].first);
791
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 crow::connections::systemBus->async_method_call(
793 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700794 swId](const boost::system::error_code error_code,
795 const boost::container::flat_map<
796 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 if (error_code)
798 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700799 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 return;
801 }
802 boost::container::flat_map<
803 std::string, VariantType>::const_iterator it =
804 propertiesList.find("Purpose");
805 if (it == propertiesList.end())
806 {
807 BMCWEB_LOG_DEBUG
808 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700809 messages::propertyMissing(asyncResp->res,
810 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 return;
812 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700813 const std::string *swInvPurpose =
Ed Tanousabf2add2019-01-22 16:40:12 -0800814 std::get_if<std::string>(&it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700815 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 {
817 BMCWEB_LOG_DEBUG
818 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700819 messages::propertyValueTypeError(asyncResp->res,
820 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 return;
822 }
823
Ed Tanous3ae837c2018-08-07 14:41:19 -0700824 BMCWEB_LOG_DEBUG << "swInvPurpose = "
825 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700826 it = propertiesList.find("Version");
827 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700829 BMCWEB_LOG_DEBUG
830 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700831 messages::propertyMissing(asyncResp->res,
832 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700833 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700835
836 BMCWEB_LOG_DEBUG << "Version found!";
837
838 const std::string *version =
Ed Tanousabf2add2019-01-22 16:40:12 -0800839 std::get_if<std::string>(&it->second);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700840
841 if (version == nullptr)
842 {
843 BMCWEB_LOG_DEBUG
844 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700845
846 messages::propertyValueTypeError(asyncResp->res,
847 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700848 return;
849 }
850 asyncResp->res.jsonValue["Version"] = *version;
851 asyncResp->res.jsonValue["Id"] = *swId;
Andrew Geissler54daabe2019-02-13 13:54:15 -0600852
853 // swInvPurpose is of format:
854 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
James Feiste2e96772019-10-03 10:51:43 -0700855 // Translate this to "ABC image"
Andrew Geissler54daabe2019-02-13 13:54:15 -0600856 size_t endDesc = swInvPurpose->rfind(".");
857 if (endDesc == std::string::npos)
858 {
859 messages::internalError(asyncResp->res);
860 return;
861 }
862 endDesc++;
863 if (endDesc >= swInvPurpose->size())
864 {
865 messages::internalError(asyncResp->res);
866 return;
867 }
868
869 std::string formatDesc =
870 swInvPurpose->substr(endDesc);
871 asyncResp->res.jsonValue["Description"] =
James Feiste2e96772019-10-03 10:51:43 -0700872 formatDesc + " image";
Andrew Geissler87d84722019-02-28 14:28:39 -0600873 getRelatedItems(asyncResp, *swInvPurpose);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 },
875 obj.second[0].first, obj.first,
876 "org.freedesktop.DBus.Properties", "GetAll",
877 "xyz.openbmc_project.Software.Version");
878 }
Andrew Geissler69132282019-07-01 11:00:35 -0500879 if (!found)
880 {
881 BMCWEB_LOG_ERROR << "Input swID " + *swId + " not found!";
882 messages::resourceMissingAtURI(
883 asyncResp->res,
884 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId);
885 return;
886 }
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530887 asyncResp->res.jsonValue["@odata.type"] =
888 "#SoftwareInventory.v1_1_0.SoftwareInventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530889 asyncResp->res.jsonValue["Name"] = "Software Inventory";
Ayushi Smriti4e68c452019-09-04 14:37:55 +0530890 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK";
AppaRao Puli3f8a7432020-01-29 02:36:32 +0530891
892 asyncResp->res.jsonValue["Updateable"] = false;
893 fw_util::getFwUpdateableStatus(asyncResp, swId);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 },
895 "xyz.openbmc_project.ObjectMapper",
896 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700897 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/",
898 static_cast<int32_t>(0),
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 std::array<const char *, 1>{
900 "xyz.openbmc_project.Software.Version"});
901 }
902};
903
904} // namespace redfish