blob: 74638bbd871271b449a03584a8984c998fafe82b [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{
Ed Tanous27826b52018-10-29 11:40:58 -070024
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070025static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
Jennifer Lee729dae72018-04-24 15:59:34 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027class UpdateService : public Node
28{
29 public:
30 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/")
31 {
32 Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService";
33 Node::json["@odata.id"] = "/redfish/v1/UpdateService";
34 Node::json["@odata.context"] =
35 "/redfish/v1/$metadata#UpdateService.UpdateService";
36 Node::json["Id"] = "UpdateService";
37 Node::json["Description"] = "Service for Software Update";
38 Node::json["Name"] = "Update Service";
39 Node::json["HttpPushUri"] = "/redfish/v1/UpdateService";
40 // UpdateService cannot be disabled
41 Node::json["ServiceEnabled"] = true;
42 Node::json["FirmwareInventory"] = {
43 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
Jennifer Lee729dae72018-04-24 15:59:34 -070044
Ed Tanous1abe55e2018-09-05 08:30:59 -070045 entityPrivileges = {
46 {boost::beast::http::verb::get, {{"Login"}}},
47 {boost::beast::http::verb::head, {{"Login"}}},
48 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
49 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
50 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
51 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070052 }
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070053
Ed Tanous1abe55e2018-09-05 08:30:59 -070054 private:
55 void doGet(crow::Response &res, const crow::Request &req,
56 const std::vector<std::string> &params) override
57 {
58 res.jsonValue = Node::json;
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070059 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 }
61 static void activateImage(const std::string &objPath)
62 {
63 crow::connections::systemBus->async_method_call(
64 [objPath](const boost::system::error_code error_code) {
65 if (error_code)
66 {
67 BMCWEB_LOG_DEBUG << "error_code = " << error_code;
68 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
69 }
70 },
71 "xyz.openbmc_project.Software.BMC.Updater", objPath,
72 "org.freedesktop.DBus.Properties", "Set",
73 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
74 sdbusplus::message::variant<std::string>(
75 "xyz.openbmc_project.Software.Activation.RequestedActivations."
76 "Active"));
77 }
78 void doPost(crow::Response &res, const crow::Request &req,
79 const std::vector<std::string> &params) override
80 {
81 BMCWEB_LOG_DEBUG << "doPost...";
Jennifer Leeacb7cfb2018-06-07 16:08:15 -070082
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 // Only allow one FW update at a time
84 if (fwUpdateMatcher != nullptr)
85 {
86 res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -070087 messages::serviceTemporarilyUnavailable(res, "3");
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 res.end();
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070089 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 }
91 // Make this const static so it survives outside this method
92 static boost::asio::deadline_timer timeout(
93 *req.ioService, boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070094
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 timeout.expires_from_now(boost::posix_time::seconds(5));
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -070096
Ed Tanous1abe55e2018-09-05 08:30:59 -070097 timeout.async_wait([&res](const boost::system::error_code &ec) {
98 fwUpdateMatcher = nullptr;
99 if (ec == boost::asio::error::operation_aborted)
100 {
101 // expected, we were canceled before the timer completed.
102 return;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700103 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 BMCWEB_LOG_ERROR
105 << "Timed out waiting for firmware object being created";
106 BMCWEB_LOG_ERROR
107 << "FW image may has already been uploaded to server";
108 if (ec)
109 {
110 BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
111 return;
112 }
113
Jason M. Billsf12894f2018-10-09 12:45:45 -0700114 redfish::messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 res.end();
116 });
117
118 auto callback = [&res](sdbusplus::message::message &m) {
119 BMCWEB_LOG_DEBUG << "Match fired";
120 bool flag = false;
121
122 if (m.is_method_error())
123 {
124 BMCWEB_LOG_DEBUG << "Dbus method error!!!";
125 res.end();
126 return;
127 }
128 std::vector<std::pair<
129 std::string,
130 std::vector<std::pair<
131 std::string, sdbusplus::message::variant<std::string>>>>>
Ed Tanous3ae837c2018-08-07 14:41:19 -0700132 interfacesProperties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133
134 sdbusplus::message::object_path objPath;
135
Ed Tanous3ae837c2018-08-07 14:41:19 -0700136 m.read(objPath, interfacesProperties); // Read in the object path
137 // that was just created
Ed Tanous1abe55e2018-09-05 08:30:59 -0700138 // std::string str_objpath = objPath.str; // keep a copy for
139 // constructing response message
140 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
Ed Tanous3ae837c2018-08-07 14:41:19 -0700141 for (auto &interface : interfacesProperties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 {
143 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
144
145 if (interface.first ==
146 "xyz.openbmc_project.Software.Activation")
147 {
148 // cancel timer only when
149 // xyz.openbmc_project.Software.Activation interface is
150 // added
151 boost::system::error_code ec;
152 timeout.cancel(ec);
153 if (ec)
154 {
155 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
156 }
157 UpdateService::activateImage(objPath.str); // str_objpath);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700158 redfish::messages::success(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 BMCWEB_LOG_DEBUG << "ending response";
160 res.end();
161 fwUpdateMatcher = nullptr;
162 }
163 }
164 };
165
166 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
167 *crow::connections::systemBus,
168 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
169 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
170 callback);
171
172 std::string filepath(
173 "/tmp/images/" +
174 boost::uuids::to_string(boost::uuids::random_generator()()));
175 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
176 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
177 std::ofstream::trunc);
178 out << req.body;
179 out.close();
180 BMCWEB_LOG_DEBUG << "file upload complete!!";
181 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700182};
Ed Tanousc711bf82018-07-30 16:31:33 -0700183
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184class SoftwareInventoryCollection : public Node
185{
186 public:
187 template <typename CrowApp>
188 SoftwareInventoryCollection(CrowApp &app) :
189 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
190 {
191 Node::json["@odata.type"] =
192 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
193 Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory";
194 Node::json["@odata.context"] =
195 "/redfish/v1/"
196 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
197 Node::json["Name"] = "Software Inventory Collection";
Jennifer Lee729dae72018-04-24 15:59:34 -0700198
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 entityPrivileges = {
200 {boost::beast::http::verb::get, {{"Login"}}},
201 {boost::beast::http::verb::head, {{"Login"}}},
202 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
203 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
204 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
205 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700206 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700207
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208 private:
209 void doGet(crow::Response &res, const crow::Request &req,
210 const std::vector<std::string> &params) override
211 {
212 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
213 res.jsonValue = Node::json;
Ed Tanousc711bf82018-07-30 16:31:33 -0700214
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 crow::connections::systemBus->async_method_call(
216 [asyncResp](
217 const boost::system::error_code ec,
218 const std::vector<std::pair<
219 std::string, std::vector<std::pair<
220 std::string, std::vector<std::string>>>>>
221 &subtree) {
222 if (ec)
223 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700224 messages::internalError(asyncResp->res);
Ed Tanousc711bf82018-07-30 16:31:33 -0700225 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 }
227 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
228 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700229
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 for (auto &obj : subtree)
231 {
232 const std::vector<
233 std::pair<std::string, std::vector<std::string>>>
234 &connections = obj.second;
235
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700236 // if can't parse fw id then return
Ed Tanous27826b52018-10-29 11:40:58 -0700237 std::size_t idPos;
238 if ((idPos = obj.first.rfind("/")) == std::string::npos)
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700239 {
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 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700244 std::string swId = obj.first.substr(idPos + 1);
245
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 for (auto &conn : connections)
247 {
248 const std::string &connectionName = conn.first;
249 BMCWEB_LOG_DEBUG << "connectionName = "
250 << connectionName;
251 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
252
253 crow::connections::systemBus->async_method_call(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700254 [asyncResp,
255 swId](const boost::system::error_code error_code,
256 const VariantType &activation) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 BMCWEB_LOG_DEBUG
258 << "safe returned in lambda function";
259 if (error_code)
260 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700261 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700262 return;
263 }
264
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700265 const std::string *swActivationStatus =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266 mapbox::getPtr<const std::string>(
267 activation);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700268 if (swActivationStatus == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700269 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700270 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 return;
272 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700273 if (swActivationStatus != nullptr &&
274 *swActivationStatus !=
275 "xyz.openbmc_project.Software."
276 "Activation."
277 "Activations.Active")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700279 // The activation status of this software is
280 // not currently active, so does not need to
281 // be listed in the response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282 return;
283 }
284 nlohmann::json &members =
285 asyncResp->res.jsonValue["Members"];
286 members.push_back(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700287 {{"@odata.id", "/redfish/v1/UpdateService/"
288 "FirmwareInventory/" +
289 swId}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 asyncResp->res
291 .jsonValue["Members@odata.count"] =
292 members.size();
293 },
294 connectionName, obj.first,
295 "org.freedesktop.DBus.Properties", "Get",
296 "xyz.openbmc_project.Software.Activation",
297 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700298 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 }
300 },
301 "xyz.openbmc_project.ObjectMapper",
302 "/xyz/openbmc_project/object_mapper",
303 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
304 "/xyz/openbmc_project/software", int32_t(1),
305 std::array<const char *, 1>{
306 "xyz.openbmc_project.Software.Version"});
307 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700308};
309
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310class SoftwareInventory : public Node
311{
312 public:
313 template <typename CrowApp>
314 SoftwareInventory(CrowApp &app) :
315 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
316 std::string())
317 {
318 Node::json["@odata.type"] =
319 "#SoftwareInventory.v1_1_0.SoftwareInventory";
320 Node::json["@odata.context"] =
321 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
322 Node::json["Name"] = "Software Inventory";
323 Node::json["Updateable"] = false;
324 Node::json["Status"]["Health"] = "OK";
325 Node::json["Status"]["HealthRollup"] = "OK";
326 Node::json["Status"]["State"] = "Enabled";
327 entityPrivileges = {
328 {boost::beast::http::verb::get, {{"Login"}}},
329 {boost::beast::http::verb::head, {{"Login"}}},
330 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
331 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
332 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
333 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
334 }
335
336 private:
337 void doGet(crow::Response &res, const crow::Request &req,
338 const std::vector<std::string> &params) override
339 {
340 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
341 res.jsonValue = Node::json;
342
343 if (params.size() != 1)
344 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700345 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 res.end();
347 return;
348 }
349
Ed Tanous3ae837c2018-08-07 14:41:19 -0700350 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 std::make_shared<std::string>(params[0]);
352
353 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700354 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355
356 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700357 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700358 const boost::system::error_code ec,
359 const std::vector<std::pair<
360 std::string, std::vector<std::pair<
361 std::string, std::vector<std::string>>>>>
362 &subtree) {
363 BMCWEB_LOG_DEBUG << "doGet callback...";
364 if (ec)
365 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700366 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700367 return;
368 }
369
370 for (const std::pair<
371 std::string,
372 std::vector<
373 std::pair<std::string, std::vector<std::string>>>>
374 &obj : subtree)
375 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700376 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700377 {
378 continue;
379 }
380
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700381 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382 {
383 continue;
384 }
385
386 crow::connections::systemBus->async_method_call(
387 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700388 swId](const boost::system::error_code error_code,
389 const boost::container::flat_map<
390 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700391 if (error_code)
392 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700393 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394 return;
395 }
396 boost::container::flat_map<
397 std::string, VariantType>::const_iterator it =
398 propertiesList.find("Purpose");
399 if (it == propertiesList.end())
400 {
401 BMCWEB_LOG_DEBUG
402 << "Can't find property \"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700403 messages::propertyMissing(asyncResp->res,
404 "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700405 return;
406 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700407 const std::string *swInvPurpose =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700408 mapbox::getPtr<const std::string>(it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700409 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 {
411 BMCWEB_LOG_DEBUG
412 << "wrong types for property\"Purpose\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700413 messages::propertyValueTypeError(asyncResp->res,
414 "", "Purpose");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415 return;
416 }
417
Ed Tanous3ae837c2018-08-07 14:41:19 -0700418 BMCWEB_LOG_DEBUG << "swInvPurpose = "
419 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700420 it = propertiesList.find("Version");
421 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700423 BMCWEB_LOG_DEBUG
424 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700425 messages::propertyMissing(asyncResp->res,
426 "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700427 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700429
430 BMCWEB_LOG_DEBUG << "Version found!";
431
432 const std::string *version =
433 mapbox::getPtr<const std::string>(it->second);
434
435 if (version == nullptr)
436 {
437 BMCWEB_LOG_DEBUG
438 << "Can't find property \"Version\"!";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700439
440 messages::propertyValueTypeError(asyncResp->res,
441 "", "Version");
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700442 return;
443 }
444 asyncResp->res.jsonValue["Version"] = *version;
445 asyncResp->res.jsonValue["Id"] = *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 },
447 obj.second[0].first, obj.first,
448 "org.freedesktop.DBus.Properties", "GetAll",
449 "xyz.openbmc_project.Software.Version");
450 }
451 },
452 "xyz.openbmc_project.ObjectMapper",
453 "/xyz/openbmc_project/object_mapper",
454 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
455 "/xyz/openbmc_project/software", int32_t(1),
456 std::array<const char *, 1>{
457 "xyz.openbmc_project.Software.Version"});
458 }
459};
460
461} // namespace redfish