diff options
author | Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> | 2019-07-11 10:16:43 +0200 |
---|---|---|
committer | Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> | 2020-01-27 09:52:18 +0000 |
commit | 107077def176ad4a29557fae353de9bb00381ca9 (patch) | |
tree | 98c079957b689e7b615ee12ada7ba06e2d6bb4de | |
parent | 4228a1609a9f9751604a1e636de11a34ed58b840 (diff) | |
download | bmcweb-107077def176ad4a29557fae353de9bb00381ca9.tar.gz bmcweb-107077def176ad4a29557fae353de9bb00381ca9.zip |
Add VirtualMedia schema to Redfish
This change adds VirtualMedia scheme to Redfish.
Implementation is based on input from virtual-media module
and nbd proxy which is a bmcweb part. The code is used
only in case ndb-proxy is supported in bmcweb
(BMCWEB_ENABLE_VM_NBDPROXY compilation flag).
Tested:
* Manual tests together with nbd proxy and virtual media app
- For requests: Postman and/or HTTPie, started with logs
enabled and Valgrind
- Manual result validation
* Tests ran:
- GET on collection with manual validation
- PUT/POST/DELETE on collection
- GET on item/nonexistent item
- PUT/POST/DELETE on item
* Redfish Service Validator tested, no new issues found.
Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com>
Change-Id: I5415dc0ffe52069fd35bc614b0378bbc4ad41ff6
-rw-r--r-- | redfish-core/include/redfish.hpp | 7 | ||||
-rw-r--r-- | redfish-core/lib/account_service.hpp | 13 | ||||
-rw-r--r-- | redfish-core/lib/managers.hpp | 6 | ||||
-rw-r--r-- | redfish-core/lib/virtual_media.hpp | 373 |
4 files changed, 394 insertions, 5 deletions
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 715b4a4..c498a19 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -35,6 +35,9 @@ #include "../lib/systems.hpp" #include "../lib/thermal.hpp" #include "../lib/update_service.hpp" +#ifdef BMCWEB_ENABLE_VM_NBDPROXY +#include "../lib/virtual_media.hpp" +#endif // BMCWEB_ENABLE_VM_NBDPROXY #include "webserver_common.hpp" namespace redfish @@ -124,6 +127,10 @@ class RedfishService nodes.emplace_back(std::make_unique<SystemActionsReset>(app)); nodes.emplace_back(std::make_unique<BiosService>(app)); nodes.emplace_back(std::make_unique<BiosReset>(app)); +#ifdef BMCWEB_ENABLE_VM_NBDPROXY + nodes.emplace_back(std::make_unique<VirtualMedia>(app)); + nodes.emplace_back(std::make_unique<VirtualMediaCollection>(app)); +#endif // BMCWEB_ENABLE_VM_NBDPROXY #ifdef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES nodes.emplace_back(std::make_unique<DBusLogServiceActionsClear>(app)); nodes.emplace_back(std::make_unique<DBusEventLogEntryCollection>(app)); diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index 9f066e3..017a83d 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -64,11 +64,14 @@ struct LDAPConfigData std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList; }; -using ManagedObjectType = std::vector<std::pair< - sdbusplus::message::object_path, - boost::container::flat_map< - std::string, boost::container::flat_map< - std::string, std::variant<bool, std::string>>>>>; +using DbusVariantType = sdbusplus::message::variant<bool, int32_t, std::string>; + +using DbusInterfaceType = boost::container::flat_map< + std::string, boost::container::flat_map<std::string, DbusVariantType>>; + +using ManagedObjectType = + std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>; + using GetObjectType = std::vector<std::pair<std::string, std::vector<std::string>>>; diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 1dab258..dca5933 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -1560,6 +1560,12 @@ class Manager : public Node res.jsonValue["EthernetInterfaces"] = { {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}}; + +#ifdef BMCWEB_ENABLE_VM_NBDPROXY + res.jsonValue["VirtualMedia"] = { + {"@odata.id", "/redfish/v1/Managers/bmc/VirtualMedia"}}; +#endif // BMCWEB_ENABLE_VM_NBDPROXY + // default oem data nlohmann::json& oem = res.jsonValue["Oem"]; nlohmann::json& oemOpenbmc = oem["OpenBmc"]; diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp new file mode 100644 index 0000000..c12da70 --- /dev/null +++ b/redfish-core/lib/virtual_media.hpp @@ -0,0 +1,373 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ +#pragma once + +#include <boost/container/flat_map.hpp> +#include <node.hpp> +#include <utils/json_utils.hpp> +// for GetObjectType and ManagedObjectType +#include <../lib/account_service.hpp> + +namespace redfish + +{ + +/** + * @brief Read all known properties from VM object interfaces + */ +static void vmParseInterfaceObject(const DbusInterfaceType &interface, + std::shared_ptr<AsyncResp> aResp) +{ + const auto mountPointIface = + interface.find("xyz.openbmc_project.VirtualMedia.MountPoint"); + if (mountPointIface == interface.cend()) + { + BMCWEB_LOG_DEBUG << "Interface MountPoint not found"; + return; + } + + const auto processIface = + interface.find("xyz.openbmc_project.VirtualMedia.Process"); + if (processIface == interface.cend()) + { + BMCWEB_LOG_DEBUG << "Interface Process not found"; + return; + } + + const auto endpointIdProperty = mountPointIface->second.find("EndpointId"); + if (endpointIdProperty == mountPointIface->second.cend()) + { + BMCWEB_LOG_DEBUG << "Property EndpointId not found"; + return; + } + + const auto activeProperty = processIface->second.find("Active"); + if (activeProperty == processIface->second.cend()) + { + BMCWEB_LOG_DEBUG << "Property Active not found"; + return; + } + + const bool *activeValue = std::get_if<bool>(&activeProperty->second); + if (!activeValue) + { + BMCWEB_LOG_DEBUG << "Value Active not found"; + return; + } + + const std::string *endpointIdValue = + std::get_if<std::string>(&endpointIdProperty->second); + if (endpointIdValue) + { + if (!endpointIdValue->empty()) + { + // Proxy mode + aResp->res.jsonValue["Oem"]["WebSocketEndpoint"] = *endpointIdValue; + aResp->res.jsonValue["TransferProtocolType"] = "OEM"; + aResp->res.jsonValue["Inserted"] = *activeValue; + if (*activeValue == true) + { + aResp->res.jsonValue["ConnectedVia"] = "Applet"; + } + } + else + { + // Legacy mode + const auto imageUrlProperty = + mountPointIface->second.find("ImageURL"); + if (imageUrlProperty != processIface->second.cend()) + { + const std::string *imageUrlValue = + std::get_if<std::string>(&imageUrlProperty->second); + if (imageUrlValue && !imageUrlValue->empty()) + { + aResp->res.jsonValue["ImageName"] = *imageUrlValue; + aResp->res.jsonValue["Inserted"] = *activeValue; + if (*activeValue == true) + { + aResp->res.jsonValue["ConnectedVia"] = "URI"; + } + } + } + } + } +} + +/** + * @brief Fill template for Virtual Media Item. + */ +static nlohmann::json vmItemTemplate(const std::string &name, + const std::string &resName) +{ + nlohmann::json item; + item["@odata.id"] = + "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName; + item["@odata.type"] = "#VirtualMedia.v1_1_0.VirtualMedia"; + item["@odata.context"] = "/redfish/v1/$metadata#VirtualMedia.VirtualMedia"; + item["Name"] = "Virtual Removable Media"; + item["Id"] = resName; + item["Image"] = nullptr; + item["Inserted"] = nullptr; + item["ImageName"] = nullptr; + item["WriteProtected"] = true; + item["ConnectedVia"] = "NotConnected"; + item["MediaTypes"] = {"CD", "USBStick"}; + item["TransferMethod"] = "Stream"; + item["TransferProtocolType"] = nullptr; + item["Oem"]["WebSocketEndpoint"] = nullptr; + + return item; +} + +/** + * @brief Fills collection data + */ +static void getVmResourceList(std::shared_ptr<AsyncResp> aResp, + const std::string &service, + const std::string &name) +{ + BMCWEB_LOG_DEBUG << "Get available Virtual Media resources."; + crow::connections::systemBus->async_method_call( + [name, aResp{std::move(aResp)}](const boost::system::error_code ec, + ManagedObjectType &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + return; + } + nlohmann::json &members = aResp->res.jsonValue["Members"]; + members = nlohmann::json::array(); + + for (const auto &object : subtree) + { + nlohmann::json item; + 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; + + item["@odata.id"] = "/redfish/v1/Managers/" + name + + "/VirtualMedia/" + path.substr(lastIndex); + + members.emplace_back(std::move(item)); + } + aResp->res.jsonValue["Members@odata.count"] = members.size(); + }, + service, "/xyz/openbmc_project/VirtualMedia", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); +} + +/** + * @brief Fills data for specific resource + */ +static void getVmData(std::shared_ptr<AsyncResp> aResp, + const std::string &service, const std::string &name, + const std::string &resName) +{ + BMCWEB_LOG_DEBUG << "Get Virtual Media resource data."; + + crow::connections::systemBus->async_method_call( + [resName, name, aResp](const boost::system::error_code ec, + ManagedObjectType &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + return; + } + + for (auto &item : subtree) + { + const std::string &path = + static_cast<const std::string &>(item.first); + + std::size_t lastItem = path.rfind("/"); + if (lastItem == std::string::npos) + { + continue; + } + + if (path.substr(lastItem + 1) != resName) + { + continue; + } + + aResp->res.jsonValue = vmItemTemplate(name, resName); + + vmParseInterfaceObject(item.second, aResp); + + return; + } + + messages::resourceNotFound( + aResp->res, "#VirtualMedia.v1_1_0.VirtualMedia", resName); + }, + service, "/xyz/openbmc_project/VirtualMedia", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); +} + +class VirtualMediaCollection : public Node +{ + public: + /* + * Default Constructor + */ + VirtualMediaCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", 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: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + auto asyncResp = std::make_shared<AsyncResp>(res); + + // Check if there is required param, truly entering this shall be + // impossible + if (params.size() != 1) + { + messages::internalError(res); + + return; + } + + const std::string &name = params[0]; + + if (name != "bmc") + { + messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); + + return; + } + + res.jsonValue["@odata.type"] = + "#VirtualMediaCollection.VirtualMediaCollection"; + res.jsonValue["Name"] = "Virtual Media Services"; + res.jsonValue["@odata.context"] = + "/redfish/v1/" + "$metadata#VirtualMediaCollection.VirtualMediaCollection"; + res.jsonValue["@odata.id"] = + "/redfish/v1/Managers/" + name + "/VirtualMedia/"; + + crow::connections::systemBus->async_method_call( + [asyncResp, name](const boost::system::error_code ec, + const GetObjectType &getObjectType) { + if (ec) + { + BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " + << ec; + messages::internalError(asyncResp->res); + + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG << "GetObjectType: " << service; + + getVmResourceList(asyncResp, service, name); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>()); + } +}; + +class VirtualMedia : public Node +{ + public: + /* + * Default Constructor + */ + VirtualMedia(CrowApp &app) : + Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/", + 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: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Check if there is required param, truly entering this shall be + // impossible + if (params.size() != 2) + { + messages::internalError(res); + + res.end(); + return; + } + const std::string &name = params[0]; + const std::string &resName = params[1]; + + auto asyncResp = std::make_shared<AsyncResp>(res); + + if (name != "bmc") + { + messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); + + return; + } + + crow::connections::systemBus->async_method_call( + [asyncResp, name, resName](const boost::system::error_code ec, + const GetObjectType &getObjectType) { + if (ec) + { + BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " + << ec; + messages::internalError(asyncResp->res); + + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG << "GetObjectType: " << service; + + getVmData(asyncResp, service, name, resName); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>()); + } +}; + +} // namespace redfish |