blob: 658937d9a5fff7d0fdfd66e653d6008a901ac4df [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>>>>>
133 interfaces_properties;
134
135 sdbusplus::message::object_path objPath;
136
137 m.read(objPath, interfaces_properties); // Read in the object path
138 // that was just created
139 // std::string str_objpath = objPath.str; // keep a copy for
140 // constructing response message
141 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath;
142 for (auto &interface : interfaces_properties)
143 {
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
259 const std::string *sw_inv_purpose =
260 mapbox::getPtr<const std::string>(
261 activation);
262 if (sw_inv_purpose == nullptr)
263 {
264 asyncResp->res.result(
265 boost::beast::http::status::
266 internal_server_error);
267 return;
268 }
269 std::size_t last_pos =
270 sw_inv_purpose->rfind(".");
271 if (last_pos == std::string::npos)
272 {
273 asyncResp->res.result(
274 boost::beast::http::status::
275 internal_server_error);
276 return;
277 }
278 nlohmann::json &members =
279 asyncResp->res.jsonValue["Members"];
280 members.push_back(
281 {{"@odata.id", "/redfish/v1/UpdateService/"
282 "FirmwareInventory/" +
283 sw_inv_purpose->substr(
284 last_pos + 1)}});
285 asyncResp->res
286 .jsonValue["Members@odata.count"] =
287 members.size();
288 },
289 connectionName, obj.first,
290 "org.freedesktop.DBus.Properties", "Get",
291 "xyz.openbmc_project.Software.Activation",
292 "Activation");
Ed Tanousc711bf82018-07-30 16:31:33 -0700293 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 }
295 },
296 "xyz.openbmc_project.ObjectMapper",
297 "/xyz/openbmc_project/object_mapper",
298 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
299 "/xyz/openbmc_project/software", int32_t(1),
300 std::array<const char *, 1>{
301 "xyz.openbmc_project.Software.Version"});
302 }
Jennifer Lee729dae72018-04-24 15:59:34 -0700303};
304
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305class SoftwareInventory : public Node
306{
307 public:
308 template <typename CrowApp>
309 SoftwareInventory(CrowApp &app) :
310 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/",
311 std::string())
312 {
313 Node::json["@odata.type"] =
314 "#SoftwareInventory.v1_1_0.SoftwareInventory";
315 Node::json["@odata.context"] =
316 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
317 Node::json["Name"] = "Software Inventory";
318 Node::json["Updateable"] = false;
319 Node::json["Status"]["Health"] = "OK";
320 Node::json["Status"]["HealthRollup"] = "OK";
321 Node::json["Status"]["State"] = "Enabled";
322 entityPrivileges = {
323 {boost::beast::http::verb::get, {{"Login"}}},
324 {boost::beast::http::verb::head, {{"Login"}}},
325 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
326 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
327 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
328 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
329 }
330
331 private:
332 void doGet(crow::Response &res, const crow::Request &req,
333 const std::vector<std::string> &params) override
334 {
335 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
336 res.jsonValue = Node::json;
337
338 if (params.size() != 1)
339 {
340 res.result(boost::beast::http::status::internal_server_error);
341 res.jsonValue = messages::internalError();
342 res.end();
343 return;
344 }
345
346 std::shared_ptr<std::string> sw_id =
347 std::make_shared<std::string>(params[0]);
348
349 res.jsonValue["@odata.id"] =
350 "/redfish/v1/UpdateService/FirmwareInventory/" + *sw_id;
351
352 crow::connections::systemBus->async_method_call(
353 [asyncResp, sw_id](
354 const boost::system::error_code ec,
355 const std::vector<std::pair<
356 std::string, std::vector<std::pair<
357 std::string, std::vector<std::string>>>>>
358 &subtree) {
359 BMCWEB_LOG_DEBUG << "doGet callback...";
360 if (ec)
361 {
362 asyncResp->res.result(
363 boost::beast::http::status::internal_server_error);
364 return;
365 }
366
367 for (const std::pair<
368 std::string,
369 std::vector<
370 std::pair<std::string, std::vector<std::string>>>>
371 &obj : subtree)
372 {
373 if (boost::ends_with(obj.first, *sw_id) != true)
374 {
375 continue;
376 }
377
378 if (obj.second.size() <= 1)
379 {
380 continue;
381 }
382
383 crow::connections::systemBus->async_method_call(
384 [asyncResp,
385 sw_id](const boost::system::error_code error_code,
386 const boost::container::flat_map<
387 std::string, VariantType> &propertiesList) {
388 if (error_code)
389 {
390 asyncResp->res.result(
391 boost::beast::http::status::
392 internal_server_error);
393 return;
394 }
395 boost::container::flat_map<
396 std::string, VariantType>::const_iterator it =
397 propertiesList.find("Purpose");
398 if (it == propertiesList.end())
399 {
400 BMCWEB_LOG_DEBUG
401 << "Can't find property \"Purpose\"!";
402 asyncResp->res.result(
403 boost::beast::http::status::
404 internal_server_error);
405 return;
406 }
407 const std::string *sw_inv_purpose =
408 mapbox::getPtr<const std::string>(it->second);
409 if (sw_inv_purpose == nullptr)
410 {
411 BMCWEB_LOG_DEBUG
412 << "wrong types for property\"Purpose\"!";
413 asyncResp->res.result(
414 boost::beast::http::status::
415 internal_server_error);
416 return;
417 }
418
419 BMCWEB_LOG_DEBUG << "sw_inv_purpose = "
420 << *sw_inv_purpose;
421 if (boost::ends_with(*sw_inv_purpose, "." + *sw_id))
422 {
423 it = propertiesList.find("Version");
424 if (it == propertiesList.end())
425 {
426 BMCWEB_LOG_DEBUG
427 << "Can't find property \"Version\"!";
428 asyncResp->res.result(
429 boost::beast::http::status::
430 internal_server_error);
431 return;
432 }
433
434 const std::string *version =
435 mapbox::getPtr<const std::string>(
436 it->second);
437
438 if (version != nullptr)
439 {
440 BMCWEB_LOG_DEBUG
441 << "Can't find property \"Version\"!";
442 asyncResp->res.result(
443 boost::beast::http::status::
444 internal_server_error);
445 return;
446 }
447 asyncResp->res.jsonValue["Version"] = *version;
448 asyncResp->res.jsonValue["Id"] = *sw_id;
449 }
450 },
451 obj.second[0].first, obj.first,
452 "org.freedesktop.DBus.Properties", "GetAll",
453 "xyz.openbmc_project.Software.Version");
454 }
455 },
456 "xyz.openbmc_project.ObjectMapper",
457 "/xyz/openbmc_project/object_mapper",
458 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
459 "/xyz/openbmc_project/software", int32_t(1),
460 std::array<const char *, 1>{
461 "xyz.openbmc_project.Software.Version"});
462 }
463};
464
465} // namespace redfish