summaryrefslogtreecommitdiff
path: root/redfish-core
diff options
context:
space:
mode:
authorJennifer Lee <jennifer1.lee@intel.com>2018-06-08 02:08:15 +0300
committerEd Tanous <ed.tanous@intel.com>2018-08-15 20:53:51 +0300
commitacb7cfb4b571bd2045b1d269625ba054806a466d (patch)
tree1697302da75790332a7ce989f5a38ce679181863 /redfish-core
parentfd828baf872f3a3d10ae626d4e68509f31b30384 (diff)
downloadbmcweb-acb7cfb4b571bd2045b1d269625ba054806a466d.tar.xz
Implement POST for redfish UpdateService
- POST an image file to /redfish/v1/UpdateServer uri will upload the image and activate it - Modified SoftwareInventoryCollection to list items with xyz.openbmc_project.Software.Activation/Activation property as "xyz.openbmc_project.Software.Activation.Activations.Active" - SoftwareInventory odata.id is identified with DBus generated uuid Signed-off-by: Jennifer Lee <jennifer1.lee@intel.com> Change-Id: I48c3e6d868d1c27420cab5a706e4205f06713923
Diffstat (limited to 'redfish-core')
-rw-r--r--redfish-core/lib/update_service.hpp361
1 files changed, 269 insertions, 92 deletions
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 2ffa82ccef..efa0fbfbf8 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -19,11 +19,12 @@
#include <boost/container/flat_map.hpp>
namespace redfish {
+static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher;
class OnDemandSoftwareInventoryProvider {
public:
template <typename CallbackFunc>
- void get_all_software_inventory_object(CallbackFunc &&callback) {
+ void getAllSoftwareInventoryObject(CallbackFunc &&callback) {
crow::connections::systemBus->async_method_call(
[callback{std::move(callback)}](
const boost::system::error_code error_code,
@@ -53,7 +54,7 @@ class OnDemandSoftwareInventoryProvider {
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/software", int32_t(1),
- std::array<const char *, 1>{"xyz.openbmc_project.Software.Version"});
+ std::array<const char *, 1>{"xyz.openbmc_project.Software.Activation"});
}
};
@@ -67,6 +68,7 @@ class UpdateService : public Node {
Node::json["Id"] = "UpdateService";
Node::json["Description"] = "Service for Software Update";
Node::json["Name"] = "Update Service";
+ Node::json["HttpPushUri"] = "/redfish/v1/UpdateService";
Node::json["ServiceEnabled"] = true; // UpdateService cannot be disabled
Node::json["FirmwareInventory"] = {
{"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}};
@@ -86,6 +88,115 @@ class UpdateService : public Node {
res.jsonValue = Node::json;
res.end();
}
+ static void activateImage(const std::string &obj_path) {
+ crow::connections::systemBus->async_method_call(
+ [obj_path](const boost::system::error_code error_code) {
+ if (error_code) {
+ BMCWEB_LOG_DEBUG << "error_code = " << error_code;
+ BMCWEB_LOG_DEBUG << "error msg = " << error_code.message();
+ }
+ },
+ "xyz.openbmc_project.Software.BMC.Updater", obj_path,
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Software.Activation", "RequestedActivation",
+ sdbusplus::message::variant<std::string>(
+ "xyz.openbmc_project.Software.Activation.RequestedActivations."
+ "Active"));
+ }
+ void doPost(crow::Response &res, const crow::Request &req,
+ const std::vector<std::string> &params) override {
+ BMCWEB_LOG_DEBUG << "doPost...";
+
+ // Only allow one FW update at a time
+ if (fwUpdateMatcher != nullptr) {
+ res.addHeader("Retry-After", "30");
+ res.result(boost::beast::http::status::service_unavailable);
+ res.jsonValue = messages::serviceTemporarilyUnavailable("3");
+ res.end();
+ return;
+ }
+ // Make this const static so it survives outside this method
+ static boost::asio::deadline_timer timeout(*req.ioService,
+ boost::posix_time::seconds(5));
+
+ timeout.expires_from_now(boost::posix_time::seconds(5));
+
+ timeout.async_wait([&res](const boost::system::error_code &ec) {
+ fwUpdateMatcher = nullptr;
+ if (ec == boost::asio::error::operation_aborted) {
+ // expected, we were canceled before the timer completed.
+ return;
+ }
+ BMCWEB_LOG_ERROR << "Timed out waiting for firmware object being created";
+ BMCWEB_LOG_ERROR << "FW image may has already been uploaded to server";
+ if (ec) {
+ BMCWEB_LOG_ERROR << "Async_wait failed" << ec;
+ return;
+ }
+
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = redfish::messages::internalError();
+ res.end();
+ });
+
+ auto callback = [&res](sdbusplus::message::message &m) {
+ BMCWEB_LOG_DEBUG << "Match fired";
+ bool flag = false;
+
+ if (m.is_method_error()) {
+ BMCWEB_LOG_DEBUG << "Dbus method error!!!";
+ res.end();
+ return;
+ }
+ std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string,
+ sdbusplus::message::variant<std::string>>>>>
+ interfaces_properties;
+
+ sdbusplus::message::object_path obj_path;
+
+ m.read(obj_path, interfaces_properties); // Read in the object path
+ // that was just created
+ // std::string str_objpath = obj_path.str; // keep a copy for
+ // constructing response message
+ BMCWEB_LOG_DEBUG << "obj path = " << obj_path.str; // str_objpath;
+ for (auto &interface : interfaces_properties) {
+ BMCWEB_LOG_DEBUG << "interface = " << interface.first;
+
+ if (interface.first == "xyz.openbmc_project.Software.Activation") {
+ // cancel timer only when xyz.openbmc_project.Software.Activation
+ // interface is added
+ boost::system::error_code ec;
+ timeout.cancel(ec);
+ if (ec) {
+ BMCWEB_LOG_ERROR << "error canceling timer " << ec;
+ }
+ UpdateService::activateImage(obj_path.str); // str_objpath);
+ res.jsonValue = redfish::messages::success();
+ BMCWEB_LOG_DEBUG << "ending response";
+ res.end();
+ fwUpdateMatcher = nullptr;
+ }
+ }
+ };
+
+ fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
+ *crow::connections::systemBus,
+ "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
+ "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
+ callback);
+
+ std::string filepath(
+ "/tmp/images/" +
+ boost::uuids::to_string(boost::uuids::random_generator()()));
+ BMCWEB_LOG_DEBUG << "Writing file to " << filepath;
+ std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
+ std::ofstream::trunc);
+ out << req.body;
+ out.close();
+ BMCWEB_LOG_DEBUG << "file upload complete!!";
+ }
};
class SoftwareInventoryCollection : public Node {
@@ -120,7 +231,7 @@ class SoftwareInventoryCollection : public Node {
void doGet(crow::Response &res, const crow::Request &req,
const std::vector<std::string> &params) override {
res.jsonValue = Node::json;
- softwareInventoryProvider.get_all_software_inventory_object(
+ softwareInventoryProvider.getAllSoftwareInventoryObject(
[&](const bool &success,
const std::vector<std::pair<
std::string,
@@ -128,61 +239,83 @@ class SoftwareInventoryCollection : public Node {
&subtree) {
if (!success) {
res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
res.end();
return;
}
if (subtree.empty()) {
BMCWEB_LOG_DEBUG << "subtree empty!!";
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
res.end();
return;
}
res.jsonValue["Members"] = nlohmann::json::array();
+ res.jsonValue["Members@odata.count"] = 0;
+
+ std::shared_ptr<AsyncResp> asyncResp =
+ std::make_shared<AsyncResp>(res);
for (auto &obj : subtree) {
const std::vector<std::pair<std::string, std::vector<std::string>>>
&connections = obj.second;
- for (auto &conn : connections) {
+ // if can't parse fw id then return
+ std::size_t id_pos;
+ if ((id_pos = obj.first.rfind("/")) == std::string::npos) {
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
+ BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!";
+ res.end();
+ return;
+ }
+ std::string fw_id = obj.first.substr(id_pos + 1);
+
+ for (const auto &conn : connections) {
const std::string connectionName = conn.first;
BMCWEB_LOG_DEBUG << "connectionName = " << connectionName;
BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
crow::connections::systemBus->async_method_call(
- [&](const boost::system::error_code error_code,
- const boost::container::flat_map<std::string, VariantType>
- &propertiesList) {
+ [asyncResp, fw_id](
+ const boost::system::error_code error_code,
+ const sdbusplus::message::variant<std::string>
+ &activation_status) {
BMCWEB_LOG_DEBUG << "safe returned in lambda function";
if (error_code) {
- res.result(
+ asyncResp->res.result(
boost::beast::http::status::internal_server_error);
- res.end();
+ asyncResp->res.jsonValue = messages::internalError();
+ asyncResp->res.end();
return;
}
- boost::container::flat_map<std::string,
- VariantType>::const_iterator it =
- propertiesList.find("Purpose");
- const std::string &sw_inv_purpose =
- *(mapbox::getPtr<const std::string>(it->second));
- std::size_t last_pos = sw_inv_purpose.rfind(".");
- if (last_pos != std::string::npos) {
- res.jsonValue["Members"].push_back(
- {{"@odata.id",
- "/redfish/v1/UpdateService/FirmwareInventory/" +
- sw_inv_purpose.substr(last_pos + 1)}});
- res.jsonValue["Members@odata.count"] =
- res.jsonValue["Members"].size();
- res.end();
+ const std::string *activation_status_str =
+ mapbox::getPtr<const std::string>(activation_status);
+ if (activation_status_str != nullptr &&
+ *activation_status_str !=
+ "xyz.openbmc_project.Software.Activation."
+ "Activations.Active") {
+ // The activation status of this software is not currently
+ // active, so does not need to be listed in the response
+ return;
}
-
+ asyncResp->res.jsonValue["Members"].push_back(
+ {{"@odata.id",
+ "/redfish/v1/UpdateService/FirmwareInventory/" +
+ fw_id}});
+ asyncResp->res.jsonValue["Members@odata.count"] =
+ asyncResp->res.jsonValue["Members"].size();
},
connectionName, obj.first, "org.freedesktop.DBus.Properties",
- "GetAll", "xyz.openbmc_project.Software.Version");
+ "Get", "xyz.openbmc_project.Software.Activation",
+ "Activation");
}
}
});
}
+
OnDemandSoftwareInventoryProvider softwareInventoryProvider;
};
/**
@@ -201,9 +334,10 @@ class SoftwareInventory : public Node {
Node::json["@odata.context"] =
"/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory";
Node::json["Name"] = "Software Inventory";
- Node::json["Status"] = "OK"; // TODO
Node::json["Updateable"] = false;
-
+ Node::json["Status"]["Health"] = "OK";
+ Node::json["Status"]["HealthRollup"] = "OK";
+ Node::json["Status"]["State"] = "Enabled";
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
{boost::beast::http::verb::head, {{"Login"}}},
@@ -223,81 +357,124 @@ class SoftwareInventory : public Node {
if (params.size() != 1) {
res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
res.end();
return;
}
- const std::string &sw_id = params[0];
+ const std::string &fw_id = params[0];
+ res.jsonValue["Id"] = fw_id;
res.jsonValue["@odata.id"] =
- "/redfish/v1/UpdateService/FirmwareInventory/" + sw_id;
- softwareInventoryProvider.get_all_software_inventory_object(
- [&, id{std::string(sw_id)} ](
- const bool &success,
- const std::vector<std::pair<
- std::string,
- std::vector<std::pair<std::string, std::vector<std::string>>>>>
- &subtree) {
- BMCWEB_LOG_DEBUG << "doGet callback...";
- if (!success) {
- res.result(boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
+ "/redfish/v1/UpdateService/FirmwareInventory/" + fw_id;
+ softwareInventoryProvider.getAllSoftwareInventoryObject([
+ &res, id{std::string(fw_id)}
+ ](const bool &success,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>
+ &subtree) {
+ BMCWEB_LOG_DEBUG << "doGet callback...";
+ if (!success) {
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
+ res.end();
+ return;
+ }
- if (subtree.empty()) {
- BMCWEB_LOG_DEBUG << "subtree empty!!";
- res.end();
- return;
- }
+ if (subtree.empty()) {
+ BMCWEB_LOG_ERROR << "subtree empty!!";
+ res.result(boost::beast::http::status::not_found);
+ res.end();
+ return;
+ }
- for (auto &obj : subtree) {
- const std::vector<std::pair<std::string, std::vector<std::string>>>
- &connections = obj.second;
+ bool fw_id_found = false;
- for (auto &conn : connections) {
- const std::string connectionName = conn.first;
- BMCWEB_LOG_DEBUG << "connectionName = " << connectionName;
- BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
+ for (auto &obj : subtree) {
+ if (boost::ends_with(obj.first, id) != true) {
+ continue;
+ }
+ fw_id_found = true;
- crow::connections::systemBus->async_method_call(
- [&, id{std::string(id)} ](
- const boost::system::error_code error_code,
- const boost::container::flat_map<std::string, VariantType>
- &propertiesList) {
- if (error_code) {
- res.result(
- boost::beast::http::status::internal_server_error);
- res.end();
- return;
- }
- boost::container::flat_map<std::string,
- VariantType>::const_iterator it =
- propertiesList.find("Purpose");
- if (it == propertiesList.end()) {
- BMCWEB_LOG_DEBUG << "Can't find property \"Purpose\"!";
- return;
- }
- const std::string &sw_inv_purpose =
- *(mapbox::getPtr<const std::string>(it->second));
- BMCWEB_LOG_DEBUG << "sw_inv_purpose = " << sw_inv_purpose;
- if (boost::ends_with(sw_inv_purpose, "." + id)) {
- it = propertiesList.find("Version");
- if (it == propertiesList.end()) {
- BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!";
- return;
- }
- res.jsonValue["Version"] =
- *(mapbox::getPtr<const std::string>(it->second));
- res.jsonValue["Id"] = id;
- res.end();
- }
+ const std::vector<std::pair<std::string, std::vector<std::string>>>
+ &connections = obj.second;
- },
- connectionName, obj.first, "org.freedesktop.DBus.Properties",
- "GetAll", "xyz.openbmc_project.Software.Version");
- }
- }
- });
+ if (connections.size() <= 0) {
+ continue;
+ }
+ const std::pair<std::string, std::vector<std::string>> &conn =
+ connections[0];
+ const std::string &connectionName = conn.first;
+ BMCWEB_LOG_DEBUG << "connectionName = " << connectionName;
+ BMCWEB_LOG_DEBUG << "obj.first = " << obj.first;
+
+ crow::connections::systemBus->async_method_call(
+ [&res, id](
+ const boost::system::error_code error_code,
+ const boost::container::flat_map<std::string, VariantType>
+ &propertiesList) {
+ if (error_code) {
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
+ res.end();
+ return;
+ }
+
+ boost::container::flat_map<std::string,
+ VariantType>::const_iterator it =
+ propertiesList.find("Purpose");
+ if (it == propertiesList.end()) {
+ BMCWEB_LOG_ERROR << "Can't find property \"Purpose\"!";
+ res.result(boost::beast::http::status::internal_server_error);
+ res.jsonValue = messages::internalError();
+ res.end();
+ return;
+ }
+
+ // SoftwareId
+ const std::string *sw_inv_purpose =
+ mapbox::getPtr<const std::string>(it->second);
+ if (sw_inv_purpose == nullptr) {
+ res.jsonValue = redfish::messages::internalError();
+ res.jsonValue = messages::internalError();
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "sw_inv_purpose = " << sw_inv_purpose;
+ std::size_t last_pos = sw_inv_purpose->rfind(".");
+ if (last_pos != std::string::npos) {
+ res.jsonValue["SoftwareId"] =
+ sw_inv_purpose->substr(last_pos + 1);
+ } else {
+ BMCWEB_LOG_ERROR << "Can't parse software purpose!";
+ }
+
+ // Version
+ it = propertiesList.find("Version");
+ if (it != propertiesList.end()) {
+ const std::string *version =
+ mapbox::getPtr<const std::string>(it->second);
+ if (version == nullptr) {
+ res.jsonValue = redfish::messages::internalError();
+ res.jsonValue = messages::internalError();
+ return;
+ }
+ res.jsonValue["Version"] =
+ *(mapbox::getPtr<const std::string>(it->second));
+ } else {
+ BMCWEB_LOG_DEBUG << "Can't find version info!";
+ }
+
+ res.end();
+ },
+ connectionName, obj.first, "org.freedesktop.DBus.Properties",
+ "GetAll", "xyz.openbmc_project.Software.Version");
+ }
+ if (!fw_id_found) {
+ res.result(boost::beast::http::status::not_found);
+ res.end();
+ return;
+ }
+ });
}
OnDemandSoftwareInventoryProvider softwareInventoryProvider;