blob: e4d57989617c23db494949bb2ba28d093541d349 [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>
21
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace redfish
23{
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070024static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
Jennifer Lee729dae72018-04-24 15:59:34 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026class UpdateService : public Node
27{
28 public:
29 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
30 {
31 Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
32 Node::json["@odata.id"] = "/redfish/v1/UpdateService";
33 Node::json["@odata.context"] =
34 "/redfish/v1/$metadata#UpdateService.UpdateService";
35 Node::json["Id"] = "UpdateService";
36 Node::json["Description"] = "Service for Software Update";
37 Node::json["Name"] = "Update Service";
38 Node::json["HttpPushUri"] = "/redfish/v1/UpdateService";
39 // UpdateService cannot be disabled
40 Node::json["ServiceEnabled"] = true;
41 Node::json["FirmwareInventory"] = {
42 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Jennifer Lee729dae72018-04-24 15:59:34 -070043
Ed Tanous1abe55e2018-09-05 08:30:59 -070044 entityPrivileges = {
45 {boost::beast::http::verb::get, {{"Login"}}},
46 {boost::beast::http::verb::head, {{"Login"}}},
47 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
48 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
49 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
50 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070051 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070052
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 private:
54 void doGet(crow::Response &res, const crow::Request &req,
55 const std::vector<std::string> &params) override
56 {
57 res.jsonValue = Node::json;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070058 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -070059 }
60 static void activateImage(const std::string &objPath)
61 {
62 crow::connections::systemBus->async_method_call(
63 [objPath](const boost::system::error_code error_code) {
64 if (error_code)
65 {
66 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
67 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
68 }
69 },
70 "xyz.openbmc_project.Software.BMC.Updater", objPath,
71 "org.freedesktop.DBus.Properties", "Set",
72 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
73 sdbusplus::message::variant<std::string>(
74 "xyz.openbmc_project.Software.Activation.RequestedActivations."
75 "Active"));
76 }
77 void doPost(crow::Response &res, const crow::Request &req,
78 const std::vector<std::string> &params) override
79 {
80 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070081
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 // Only allow one FW update at a time
83 if (fwUpdateMatcher != nullptr)
84 {
85 res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -070086 messages::serviceTemporarilyUnavailable(res, "3");
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 res.end();
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070088 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 }
90 // Make this const static so it survives outside this method
91 static boost::asio::deadline_timer timeout(
92 *req.ioService, boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070093
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 timeout.expires_from_now(boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070095
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 timeout.async_wait([&res](const boost::system::error_code &ec) {
97 fwUpdateMatcher = nullptr;
98 if (ec == boost::asio::error::operation_aborted)
99 {
100 // expected, we were canceled before the timer completed.
101 return;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700102 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 BMCWEB_LOG_ERROR
104 << "Timed out waiting for firmware object being created";
105 BMCWEB_LOG_ERROR
106 << "FW image may has already been uploaded to server";
107 if (ec)
108 {
109 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
110 return;
111 }
112
Jason M. Billsf12894f2018-10-09 12:45:45 -0700113 redfish::messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 res.end();
115 });
116
117 auto callback = [&res](sdbusplus::message::message &m) {
118 BMCWEB_LOG_DEBUG << "Match fired";
119 bool flag = false;
120
121 if (m.is_method_error())
122 {
123 BMCWEB_LOG_DEBUG << "Dbus method error!!!";
124 res.end();
125 return;
126 }
127 std::vector<std::pair<
128 std::string,
129 std::vector<std::pair<
130 std::string, sdbusplus::message::variant<std::string>>>>>
Ed Tanous3ae837c2018-08-07 14:41:19 -0700131 interfacesProperties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132
133 sdbusplus::message::object_path objPath;
134
Ed Tanous3ae837c2018-08-07 14:41:19 -0700135 m.read(objPath, interfacesProperties); // Read in the object path
136 // that was just created
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 // std::string str_objpath = objPath.str; // keep a copy for
138 // constructing response message
139 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
Ed Tanous3ae837c2018-08-07 14:41:19 -0700140 for (auto &interface : interfacesProperties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 {
142 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
143
144 if (interface.first ==
145 "xyz.openbmc_project.Software.Activation")
146 {
147 // cancel timer only when
148 // xyz.openbmc_project.Software.Activation interface is
149 // added
150 boost::system::error_code ec;
151 timeout.cancel(ec);
152 if (ec)
153 {
154 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
155 }
156 UpdateService::activateImage(objPath.str); // str_objpath);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700157 redfish::messages::success(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 BMCWEB_LOG_DEBUG << "ending response";
159 res.end();
160 fwUpdateMatcher = nullptr;
161 }
162 }
163 };
164
165 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
166 *crow::connections::systemBus,
167 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
168 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
169 callback);
170
171 std::string filepath(
172 "/tmp/images/" +
173 boost::uuids::to_string(boost::uuids::random_generator()()));
174 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
175 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
176 std::ofstream::trunc);
177 out << req.body;
178 out.close();
179 BMCWEB_LOG_DEBUG << "file upload complete!!";
180 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700181};
Ed Tanousc711bf82018-07-30 16:31:33 -0700182
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183class SoftwareInventoryCollection : public Node
184{
185 public:
186 template <typename CrowApp>
187 SoftwareInventoryCollection(CrowApp &app) :
188 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
189 {
190 Node::json["@odata.type"] =
191 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
192 Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory";
193 Node::json["@odata.context"] =
194 "/redfish/v1/"
195 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
196 Node::json["Name"] = "Software Inventory Collection";
Jennifer Lee729dae72018-04-24 15:59:34 -0700197
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 entityPrivileges = {
199 {boost::beast::http::verb::get, {{"Login"}}},
200 {boost::beast::http::verb::head, {{"Login"}}},
201 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
202 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
203 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
204 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700205 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700206
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 private:
208 void doGet(crow::Response &res, const crow::Request &req,
209 const std::vector<std::string> &params) override
210 {
211 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
212 res.jsonValue = Node::json;
Ed Tanousc711bf82018-07-30 16:31:33 -0700213
Ed Tanous1abe55e2018-09-05 08:30:59 -0700214 crow::connections::systemBus->async_method_call(
215 [asyncResp](
216 const boost::system::error_code ec,
217 const std::vector<std::pair<
218 std::string, std::vector<std::pair<
219 std::string, std::vector<std::string>>>>>
220 &subtree) {
221 if (ec)
222 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700223 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700224 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 }
226 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
227 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700228
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 for (auto &obj : subtree)
230 {
231 const std::vector<
232 std::pair<std::string, std::vector<std::string>>>
233 &connections = obj.second;
234
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700235 // if can't parse fw id then return
236 std::size_t idPos = obj.first.rfind("/");
237 if (idPos == std::string::npos ||
238 idPos + 1 == obj.first.size())
239 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700240 messages::internalError(asyncResp->res);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700241 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
242 return;
243 }
244
245 std::string swId = obj.first.substr(idPos + 1);
246
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 for (auto &conn : connections)
248 {
249 const std::string &connectionName = conn.first;
250 BMCWEB_LOG_DEBUG << "connectionName = "
251 << connectionName;
252 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
253
254 crow::connections::systemBus->async_method_call(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700255 [asyncResp,
256 swId](const boost::system::error_code error_code,
257 const VariantType &activation) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 BMCWEB_LOG_DEBUG
259 << "safe returned in lambda function";
260 if (error_code)
261 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700262 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 return;
264 }
265
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700266 const std::string *swActivationStatus =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 mapbox::getPtr<const std::string>(
268 activation);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700269 if (swActivationStatus == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700271 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 return;
273 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700274 if (swActivationStatus != nullptr &&
275 *swActivationStatus !=
276 "xyz.openbmc_project.Software."
277 "Activation."
278 "Activations.Active")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700280 // The activation status of this software is
281 // not currently active, so does not need to
282 // be listed in the response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 return;
284 }
285 nlohmann::json &members =
286 asyncResp->res.jsonValue["Members"];
287 members.push_back(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700288 {{"@odata.id", "/redfish/v1/UpdateService/"
289 "FirmwareInventory/" +
290 swId}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291 asyncResp->res
292 .jsonValue["Members@odata.count"] =
293 members.size();
294 },
295 connectionName, obj.first,
296 "org.freedesktop.DBus.Properties", "Get",
297 "xyz.openbmc_project.Software.Activation",
298 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700299 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 }
301 },
302 "xyz.openbmc_project.ObjectMapper",
303 "/xyz/openbmc_project/object_mapper",
304 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
305 "/xyz/openbmc_project/software", int32_t(1),
306 std::array<const char *, 1>{
307 "xyz.openbmc_project.Software.Version"});
308 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700309};
310
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311class SoftwareInventory : public Node
312{
313 public:
314 template <typename CrowApp>
315 SoftwareInventory(CrowApp &app) :
316 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
317 std::string())
318 {
319 Node::json["@odata.type"] =
320 "#SoftwareInventory.v1_1_0.SoftwareInventory";
321 Node::json["@odata.context"] =
322 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
323 Node::json["Name"] = "Software Inventory";
324 Node::json["Updateable"] = false;
325 Node::json["Status"]["Health"] = "OK";
326 Node::json["Status"]["HealthRollup"] = "OK";
327 Node::json["Status"]["State"] = "Enabled";
328 entityPrivileges = {
329 {boost::beast::http::verb::get, {{"Login"}}},
330 {boost::beast::http::verb::head, {{"Login"}}},
331 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
332 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
333 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
334 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
335 }
336
337 private:
338 void doGet(crow::Response &res, const crow::Request &req,
339 const std::vector<std::string> &params) override
340 {
341 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
342 res.jsonValue = Node::json;
343
344 if (params.size() != 1)
345 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700346 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700347 res.end();
348 return;
349 }
350
Ed Tanous3ae837c2018-08-07 14:41:19 -0700351 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352 std::make_shared<std::string>(params[0]);
353
354 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700355 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700356
357 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700358 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700359 const boost::system::error_code ec,
360 const std::vector<std::pair<
361 std::string, std::vector<std::pair<
362 std::string, std::vector<std::string>>>>>
363 &subtree) {
364 BMCWEB_LOG_DEBUG << "doGet callback...";
365 if (ec)
366 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700367 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368 return;
369 }
370
371 for (const std::pair<
372 std::string,
373 std::vector<
374 std::pair<std::string, std::vector<std::string>>>>
375 &obj : subtree)
376 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700377 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700378 {
379 continue;
380 }
381
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700382 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700383 {
384 continue;
385 }
386
387 crow::connections::systemBus->async_method_call(
388 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700389 swId](const boost::system::error_code error_code,
390 const boost::container::flat_map<
391 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392 if (error_code)
393 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700394 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700395 return;
396 }
397 boost::container::flat_map<
398 std::string, VariantType>::const_iterator it =
399 propertiesList.find("Purpose");
400 if (it == propertiesList.end())
401 {
402 BMCWEB_LOG_DEBUG
403 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700404 messages::propertyMissing(asyncResp->res,
405 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 return;
407 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700408 const std::string *swInvPurpose =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 mapbox::getPtr<const std::string>(it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700410 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700411 {
412 BMCWEB_LOG_DEBUG
413 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700414 messages::propertyValueTypeError(asyncResp->res,
415 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700416 return;
417 }
418
Ed Tanous3ae837c2018-08-07 14:41:19 -0700419 BMCWEB_LOG_DEBUG << "swInvPurpose = "
420 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700421 it = propertiesList.find("Version");
422 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700424 BMCWEB_LOG_DEBUG
425 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700426 messages::propertyMissing(asyncResp->res,
427 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700428 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700430
431 BMCWEB_LOG_DEBUG << "Version found!";
432
433 const std::string *version =
434 mapbox::getPtr<const std::string>(it->second);
435
436 if (version == nullptr)
437 {
438 BMCWEB_LOG_DEBUG
439 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700440
441 messages::propertyValueTypeError(asyncResp->res,
442 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700443 return;
444 }
445 asyncResp->res.jsonValue["Version"] = *version;
446 asyncResp->res.jsonValue["Id"] = *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 },
448 obj.second[0].first, obj.first,
449 "org.freedesktop.DBus.Properties", "GetAll",
450 "xyz.openbmc_project.Software.Version");
451 }
452 },
453 "xyz.openbmc_project.ObjectMapper",
454 "/xyz/openbmc_project/object_mapper",
455 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
456 "/xyz/openbmc_project/software", int32_t(1),
457 std::array<const char *, 1>{
458 "xyz.openbmc_project.Software.Version"});
459 }
460};
461
462} // namespace redfish