blob: df8473f15f8c7314b532c9af4e67bf7a5b852bbb [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
238 for (auto &conn : connections)
239 {
240 const std::string &connectionName = conn.first;
241 BMCWEB_LOG_DEBUG << "connectionName = "
242 << connectionName;
243 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
244
245 crow::connections::systemBus->async_method_call(
246 [asyncResp](
247 const boost::system::error_code error_code,
248 const VariantType &activation) {
249 BMCWEB_LOG_DEBUG
250 << "safe returned in lambda function";
251 if (error_code)
252 {
253 asyncResp->res.result(
254 boost::beast::http::status::
255 internal_server_error);
256 return;
257 }
258
Ed Tanous3ae837c2018-08-07 14:41:19 -0700259 const std::string *swInvPurpose =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 mapbox::getPtr<const std::string>(
261 activation);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700262 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 {
264 asyncResp->res.result(
265 boost::beast::http::status::
266 internal_server_error);
267 return;
268 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700269 std::size_t last_pos = swInvPurpose->rfind(".");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 if (last_pos == std::string::npos)
271 {
272 asyncResp->res.result(
273 boost::beast::http::status::
274 internal_server_error);
275 return;
276 }
277 nlohmann::json &members =
278 asyncResp->res.jsonValue["Members"];
279 members.push_back(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700280 {{"@odata.id",
281 "/redfish/v1/UpdateService/"
282 "FirmwareInventory/" +
283 swInvPurpose->substr(last_pos + 1)}});
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 asyncResp->res
285 .jsonValue["Members@odata.count"] =
286 members.size();
287 },
288 connectionName, obj.first,
289 "org.freedesktop.DBus.Properties", "Get",
290 "xyz.openbmc_project.Software.Activation",
291 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700292 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293 }
294 },
295 "xyz.openbmc_project.ObjectMapper",
296 "/xyz/openbmc_project/object_mapper",
297 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
298 "/xyz/openbmc_project/software", int32_t(1),
299 std::array<const char *, 1>{
300 "xyz.openbmc_project.Software.Version"});
301 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700302};
303
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304class SoftwareInventory : public Node
305{
306 public:
307 template <typename CrowApp>
308 SoftwareInventory(CrowApp &app) :
309 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
310 std::string())
311 {
312 Node::json["@odata.type"] =
313 "#SoftwareInventory.v1_1_0.SoftwareInventory";
314 Node::json["@odata.context"] =
315 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
316 Node::json["Name"] = "Software Inventory";
317 Node::json["Updateable"] = false;
318 Node::json["Status"]["Health"] = "OK";
319 Node::json["Status"]["HealthRollup"] = "OK";
320 Node::json["Status"]["State"] = "Enabled";
321 entityPrivileges = {
322 {boost::beast::http::verb::get, {{"Login"}}},
323 {boost::beast::http::verb::head, {{"Login"}}},
324 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
325 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
326 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
327 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
328 }
329
330 private:
331 void doGet(crow::Response &res, const crow::Request &req,
332 const std::vector<std::string> &params) override
333 {
334 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
335 res.jsonValue = Node::json;
336
337 if (params.size() != 1)
338 {
339 res.result(boost::beast::http::status::internal_server_error);
340 res.jsonValue = messages::internalError();
341 res.end();
342 return;
343 }
344
Ed Tanous3ae837c2018-08-07 14:41:19 -0700345 std::shared_ptr<std::string> swId =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700346 std::make_shared<std::string>(params[0]);
347
348 res.jsonValue["@odata.id"] =
Ed Tanous3ae837c2018-08-07 14:41:19 -0700349 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700350
351 crow::connections::systemBus->async_method_call(
Ed Tanous3ae837c2018-08-07 14:41:19 -0700352 [asyncResp, swId](
Ed Tanous1abe55e2018-09-05 08:30:59 -0700353 const boost::system::error_code ec,
354 const std::vector<std::pair<
355 std::string, std::vector<std::pair<
356 std::string, std::vector<std::string>>>>>
357 &subtree) {
358 BMCWEB_LOG_DEBUG << "doGet callback...";
359 if (ec)
360 {
361 asyncResp->res.result(
362 boost::beast::http::status::internal_server_error);
363 return;
364 }
365
366 for (const std::pair<
367 std::string,
368 std::vector<
369 std::pair<std::string, std::vector<std::string>>>>
370 &obj : subtree)
371 {
Ed Tanous3ae837c2018-08-07 14:41:19 -0700372 if (boost::ends_with(obj.first, *swId) != true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700373 {
374 continue;
375 }
376
377 if (obj.second.size() <= 1)
378 {
379 continue;
380 }
381
382 crow::connections::systemBus->async_method_call(
383 [asyncResp,
Ed Tanous3ae837c2018-08-07 14:41:19 -0700384 swId](const boost::system::error_code error_code,
385 const boost::container::flat_map<
386 std::string, VariantType> &propertiesList) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387 if (error_code)
388 {
389 asyncResp->res.result(
390 boost::beast::http::status::
391 internal_server_error);
392 return;
393 }
394 boost::container::flat_map<
395 std::string, VariantType>::const_iterator it =
396 propertiesList.find("Purpose");
397 if (it == propertiesList.end())
398 {
399 BMCWEB_LOG_DEBUG
400 << "Can't find property \"Purpose\"!";
401 asyncResp->res.result(
402 boost::beast::http::status::
403 internal_server_error);
404 return;
405 }
Ed Tanous3ae837c2018-08-07 14:41:19 -0700406 const std::string *swInvPurpose =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 mapbox::getPtr<const std::string>(it->second);
Ed Tanous3ae837c2018-08-07 14:41:19 -0700408 if (swInvPurpose == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409 {
410 BMCWEB_LOG_DEBUG
411 << "wrong types for property\"Purpose\"!";
412 asyncResp->res.result(
413 boost::beast::http::status::
414 internal_server_error);
415 return;
416 }
417
Ed Tanous3ae837c2018-08-07 14:41:19 -0700418 BMCWEB_LOG_DEBUG << "swInvPurpose = "
419 << *swInvPurpose;
420 if (boost::ends_with(*swInvPurpose, "." + *swId))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421 {
422 it = propertiesList.find("Version");
423 if (it == propertiesList.end())
424 {
425 BMCWEB_LOG_DEBUG
426 << "Can't find property \"Version\"!";
427 asyncResp->res.result(
428 boost::beast::http::status::
429 internal_server_error);
430 return;
431 }
432
433 const std::string *version =
434 mapbox::getPtr<const std::string>(
435 it->second);
436
437 if (version != nullptr)
438 {
439 BMCWEB_LOG_DEBUG
440 << "Can't find property \"Version\"!";
441 asyncResp->res.result(
442 boost::beast::http::status::
443 internal_server_error);
444 return;
445 }
446 asyncResp->res.jsonValue["Version"] = *version;
Ed Tanous3ae837c2018-08-07 14:41:19 -0700447 asyncResp->res.jsonValue["Id"] = *swId;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
449 },
450 obj.second[0].first, obj.first,
451 "org.freedesktop.DBus.Properties", "GetAll",
452 "xyz.openbmc_project.Software.Version");
453 }
454 },
455 "xyz.openbmc_project.ObjectMapper",
456 "/xyz/openbmc_project/object_mapper",
457 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
458 "/xyz/openbmc_project/software", int32_t(1),
459 std::array<const char *, 1>{
460 "xyz.openbmc_project.Software.Version"});
461 }
462};
463
464} // namespace redfish