blob: c2f76f94266af3736e81e521dce7d6c8bec6cf1a [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");
86 res.result(boost::beast::http::status::service_unavailable);
87 res.jsonValue = messages::serviceTemporarilyUnavailable("3");
88 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
114 res.result(boost::beast::http::status::internal_server_error);
115 res.jsonValue = redfish::messages::internalError();
116 res.end();
117 });
118
119 auto callback = [&res](sdbusplus::message::message &m) {
120 BMCWEB_LOG_DEBUG << "Match fired";
121 bool flag = false;
122
123 if (m.is_method_error())
124 {
125 BMCWEB_LOG_DEBUG << "Dbus method error!!!";
126 res.end();
127 return;
128 }
129 std::vector<std::pair<
130 std::string,
131 std::vector<std::pair<
132 std::string, sdbusplus::message::variant<std::string>>>>>
Ed Tanous3ae837c2018-08-07 14:41:19 -0700133 interfacesProperties;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134
135 sdbusplus::message::object_path objPath;
136
Ed Tanous3ae837c2018-08-07 14:41:19 -0700137 m.read(objPath, interfacesProperties); // Read in the object path
138 // that was just created
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 // std::string str_objpath = objPath.str; // keep a copy for
140 // constructing response message
141 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
Ed Tanous3ae837c2018-08-07 14:41:19 -0700142 for (auto &interface : interfacesProperties)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700143 {
144 BMCWEB_LOG_DEBUG << "interface = " << interface.first;
145
146 if (interface.first ==
147 "xyz.openbmc_project.Software.Activation")
148 {
149 // cancel timer only when
150 // xyz.openbmc_project.Software.Activation interface is
151 // added
152 boost::system::error_code ec;
153 timeout.cancel(ec);
154 if (ec)
155 {
156 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
157 }
158 UpdateService::activateImage(objPath.str); // str_objpath);
159 res.jsonValue = redfish::messages::success();
160 BMCWEB_LOG_DEBUG << "ending response";
161 res.end();
162 fwUpdateMatcher = nullptr;
163 }
164 }
165 };
166
167 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
168 *crow::connections::systemBus,
169 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
170 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
171 callback);
172
173 std::string filepath(
174 "/tmp/images/" +
175 boost::uuids::to_string(boost::uuids::random_generator()()));
176 BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
177 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
178 std::ofstream::trunc);
179 out << req.body;
180 out.close();
181 BMCWEB_LOG_DEBUG << "file upload complete!!";
182 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700183};
Ed Tanousc711bf82018-07-30 16:31:33 -0700184
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185class SoftwareInventoryCollection : public Node
186{
187 public:
188 template <typename CrowApp>
189 SoftwareInventoryCollection(CrowApp &app) :
190 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/")
191 {
192 Node::json["@odata.type"] =
193 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
194 Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory";
195 Node::json["@odata.context"] =
196 "/redfish/v1/"
197 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection";
198 Node::json["Name"] = "Software Inventory Collection";
Jennifer Lee729dae72018-04-24 15:59:34 -0700199
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 entityPrivileges = {
201 {boost::beast::http::verb::get, {{"Login"}}},
202 {boost::beast::http::verb::head, {{"Login"}}},
203 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
204 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
205 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
206 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Jennifer Lee729dae72018-04-24 15:59:34 -0700207 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700208
Ed Tanous1abe55e2018-09-05 08:30:59 -0700209 private:
210 void doGet(crow::Response &res, const crow::Request &req,
211 const std::vector<std::string> &params) override
212 {
213 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
214 res.jsonValue = Node::json;
Ed Tanousc711bf82018-07-30 16:31:33 -0700215
Ed Tanous1abe55e2018-09-05 08:30:59 -0700216 crow::connections::systemBus->async_method_call(
217 [asyncResp](
218 const boost::system::error_code ec,
219 const std::vector<std::pair<
220 std::string, std::vector<std::pair<
221 std::string, std::vector<std::string>>>>>
222 &subtree) {
223 if (ec)
224 {
Ed Tanousc711bf82018-07-30 16:31:33 -0700225 asyncResp->res.result(
226 boost::beast::http::status::internal_server_error);
227 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 }
229 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
230 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jennifer Lee6c4eb9d2018-05-22 10:58:31 -0700231
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 for (auto &obj : subtree)
233 {
234 const std::vector<
235 std::pair<std::string, std::vector<std::string>>>
236 &connections = obj.second;
237
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700238 // if can't parse fw id then return
239 std::size_t idPos = obj.first.rfind("/");
240 if (idPos == std::string::npos ||
241 idPos + 1 == obj.first.size())
242 {
243 asyncResp->res.result(
244 boost::beast::http::status::internal_server_error);
245 asyncResp->res.jsonValue = messages::internalError();
246 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
247 return;
248 }
249
250 std::string swId = obj.first.substr(idPos + 1);
251
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 for (auto &conn : connections)
253 {
254 const std::string &connectionName = conn.first;
255 BMCWEB_LOG_DEBUG << "connectionName = "
256 << connectionName;
257 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
258
259 crow::connections::systemBus->async_method_call(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700260 [asyncResp,
261 swId](const boost::system::error_code error_code,
262 const VariantType &activation) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 BMCWEB_LOG_DEBUG
264 << "safe returned in lambda function";
265 if (error_code)
266 {
267 asyncResp->res.result(
268 boost::beast::http::status::
269 internal_server_error);
270 return;
271 }
272
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700273 const std::string *swActivationStatus =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 mapbox::getPtr<const std::string>(
275 activation);
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700276 if (swActivationStatus == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277 {
278 asyncResp->res.result(
279 boost::beast::http::status::
280 internal_server_error);
281 return;
282 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700283 if (swActivationStatus != nullptr &&
284 *swActivationStatus !=
285 "xyz.openbmc_project.Software."
286 "Activation."
287 "Activations.Active")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700289 // The activation status of this software is
290 // not currently active, so does not need to
291 // be listed in the response
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 return;
293 }
294 nlohmann::json &members =
295 asyncResp->res.jsonValue["Members"];
296 members.push_back(
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700297 {{"@odata.id", "/redfish/v1/UpdateService/"
298 "FirmwareInventory/" +
299 swId}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 asyncResp->res
301 .jsonValue["Members@odata.count"] =
302 members.size();
303 },
304 connectionName, obj.first,
305 "org.freedesktop.DBus.Properties", "Get",
306 "xyz.openbmc_project.Software.Activation",
307 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700308 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 }
310 },
311 "xyz.openbmc_project.ObjectMapper",
312 "/xyz/openbmc_project/object_mapper",
313 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
314 "/xyz/openbmc_project/software", int32_t(1),
315 std::array<const char *, 1>{
316 "xyz.openbmc_project.Software.Version"});
317 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700318};
319
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320class SoftwareInventory : public Node
321{
322 public:
323 template <typename CrowApp>
324 SoftwareInventory(CrowApp &app) :
325 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
326 std::string())
327 {
328 Node::json["@odata.type"] =
329 "#SoftwareInventory.v1_1_0.SoftwareInventory";
330 Node::json["@odata.context"] =
331 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
332 Node::json["Name"] = "Software Inventory";
333 Node::json["Updateable"] = false;
334 Node::json["Status"]["Health"] = "OK";
335 Node::json["Status"]["HealthRollup"] = "OK";
336 Node::json["Status"]["State"] = "Enabled";
337 entityPrivileges = {
338 {boost::beast::http::verb::get, {{"Login"}}},
339 {boost::beast::http::verb::head, {{"Login"}}},
340 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
341 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
342 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
343 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
344 }
345
346 private:
347 void doGet(crow::Response &res, const crow::Request &req,
348 const std::vector<std::string> &params) override
349 {
350 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
351 res.jsonValue = Node::json;
352
353 if (params.size() != 1)
354 {
355 res.result(boost::beast::http::status::internal_server_error);
356 res.jsonValue = messages::internalError();
357 res.end();
358 return;
359 }
360
Ed Tanous3ae837c2018-08-07 14:41:19 -0700361 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700362 std::make_shared<std::string>(params[0]);
363
364 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700365 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700366
367 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700368 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700369 const boost::system::error_code ec,
370 const std::vector<std::pair<
371 std::string, std::vector<std::pair<
372 std::string, std::vector<std::string>>>>>
373 &subtree) {
374 BMCWEB_LOG_DEBUG << "doGet callback...";
375 if (ec)
376 {
377 asyncResp->res.result(
378 boost::beast::http::status::internal_server_error);
379 return;
380 }
381
382 for (const std::pair<
383 std::string,
384 std::vector<
385 std::pair<std::string, std::vector<std::string>>>>
386 &obj : subtree)
387 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700388 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 {
390 continue;
391 }
392
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700393 if (obj.second.size() < 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394 {
395 continue;
396 }
397
398 crow::connections::systemBus->async_method_call(
399 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700400 swId](const boost::system::error_code error_code,
401 const boost::container::flat_map<
402 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700403 if (error_code)
404 {
405 asyncResp->res.result(
406 boost::beast::http::status::
407 internal_server_error);
408 return;
409 }
410 boost::container::flat_map<
411 std::string, VariantType>::const_iterator it =
412 propertiesList.find("Purpose");
413 if (it == propertiesList.end())
414 {
415 BMCWEB_LOG_DEBUG
416 << "Can't find property \"Purpose\"!";
417 asyncResp->res.result(
418 boost::beast::http::status::
419 internal_server_error);
420 return;
421 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700422 const std::string *swInvPurpose =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700423 mapbox::getPtr<const std::string>(it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700424 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425 {
426 BMCWEB_LOG_DEBUG
427 << "wrong types for property\"Purpose\"!";
428 asyncResp->res.result(
429 boost::beast::http::status::
430 internal_server_error);
431 return;
432 }
433
Ed Tanous3ae837c2018-08-07 14:41:19 -0700434 BMCWEB_LOG_DEBUG << "swInvPurpose = "
435 << *swInvPurpose;
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700436 it = propertiesList.find("Version");
437 if (it == propertiesList.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700439 BMCWEB_LOG_DEBUG
440 << "Can't find property \"Version\"!";
441 asyncResp->res.result(
442 boost::beast::http::status::
443 internal_server_error);
444 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 }
Jennifer Leef4b65ab2018-09-18 12:00:13 -0700446
447 BMCWEB_LOG_DEBUG << "Version found!";
448
449 const std::string *version =
450 mapbox::getPtr<const std::string>(it->second);
451
452 if (version == nullptr)
453 {
454 BMCWEB_LOG_DEBUG
455 << "Can't find property \"Version\"!";
456 asyncResp->res.result(
457 boost::beast::http::status::
458 internal_server_error);
459 return;
460 }
461 asyncResp->res.jsonValue["Version"] = *version;
462 asyncResp->res.jsonValue["Id"] = *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 },
464 obj.second[0].first, obj.first,
465 "org.freedesktop.DBus.Properties", "GetAll",
466 "xyz.openbmc_project.Software.Version");
467 }
468 },
469 "xyz.openbmc_project.ObjectMapper",
470 "/xyz/openbmc_project/object_mapper",
471 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
472 "/xyz/openbmc_project/software", int32_t(1),
473 std::array<const char *, 1>{
474 "xyz.openbmc_project.Software.Version"});
475 }
476};
477
478} // namespace redfish