diff options
author | Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> | 2019-09-02 17:32:43 +0200 |
---|---|---|
committer | Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> | 2020-01-27 09:53:00 +0000 |
commit | e13c27606f49f70910eca01f0ee496e7e9a6b330 (patch) | |
tree | fde8f959b02e6b3c6f3cad6aa8b1f7192a4acb29 | |
parent | 107077def176ad4a29557fae353de9bb00381ca9 (diff) | |
download | bmcweb-e13c27606f49f70910eca01f0ee496e7e9a6b330.tar.gz bmcweb-e13c27606f49f70910eca01f0ee496e7e9a6b330.zip |
InsertMedia and EjectMedia actions added to VirtualMedia schema
As continuation for VirtualMedia Redfish support, this patch adds
insertion and eject actions into existing VirtualMedia code base.
Testing:
* Manual tests together with nbd proxy and virtual media app
- For requests: Postman and/or HTTPie, with logs enabled and Valgrind)
- Manual result validation
* Tests run:
- GET on collection with manual validation
- PUT/POST/DELETE on collection
- GET on item/nonexistent item
- PUT/POST/DELETE on item
- GET/PUT/DELETE on action
- POST on action - EjectMedia/InsertMedia, legacy mode
- POST on action - InsertMedia, proxy mode
- POST on action - input validation (empty, invalid URL), legacy mode
* Redfish Service Validator tested, no new issues found.
Change-Id: Icccc433c1e84bc2ac37d9c295fe72749187fb735
Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | redfish-core/include/redfish.hpp | 3 | ||||
-rw-r--r-- | redfish-core/lib/virtual_media.hpp | 377 |
3 files changed, 380 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index cf5a93f..f874d9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,6 +301,7 @@ set (WEBSERVER_MAIN src/webserver_main.cpp) include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories (${CMAKE_CURRENT_SOURCE_DIR}/redfish-core/include) +include_directories (${CMAKE_CURRENT_SOURCE_DIR}/redfish-core/lib) file (MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include/bmcweb) include_directories (${CMAKE_BINARY_DIR}/include) diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index c498a19..b1fd820 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -130,6 +130,9 @@ class RedfishService #ifdef BMCWEB_ENABLE_VM_NBDPROXY nodes.emplace_back(std::make_unique<VirtualMedia>(app)); nodes.emplace_back(std::make_unique<VirtualMediaCollection>(app)); + nodes.emplace_back( + std::make_unique<VirtualMediaActionInsertMedia>(app)); + nodes.emplace_back(std::make_unique<VirtualMediaActionEjectMedia>(app)); #endif // BMCWEB_ENABLE_VM_NBDPROXY #ifdef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES nodes.emplace_back(std::make_unique<DBusLogServiceActionsClear>(app)); diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp index c12da70..457b0b2 100644 --- a/redfish-core/lib/virtual_media.hpp +++ b/redfish-core/lib/virtual_media.hpp @@ -19,7 +19,7 @@ #include <node.hpp> #include <utils/json_utils.hpp> // for GetObjectType and ManagedObjectType -#include <../lib/account_service.hpp> +#include <account_service.hpp> namespace redfish @@ -190,6 +190,7 @@ static void getVmData(std::shared_ptr<AsyncResp> aResp, if (ec) { BMCWEB_LOG_DEBUG << "DBUS response error"; + return; } @@ -211,8 +212,22 @@ static void getVmData(std::shared_ptr<AsyncResp> aResp, aResp->res.jsonValue = vmItemTemplate(name, resName); + // Check if dbus path is Legacy type + if (path.find("VirtualMedia/Legacy") != std::string::npos) + { + aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] + ["target"] = + "/redfish/v1/Managers/" + name + "/VirtualMedia/" + + resName + "/Actions/VirtualMedia.InsertMedia"; + } + vmParseInterfaceObject(item.second, aResp); + aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"] + ["target"] = + "/redfish/v1/Managers/" + name + "/VirtualMedia/" + + resName + "/Actions/VirtualMedia.EjectMedia"; + return; } @@ -223,6 +238,366 @@ static void getVmData(std::shared_ptr<AsyncResp> aResp, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); } +/** + @brief InsertMedia action class + */ +class VirtualMediaActionInsertMedia : public Node +{ + public: + VirtualMediaActionInsertMedia(CrowApp &app) : + Node(app, + "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/" + "VirtualMedia.InsertMedia", + std::string(), std::string()) + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } + + private: + /** + * @brief Function handles POST method request. + * + * Analyzes POST body message before sends Reset request data to dbus. + */ + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + auto aResp = std::make_shared<AsyncResp>(res); + + if (params.size() != 2) + { + messages::internalError(res); + return; + } + + // take resource name from URL + const std::string &resName = params[1]; + + if (params[0] != "bmc") + { + messages::resourceNotFound(res, "VirtualMedia.Insert", resName); + + return; + } + + crow::connections::systemBus->async_method_call( + [this, aResp{std::move(aResp)}, req, + resName](const boost::system::error_code ec, + const GetObjectType &getObjectType) { + if (ec) + { + BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " + << ec; + messages::internalError(aResp->res); + + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG << "GetObjectType: " << service; + + crow::connections::systemBus->async_method_call( + [this, service, resName, req, aResp{std::move(aResp)}]( + const boost::system::error_code ec, + ManagedObjectType &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + + return; + } + + for (const auto &object : subtree) + { + const std::string &path = + static_cast<const std::string &>(object.first); + + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + continue; + } + + lastIndex += 1; + + if (path.substr(lastIndex) == resName) + { + lastIndex = path.rfind("Proxy"); + if (lastIndex != std::string::npos) + { + // Not possible in proxy mode + BMCWEB_LOG_DEBUG << "InsertMedia not " + "allowed in proxy mode"; + messages::resourceNotFound( + aResp->res, "VirtualMedia.InsertMedia", + resName); + + return; + } + + lastIndex = path.rfind("Legacy"); + if (lastIndex != std::string::npos) + { + // Legacy mode + std::string imageUrl; + + // Read obligatory paramters (url of image) + if (!json_util::readJson(req, aResp->res, + "Image", imageUrl)) + { + BMCWEB_LOG_DEBUG + << "Image is not provided"; + return; + } + + // must not be empty + if (imageUrl.size() == 0) + { + BMCWEB_LOG_ERROR + << "Request action parameter " + "Image is empty."; + messages::propertyValueFormatError( + aResp->res, "<empty>", "Image"); + + return; + } + + // manager is irrelevant for VirtualMedia + // dbus calls + doVmAction(std::move(aResp), service, + resName, true, imageUrl); + + return; + } + } + } + BMCWEB_LOG_DEBUG << "Parent item not found"; + messages::resourceNotFound(aResp->res, "VirtualMedia", + resName); + }, + service, "/xyz/openbmc_project/VirtualMedia", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>()); + } + + /** + * @brief Function transceives data with dbus directly. + * + * All BMC state properties will be retrieved before sending reset request. + */ + void doVmAction(std::shared_ptr<AsyncResp> asyncResp, + const std::string &service, const std::string &name, + bool legacy, const std::string &imageUrl) + { + + // Legacy mount requires parameter with image + if (legacy) + { + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; + messages::internalError(asyncResp->res); + + return; + } + }, + service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, + "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl); + } + else // proxy + { + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; + messages::internalError(asyncResp->res); + + return; + } + }, + service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, + "xyz.openbmc_project.VirtualMedia.Proxy", "Mount"); + } + } +}; + +/** + @brief EjectMedia action class + */ +class VirtualMediaActionEjectMedia : public Node +{ + public: + VirtualMediaActionEjectMedia(CrowApp &app) : + Node(app, + "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/" + "VirtualMedia.EjectMedia", + std::string(), std::string()) + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, + {boost::beast::http::verb::put, {{"ConfigureManager"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, + {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; + } + + private: + /** + * @brief Function handles POST method request. + * + * Analyzes POST body message before sends Reset request data to dbus. + */ + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + auto aResp = std::make_shared<AsyncResp>(res); + + if (params.size() != 2) + { + messages::internalError(res); + return; + } + + // take resource name from URL + const std::string &resName = params[1]; + + if (params[0] != "bmc") + { + messages::resourceNotFound(res, "VirtualMedia.Eject", resName); + + return; + } + + crow::connections::systemBus->async_method_call( + [this, aResp{std::move(aResp)}, req, + resName](const boost::system::error_code ec, + const GetObjectType &getObjectType) { + if (ec) + { + BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " + << ec; + messages::internalError(aResp->res); + + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG << "GetObjectType: " << service; + + crow::connections::systemBus->async_method_call( + [this, resName, service, req, aResp{std::move(aResp)}]( + const boost::system::error_code ec, + ManagedObjectType &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + + return; + } + + for (const auto &object : subtree) + { + const std::string &path = + static_cast<const std::string &>(object.first); + + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + continue; + } + + lastIndex += 1; + + if (path.substr(lastIndex) == resName) + { + lastIndex = path.rfind("Proxy"); + if (lastIndex != std::string::npos) + { + // Proxy mode + doVmAction(std::move(aResp), service, + resName, false); + } + + lastIndex = path.rfind("Legacy"); + if (lastIndex != std::string::npos) + { + // Legacy mode + doVmAction(std::move(aResp), service, + resName, true); + } + + return; + } + } + BMCWEB_LOG_DEBUG << "Parent item not found"; + messages::resourceNotFound(aResp->res, "VirtualMedia", + resName); + }, + service, "/xyz/openbmc_project/VirtualMedia", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>()); + } + + /** + * @brief Function transceives data with dbus directly. + * + * All BMC state properties will be retrieved before sending reset request. + */ + void doVmAction(std::shared_ptr<AsyncResp> asyncResp, + const std::string &service, const std::string &name, + bool legacy) + { + + // Legacy mount requires parameter with image + if (legacy) + { + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; + + messages::internalError(asyncResp->res); + return; + } + }, + service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, + "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); + } + else // proxy + { + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; + + messages::internalError(asyncResp->res); + return; + } + }, + service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, + "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); + } + } +}; + class VirtualMediaCollection : public Node { public: |