diff options
author | Jennifer Lee <jennifer1.lee@intel.com> | 2018-06-08 02:08:15 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2018-08-15 20:53:51 +0300 |
commit | acb7cfb4b571bd2045b1d269625ba054806a466d (patch) | |
tree | 1697302da75790332a7ce989f5a38ce679181863 /redfish-core | |
parent | fd828baf872f3a3d10ae626d4e68509f31b30384 (diff) | |
download | bmcweb-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.hpp | 361 |
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> ¶ms) 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> ¶ms) 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; |