diff options
Diffstat (limited to 'redfish-core/lib')
-rw-r--r-- | redfish-core/lib/account_service.hpp | 69 | ||||
-rw-r--r-- | redfish-core/lib/chassis.hpp | 415 | ||||
-rw-r--r-- | redfish-core/lib/ethernet.hpp | 3448 | ||||
-rw-r--r-- | redfish-core/lib/managers.hpp | 182 | ||||
-rw-r--r-- | redfish-core/lib/network_protocol.hpp | 311 | ||||
-rw-r--r-- | redfish-core/lib/redfish_sessions.hpp | 604 | ||||
-rw-r--r-- | redfish-core/lib/roles.hpp | 134 | ||||
-rw-r--r-- | redfish-core/lib/sensors.hpp | 788 | ||||
-rw-r--r-- | redfish-core/lib/service_root.hpp | 99 | ||||
-rw-r--r-- | redfish-core/lib/systems.hpp | 1431 | ||||
-rw-r--r-- | redfish-core/lib/thermal.hpp | 75 | ||||
-rw-r--r-- | redfish-core/lib/update_service.hpp | 771 |
12 files changed, 4589 insertions, 3738 deletions
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index 47b4c4c7b6..c58cafd633 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -17,40 +17,45 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class AccountService : public Node { - public: - AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService"; - Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#AccountService.AccountService"; - Node::json["Id"] = "AccountService"; - Node::json["Description"] = "BMC User Accounts"; - Node::json["Name"] = "Account Service"; - Node::json["ServiceEnabled"] = true; - Node::json["MinPasswordLength"] = 1; - Node::json["MaxPasswordLength"] = 20; - Node::json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts"; - Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; +class AccountService : public Node +{ + public: + AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/") + { + Node::json["@odata.id"] = "/redfish/v1/AccountService"; + Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#AccountService.AccountService"; + Node::json["Id"] = "AccountService"; + Node::json["Description"] = "BMC User Accounts"; + Node::json["Name"] = "Account Service"; + Node::json["ServiceEnabled"] = true; + Node::json["MinPasswordLength"] = 1; + Node::json["MaxPasswordLength"] = 20; + Node::json["Accounts"]["@odata.id"] = + "/redfish/v1/AccountService/Accounts"; + Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles"; - entityPrivileges = { - {boost::beast::http::verb::get, - {{"ConfigureUsers"}, {"ConfigureManager"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, - {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, + {{"ConfigureUsers"}, {"ConfigureManager"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::put, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}}, + {boost::beast::http::verb::post, {{"ConfigureUsers"}}}}; + } - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp index b1c20a538a..7b68a270ad 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -16,9 +16,11 @@ #pragma once #include "node.hpp" + #include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ /** * DBus types primitives for several generic DBus interfaces @@ -47,220 +49,251 @@ using PropertiesType = boost::container::flat_map<std::string, VariantType>; * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandChassisProvider { - public: - /** - * Function that retrieves all Chassis available through EntityManager. - * @param callback a function that shall be called to convert Dbus output into - * JSON. - */ - template <typename CallbackFunc> - void getChassisList(CallbackFunc &&callback) { - const std::array<const char *, 4> interfaces = { - "xyz.openbmc_project.Inventory.Item.Board", - "xyz.openbmc_project.Inventory.Item.Chassis", - "xyz.openbmc_project.Inventory.Item.PowerSupply", - "xyz.openbmc_project.Inventory.Item.System", +class OnDemandChassisProvider +{ + public: + /** + * Function that retrieves all Chassis available through EntityManager. + * @param callback a function that shall be called to convert Dbus output + * into JSON. + */ + template <typename CallbackFunc> + void getChassisList(CallbackFunc &&callback) + { + const std::array<const char *, 4> interfaces = { + "xyz.openbmc_project.Inventory.Item.Board", + "xyz.openbmc_project.Inventory.Item.Chassis", + "xyz.openbmc_project.Inventory.Item.PowerSupply", + "xyz.openbmc_project.Inventory.Item.System", + }; + crow::connections::systemBus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code error_code, + const std::vector<std::string> &resp) { + // Callback requires vector<string> to retrieve all available + // chassis list. + std::vector<std::string> chassisList; + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Entity Manager, and empty output could + // not be treated same way as error. + callback(false, chassisList); + return; + } + // Iterate over all retrieved ObjectPaths. + for (const std::string &objpath : resp) + { + std::size_t lastPos = objpath.rfind("/"); + if (lastPos != std::string::npos) + { + // and put it into output vector. + chassisList.emplace_back(objpath.substr(lastPos + 1)); + } + } + // Finally make a callback with useful data + callback(true, chassisList); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + "/xyz/openbmc_project/inventory", int32_t(3), interfaces); }; - crow::connections::systemBus->async_method_call( - [callback{std::move(callback)}]( - const boost::system::error_code error_code, - const std::vector<std::string> &resp) { - // Callback requires vector<string> to retrieve all available chassis - // list. - std::vector<std::string> chassisList; - if (error_code) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Entity Manager, - // and empty output could not be treated same way as error. - callback(false, chassisList); - return; - } - // Iterate over all retrieved ObjectPaths. - for (const std::string &objpath : resp) { - std::size_t lastPos = objpath.rfind("/"); - if (lastPos != std::string::npos) { - // and put it into output vector. - chassisList.emplace_back(objpath.substr(lastPos + 1)); - } - } - // Finally make a callback with useful data - callback(true, chassisList); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", - "/xyz/openbmc_project/inventory", int32_t(3), interfaces); - }; }; /** * ChassisCollection derived class for delivering Chassis Collection Schema */ -class ChassisCollection : public Node { - public: - ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { - Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; - Node::json["@odata.id"] = "/redfish/v1/Chassis"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; - Node::json["Name"] = "Chassis Collection"; +class ChassisCollection : public Node +{ + public: + ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") + { + Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; + Node::json["Name"] = "Chassis Collection"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // get chassis list, and call the below callback for JSON preparation - chassisProvider.getChassisList( - [&](const bool &success, const std::vector<std::string> &output) { - if (success) { - // ... prepare json array with appropriate @odata.id links - nlohmann::json chassisArray = nlohmann::json::array(); - for (const std::string &chassisItem : output) { - chassisArray.push_back( - {{"@odata.id", "/redfish/v1/Chassis/" + chassisItem}}); - } - // Then attach members, count size and return, - Node::json["Members"] = chassisArray; - Node::json["Members@odata.count"] = chassisArray.size(); - res.jsonValue = Node::json; - } else { - // ... otherwise, return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // get chassis list, and call the below callback for JSON preparation + chassisProvider.getChassisList( + [&](const bool &success, const std::vector<std::string> &output) { + if (success) + { + // ... prepare json array with appropriate @odata.id links + nlohmann::json chassisArray = nlohmann::json::array(); + for (const std::string &chassisItem : output) + { + chassisArray.push_back( + {{"@odata.id", + "/redfish/v1/Chassis/" + chassisItem}}); + } + // Then attach members, count size and return, + Node::json["Members"] = chassisArray; + Node::json["Members@odata.count"] = chassisArray.size(); + res.jsonValue = Node::json; + } + else + { + // ... otherwise, return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); + } - // Chassis Provider object - // TODO(Pawel) consider move it to singleton - OnDemandChassisProvider chassisProvider; + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassisProvider; }; /** * Chassis override class for delivering Chassis Schema */ -class Chassis : public Node { - public: - Chassis(CrowApp &app) - : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { - Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; - Node::json["@odata.id"] = "/redfish/v1/Chassis"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; - Node::json["Name"] = "Chassis Collection"; - Node::json["ChassisType"] = "RackMount"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } +class Chassis : public Node +{ + public: + Chassis(CrowApp &app) : + Node(app, "/redfish/v1/Chassis/<str>/", std::string()) + { + Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; + Node::json["@odata.id"] = "/redfish/v1/Chassis"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; + Node::json["Name"] = "Chassis Collection"; + Node::json["ChassisType"] = "RackMount"; - 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() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - res.jsonValue = Node::json; - const std::string &chassisId = params[0]; - crow::connections::systemBus->async_method_call( - [&res, chassisId(std::string(chassisId)) ]( - const boost::system::error_code error_code, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (error_code) { - res.jsonValue = {}; + 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() != 1) + { res.result(boost::beast::http::status::internal_server_error); res.end(); return; - } - // Iterate over all retrieved ObjectPaths. - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &object : subtree) { - const std::string &path = object.first; - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connectionNames = object.second; + } - if (!boost::ends_with(path, chassisId)) { - continue; - } - if (connectionNames.size() < 1) { - BMCWEB_LOG_ERROR << "Only got " << connectionNames.size() - << " Connection names"; - continue; - } + res.jsonValue = Node::json; + const std::string &chassisId = params[0]; + crow::connections::systemBus->async_method_call( + [&res, chassisId(std::string(chassisId))]( + const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (error_code) + { + res.jsonValue = {}; + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + return; + } + // Iterate over all retrieved ObjectPaths. + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &object : subtree) + { + const std::string &path = object.first; + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connectionNames = object.second; - const std::string connectionName = connectionNames[0].first; - crow::connections::systemBus->async_method_call( - [&res, chassisId(std::string(chassisId)) ]( - const boost::system::error_code error_code, - const std::vector<std::pair<std::string, VariantType>> - &propertiesList) { - for (const std::pair<std::string, VariantType> &property : - propertiesList) { - const std::string *value = - mapbox::getPtr<const std::string>(property.second); - if (value != nullptr) { - res.jsonValue[property.first] = *value; + if (!boost::ends_with(path, chassisId)) + { + continue; } - } - res.jsonValue["Name"] = chassisId; - res.jsonValue["Id"] = chassisId; - res.jsonValue["Thermal"] = { - {"@odata.id", - "/redfish/v1/Chassis/" + chassisId + "/Thermal"}}; - res.end(); - }, - connectionName, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); - // Found the Connection we were looking for, return - return; - } + if (connectionNames.size() < 1) + { + BMCWEB_LOG_ERROR << "Only got " + << connectionNames.size() + << " Connection names"; + continue; + } + + const std::string connectionName = connectionNames[0].first; + crow::connections::systemBus->async_method_call( + [&res, chassisId(std::string(chassisId))]( + const boost::system::error_code error_code, + const std::vector<std::pair< + std::string, VariantType>> &propertiesList) { + for (const std::pair<std::string, VariantType> + &property : propertiesList) + { + const std::string *value = + mapbox::getPtr<const std::string>( + property.second); + if (value != nullptr) + { + res.jsonValue[property.first] = *value; + } + } + res.jsonValue["Name"] = chassisId; + res.jsonValue["Id"] = chassisId; + res.jsonValue["Thermal"] = { + {"@odata.id", "/redfish/v1/Chassis/" + + chassisId + "/Thermal"}}; + res.end(); + }, + connectionName, path, "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory.Decorator.Asset"); + // Found the Connection we were looking for, return + return; + } - // Couldn't find an object with that name. return an error - res.result(boost::beast::http::status::not_found); + // Couldn't find an object with that name. return an error + res.result(boost::beast::http::status::not_found); - res.end(); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/inventory", int32_t(0), - std::array<const char *, 1>{ - "xyz.openbmc_project.Inventory.Decorator.Asset"}); - } + res.end(); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/inventory", int32_t(0), + std::array<const char *, 1>{ + "xyz.openbmc_project.Inventory.Decorator.Asset"}); + } - // Chassis Provider object - // TODO(Pawel) consider move it to singleton - OnDemandChassisProvider chassisProvider; -}; // namespace redfish + // Chassis Provider object + // TODO(Pawel) consider move it to singleton + OnDemandChassisProvider chassisProvider; +}; // namespace redfish -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp index 01111a4e5f..0c642798b5 100644 --- a/redfish-core/lib/ethernet.hpp +++ b/redfish-core/lib/ethernet.hpp @@ -15,13 +15,14 @@ */ #pragma once +#include <boost/container/flat_map.hpp> #include <dbus_singleton.hpp> #include <error_messages.hpp> #include <node.hpp> #include <utils/json_utils.hpp> -#include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ /** * DBus types primitives for several generic DBus interfaces @@ -45,35 +46,40 @@ using GetManagedObjectsType = boost::container::flat_map< * Structure for keeping IPv4 data required by Redfish * TODO(Pawel) consider change everything to ptr, or to non-ptr values. */ -struct IPv4AddressData { - std::string id; - const std::string *address; - const std::string *domain; - const std::string *gateway; - std::string netmask; - std::string origin; - bool global; - /** - * @brief Operator< to enable sorting - * - * @param[in] obj Object to compare with - * - * @return This object id < supplied object id - */ - bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); } +struct IPv4AddressData +{ + std::string id; + const std::string *address; + const std::string *domain; + const std::string *gateway; + std::string netmask; + std::string origin; + bool global; + /** + * @brief Operator< to enable sorting + * + * @param[in] obj Object to compare with + * + * @return This object id < supplied object id + */ + bool operator<(const IPv4AddressData &obj) const + { + return (id < obj.id); + } }; /** * Structure for keeping basic single Ethernet Interface information * available from DBus */ -struct EthernetInterfaceData { - const unsigned int *speed; - const bool *autoNeg; - const std::string *hostname; - const std::string *defaultGateway; - const std::string *macAddress; - const unsigned int *vlanId; +struct EthernetInterfaceData +{ + const unsigned int *speed; + const bool *autoNeg; + const std::string *hostname; + const std::string *defaultGateway; + const std::string *macAddress; + const unsigned int *vlanId; }; /** @@ -85,1264 +91,1493 @@ struct EthernetInterfaceData { * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandEthernetProvider { - private: - // Consts that may have influence on EthernetProvider performance/memory usage - const size_t maxIpV4AddressesPerInterface = 10; - - // Helper function that allows to extract GetAllPropertiesType from - // GetManagedObjectsType, based on object path, and interface name - const PropertiesMapType *extractInterfaceProperties( - const sdbusplus::message::object_path &objpath, - const std::string &interface, const GetManagedObjectsType &dbus_data) { - const auto &dbusObj = dbus_data.find(objpath); - if (dbusObj != dbus_data.end()) { - const auto &iface = dbusObj->second.find(interface); - if (iface != dbusObj->second.end()) { - return &iface->second; - } +class OnDemandEthernetProvider +{ + private: + // Consts that may have influence on EthernetProvider performance/memory + // usage + const size_t maxIpV4AddressesPerInterface = 10; + + // Helper function that allows to extract GetAllPropertiesType from + // GetManagedObjectsType, based on object path, and interface name + const PropertiesMapType *extractInterfaceProperties( + const sdbusplus::message::object_path &objpath, + const std::string &interface, const GetManagedObjectsType &dbus_data) + { + const auto &dbusObj = dbus_data.find(objpath); + if (dbusObj != dbus_data.end()) + { + const auto &iface = dbusObj->second.find(interface); + if (iface != dbusObj->second.end()) + { + return &iface->second; + } + } + return nullptr; } - return nullptr; - } - - // Helper Wrapper that does inline object_path conversion from string - // into sdbusplus::message::object_path type - inline const PropertiesMapType *extractInterfaceProperties( - const std::string &objpath, const std::string &interface, - const GetManagedObjectsType &dbus_data) { - const auto &dbusObj = sdbusplus::message::object_path{objpath}; - return extractInterfaceProperties(dbusObj, interface, dbus_data); - } - - // Helper function that allows to get pointer to the property from - // GetAllPropertiesType native, or extracted by GetAllPropertiesType - template <typename T> - inline T const *const extractProperty(const PropertiesMapType &properties, - const std::string &name) { - const auto &property = properties.find(name); - if (property != properties.end()) { - return mapbox::getPtr<const T>(property->second); + + // Helper Wrapper that does inline object_path conversion from string + // into sdbusplus::message::object_path type + inline const PropertiesMapType * + extractInterfaceProperties(const std::string &objpath, + const std::string &interface, + const GetManagedObjectsType &dbus_data) + { + const auto &dbusObj = sdbusplus::message::object_path{objpath}; + return extractInterfaceProperties(dbusObj, interface, dbus_data); } - return nullptr; - } - // TODO(Pawel) Consider to move the above functions to dbus - // generic_interfaces.hpp - - // Helper function that extracts data from several dbus objects and several - // interfaces required by single ethernet interface instance - void extractEthernetInterfaceData(const std::string ðifaceId, - const GetManagedObjectsType &dbus_data, - EthernetInterfaceData ð_data) { - // Extract data that contains MAC Address - const PropertiesMapType *macProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.MACAddress", dbus_data); - - if (macProperties != nullptr) { - eth_data.macAddress = - extractProperty<std::string>(*macProperties, "MACAddress"); + + // Helper function that allows to get pointer to the property from + // GetAllPropertiesType native, or extracted by GetAllPropertiesType + template <typename T> + inline T const *const extractProperty(const PropertiesMapType &properties, + const std::string &name) + { + const auto &property = properties.find(name); + if (property != properties.end()) + { + return mapbox::getPtr<const T>(property->second); + } + return nullptr; } + // TODO(Pawel) Consider to move the above functions to dbus + // generic_interfaces.hpp + + // Helper function that extracts data from several dbus objects and several + // interfaces required by single ethernet interface instance + void extractEthernetInterfaceData(const std::string ðifaceId, + const GetManagedObjectsType &dbus_data, + EthernetInterfaceData ð_data) + { + // Extract data that contains MAC Address + const PropertiesMapType *macProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.MACAddress", dbus_data); + + if (macProperties != nullptr) + { + eth_data.macAddress = + extractProperty<std::string>(*macProperties, "MACAddress"); + } - const PropertiesMapType *vlanProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.VLAN", dbus_data); + const PropertiesMapType *vlanProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.VLAN", dbus_data); - if (vlanProperties != nullptr) { - eth_data.vlanId = extractProperty<unsigned int>(*vlanProperties, "Id"); - } + if (vlanProperties != nullptr) + { + eth_data.vlanId = + extractProperty<unsigned int>(*vlanProperties, "Id"); + } - // Extract data that contains link information (auto negotiation and speed) - const PropertiesMapType *ethProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/" + ethifaceId, - "xyz.openbmc_project.Network.EthernetInterface", dbus_data); + // Extract data that contains link information (auto negotiation and + // speed) + const PropertiesMapType *ethProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/" + ethifaceId, + "xyz.openbmc_project.Network.EthernetInterface", dbus_data); + + if (ethProperties != nullptr) + { + eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg"); + eth_data.speed = + extractProperty<unsigned int>(*ethProperties, "Speed"); + } - if (ethProperties != nullptr) { - eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg"); - eth_data.speed = extractProperty<unsigned int>(*ethProperties, "Speed"); + // Extract data that contains network config (HostName and DefaultGW) + const PropertiesMapType *configProperties = extractInterfaceProperties( + "/xyz/openbmc_project/network/config", + "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); + + if (configProperties != nullptr) + { + eth_data.hostname = + extractProperty<std::string>(*configProperties, "HostName"); + eth_data.defaultGateway = extractProperty<std::string>( + *configProperties, "DefaultGateway"); + } } - // Extract data that contains network config (HostName and DefaultGW) - const PropertiesMapType *configProperties = extractInterfaceProperties( - "/xyz/openbmc_project/network/config", - "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); - - if (configProperties != nullptr) { - eth_data.hostname = - extractProperty<std::string>(*configProperties, "HostName"); - eth_data.defaultGateway = - extractProperty<std::string>(*configProperties, "DefaultGateway"); + // Helper function that changes bits netmask notation (i.e. /24) + // into full dot notation + inline std::string getNetmask(unsigned int bits) + { + uint32_t value = 0xffffffff << (32 - bits); + std::string netmask = std::to_string((value >> 24) & 0xff) + "." + + std::to_string((value >> 16) & 0xff) + "." + + std::to_string((value >> 8) & 0xff) + "." + + std::to_string(value & 0xff); + return netmask; } - } - - // Helper function that changes bits netmask notation (i.e. /24) - // into full dot notation - inline std::string getNetmask(unsigned int bits) { - uint32_t value = 0xffffffff << (32 - bits); - std::string netmask = std::to_string((value >> 24) & 0xff) + "." + - std::to_string((value >> 16) & 0xff) + "." + - std::to_string((value >> 8) & 0xff) + "." + - std::to_string(value & 0xff); - return netmask; - } - - // Helper function that extracts data for single ethernet ipv4 address - void extractIPv4Data(const std::string ðifaceId, - const GetManagedObjectsType &dbus_data, - std::vector<IPv4AddressData> &ipv4_config) { - const std::string pathStart = - "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/"; - - // Since there might be several IPv4 configurations aligned with - // single ethernet interface, loop over all of them - for (auto &objpath : dbus_data) { - // Check if proper patter for object path appears - if (boost::starts_with(static_cast<const std::string &>(objpath.first), - pathStart)) { - // and get approrpiate interface - const auto &interface = - objpath.second.find("xyz.openbmc_project.Network.IP"); - if (interface != objpath.second.end()) { - // Make a properties 'shortcut', to make everything more readable - const PropertiesMapType &properties = interface->second; - // Instance IPv4AddressData structure, and set as appropriate - IPv4AddressData ipv4Address; - - ipv4Address.id = static_cast<const std::string &>(objpath.first) - .substr(pathStart.size()); - - // IPv4 address - ipv4Address.address = - extractProperty<std::string>(properties, "Address"); - // IPv4 gateway - ipv4Address.gateway = - extractProperty<std::string>(properties, "Gateway"); - - // Origin is kind of DBus object so fetch pointer... - const std::string *origin = - extractProperty<std::string>(properties, "Origin"); - if (origin != nullptr) { - ipv4Address.origin = - translateAddressOriginBetweenDBusAndRedfish(origin, true, true); - } - - // Netmask is presented as PrefixLength - const auto *mask = - extractProperty<uint8_t>(properties, "PrefixLength"); - if (mask != nullptr) { - // convert it to the string - ipv4Address.netmask = getNetmask(*mask); - } - - // Attach IPv4 only if address is present - if (ipv4Address.address != nullptr) { - // Check if given address is local, or global - if (boost::starts_with(*ipv4Address.address, "169.254")) { - ipv4Address.global = false; - } else { - ipv4Address.global = true; + + // Helper function that extracts data for single ethernet ipv4 address + void extractIPv4Data(const std::string ðifaceId, + const GetManagedObjectsType &dbus_data, + std::vector<IPv4AddressData> &ipv4_config) + { + const std::string pathStart = + "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/"; + + // Since there might be several IPv4 configurations aligned with + // single ethernet interface, loop over all of them + for (auto &objpath : dbus_data) + { + // Check if proper patter for object path appears + if (boost::starts_with( + static_cast<const std::string &>(objpath.first), pathStart)) + { + // and get approrpiate interface + const auto &interface = + objpath.second.find("xyz.openbmc_project.Network.IP"); + if (interface != objpath.second.end()) + { + // Make a properties 'shortcut', to make everything more + // readable + const PropertiesMapType &properties = interface->second; + // Instance IPv4AddressData structure, and set as + // appropriate + IPv4AddressData ipv4Address; + + ipv4Address.id = + static_cast<const std::string &>(objpath.first) + .substr(pathStart.size()); + + // IPv4 address + ipv4Address.address = + extractProperty<std::string>(properties, "Address"); + // IPv4 gateway + ipv4Address.gateway = + extractProperty<std::string>(properties, "Gateway"); + + // Origin is kind of DBus object so fetch pointer... + const std::string *origin = + extractProperty<std::string>(properties, "Origin"); + if (origin != nullptr) + { + ipv4Address.origin = + translateAddressOriginBetweenDBusAndRedfish( + origin, true, true); + } + + // Netmask is presented as PrefixLength + const auto *mask = + extractProperty<uint8_t>(properties, "PrefixLength"); + if (mask != nullptr) + { + // convert it to the string + ipv4Address.netmask = getNetmask(*mask); + } + + // Attach IPv4 only if address is present + if (ipv4Address.address != nullptr) + { + // Check if given address is local, or global + if (boost::starts_with(*ipv4Address.address, "169.254")) + { + ipv4Address.global = false; + } + else + { + ipv4Address.global = true; + } + ipv4_config.emplace_back(std::move(ipv4Address)); + } + } } - ipv4_config.emplace_back(std::move(ipv4Address)); - } } - } + + /** + * We have to sort this vector and ensure that order of IPv4 addresses + * is consistent between GETs to allow modification and deletion in + * PATCHes + */ + std::sort(ipv4_config.begin(), ipv4_config.end()); } + static const constexpr int ipV4AddressSectionsCount = 4; + + public: /** - * We have to sort this vector and ensure that order of IPv4 addresses - * is consistent between GETs to allow modification and deletion in PATCHes + * @brief Creates VLAN for given interface with given Id through D-Bus + * + * @param[in] ifaceId Id of interface for which VLAN will be created + * @param[in] inputVlanId ID of the new VLAN + * @param[in] callback Function that will be called after the operation + * + * @return None. */ - std::sort(ipv4_config.begin(), ipv4_config.end()); - } - - static const constexpr int ipV4AddressSectionsCount = 4; - - public: - /** - * @brief Creates VLAN for given interface with given Id through D-Bus - * - * @param[in] ifaceId Id of interface for which VLAN will be created - * @param[in] inputVlanId ID of the new VLAN - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, - static_cast<uint32_t>(inputVlanId)); - }; - - /** - * @brief Sets given Id on the given VLAN interface through D-Bus - * - * @param[in] ifaceId Id of VLAN interface that should be modified - * @param[in] inputVlanId New ID of the VLAN - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - static void changeVlanId(const std::string &ifaceId, - const uint32_t &inputVlanId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.VLAN", "Id", - sdbusplus::message::variant<uint32_t>(inputVlanId)); - }; - - /** - * @brief Helper function that verifies IP address to check if it is in - * proper format. If bits pointer is provided, also calculates active - * bit count for Subnet Mask. - * - * @param[in] ip IP that will be verified - * @param[out] bits Calculated mask in bits notation - * - * @return true in case of success, false otherwise - */ - bool ipv4VerifyIpAndGetBitcount(const std::string &ip, - uint8_t *bits = nullptr) { - std::vector<std::string> bytesInMask; - - boost::split(bytesInMask, ip, boost::is_any_of(".")); - - if (bytesInMask.size() != ipV4AddressSectionsCount) { - return false; - } + template <typename CallbackFunc> + void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network", + "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, + static_cast<uint32_t>(inputVlanId)); + }; - if (bits != nullptr) { - *bits = 0; - } + /** + * @brief Sets given Id on the given VLAN interface through D-Bus + * + * @param[in] ifaceId Id of VLAN interface that should be modified + * @param[in] inputVlanId New ID of the VLAN + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + static void changeVlanId(const std::string &ifaceId, + const uint32_t &inputVlanId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + ifaceId, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.VLAN", "Id", + sdbusplus::message::variant<uint32_t>(inputVlanId)); + }; + + /** + * @brief Helper function that verifies IP address to check if it is in + * proper format. If bits pointer is provided, also calculates active + * bit count for Subnet Mask. + * + * @param[in] ip IP that will be verified + * @param[out] bits Calculated mask in bits notation + * + * @return true in case of success, false otherwise + */ + bool ipv4VerifyIpAndGetBitcount(const std::string &ip, + uint8_t *bits = nullptr) + { + std::vector<std::string> bytesInMask; + + boost::split(bytesInMask, ip, boost::is_any_of(".")); - char *endPtr; - long previousValue = 255; - bool firstZeroInByteHit; - for (const std::string &byte : bytesInMask) { - if (byte.empty()) { - return false; - } - - // Use strtol instead of stroi to avoid exceptions - long value = std::strtol(byte.c_str(), &endPtr, 10); - - // endPtr should point to the end of the string, otherwise given string - // is not 100% number - if (*endPtr != '\0') { - return false; - } - - // Value should be contained in byte - if (value < 0 || value > 255) { - return false; - } - - if (bits != nullptr) { - // Mask has to be continuous between bytes - if (previousValue != 255 && value != 0) { - return false; + if (bytesInMask.size() != ipV4AddressSectionsCount) + { + return false; } - // Mask has to be continuous inside bytes - firstZeroInByteHit = false; - - // Count bits - for (int bitIdx = 7; bitIdx >= 0; bitIdx--) { - if (value & (1 << bitIdx)) { - if (firstZeroInByteHit) { - // Continuity not preserved - return false; - } else { - (*bits)++; + if (bits != nullptr) + { + *bits = 0; + } + + char *endPtr; + long previousValue = 255; + bool firstZeroInByteHit; + for (const std::string &byte : bytesInMask) + { + if (byte.empty()) + { + return false; + } + + // Use strtol instead of stroi to avoid exceptions + long value = std::strtol(byte.c_str(), &endPtr, 10); + + // endPtr should point to the end of the string, otherwise given + // string is not 100% number + if (*endPtr != '\0') + { + return false; + } + + // Value should be contained in byte + if (value < 0 || value > 255) + { + return false; } - } else { - firstZeroInByteHit = true; - } + + if (bits != nullptr) + { + // Mask has to be continuous between bytes + if (previousValue != 255 && value != 0) + { + return false; + } + + // Mask has to be continuous inside bytes + firstZeroInByteHit = false; + + // Count bits + for (int bitIdx = 7; bitIdx >= 0; bitIdx--) + { + if (value & (1 << bitIdx)) + { + if (firstZeroInByteHit) + { + // Continuity not preserved + return false; + } + else + { + (*bits)++; + } + } + else + { + firstZeroInByteHit = true; + } + } + } + + previousValue = value; } - } - previousValue = value; + return true; } - return true; - } - - /** - * @brief Changes IPv4 address type property (Address, Gateway) - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] name Name of field in JSON representation - * @param[in] newValue New value that should be written - * @param[io] asyncResp Response object that will be returned to client - * - * @return true if give IP is valid and has been sent do D-Bus, false - * otherwise - */ - void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, - const std::string &ipHash, - const std::string &name, - const std::string &newValue, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = [ - asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)}, - newValue{std::move(newValue)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue; - } + /** + * @brief Changes IPv4 address type property (Address, Gateway) + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] name Name of field in JSON representation + * @param[in] newValue New value that should be written + * @param[io] asyncResp Response object that will be returned to client + * + * @return true if give IP is valid and has been sent do D-Bus, false + * otherwise + */ + void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, + const std::string &ipHash, + const std::string &name, + const std::string &newValue, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + name{std::move(name)}, newValue{std::move(newValue)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = + newValue; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", name, + sdbusplus::message::variant<std::string>(newValue)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", name, - sdbusplus::message::variant<std::string>(newValue)); - }; - - /** - * @brief Changes IPv4 address origin property - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] newValue New value in Redfish format - * @param[in] newValueDbus New value in D-Bus format - * @param[io] asyncResp Response object that will be returned to client - * - * @return true if give IP is valid and has been sent do D-Bus, false - * otherwise - */ - void changeIPv4Origin(const std::string &ifaceId, int ipIdx, - const std::string &ipHash, const std::string &newValue, - const std::string &newValueDbus, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = - [ asyncResp, ipIdx{std::move(ipIdx)}, - newValue{std::move(newValue)} ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = - newValue; - } + /** + * @brief Changes IPv4 address origin property + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be + * modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] newValue New value in Redfish format + * @param[in] newValueDbus New value in D-Bus format + * @param[io] asyncResp Response object that will be returned to client + * + * @return true if give IP is valid and has been sent do D-Bus, false + * otherwise + */ + void changeIPv4Origin(const std::string &ifaceId, int ipIdx, + const std::string &ipHash, + const std::string &newValue, + const std::string &newValueDbus, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + newValue{std::move(newValue)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + + "/AddressOrigin"); + } + else + { + asyncResp->res + .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = + newValue; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "Origin", + sdbusplus::message::variant<std::string>(newValueDbus)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", "Origin", - sdbusplus::message::variant<std::string>(newValueDbus)); - }; - - /** - * @brief Modifies SubnetMask for given IP - * - * @param[in] ifaceId Id of interface whose IP should be modified - * @param[in] ipIdx index of IP in input array that should be modified - * @param[in] ipHash DBus Hash id of modified IP - * @param[in] newValueStr Mask in dot notation as string - * @param[in] newValue Mask as PrefixLength in bitcount - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void changeIPv4SubnetMaskProperty( - const std::string &ifaceId, int ipIdx, const std::string &ipHash, - const std::string &newValueStr, uint8_t &newValue, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto callback = [ - asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = - newValueStr; - } + /** + * @brief Modifies SubnetMask for given IP + * + * @param[in] ifaceId Id of interface whose IP should be modified + * @param[in] ipIdx index of IP in input array that should be + * modified + * @param[in] ipHash DBus Hash id of modified IP + * @param[in] newValueStr Mask in dot notation as string + * @param[in] newValue Mask as PrefixLength in bitcount + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void changeIPv4SubnetMaskProperty( + const std::string &ifaceId, int ipIdx, const std::string &ipHash, + const std::string &newValueStr, uint8_t &newValue, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, + newValueStr{std::move(newValueStr)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = + newValueStr; + } + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "PrefixLength", + sdbusplus::message::variant<uint8_t>(newValue)); }; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.IP", "PrefixLength", - sdbusplus::message::variant<uint8_t>(newValue)); - }; - - /** - * @brief Disables VLAN with given ifaceId - * - * @param[in] ifaceId Id of VLAN interface that should be disabled - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "xyz.openbmc_project.Object.Delete", "Delete"); - }; - - /** - * @brief Sets given HostName of the machine through D-Bus - * - * @param[in] newHostname New name that HostName will be changed to - * @param[in] callback Function that will be called after the operation - * - * @return None. - */ - template <typename CallbackFunc> - void setHostName(const std::string &newHostname, CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - callback, "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/config", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.SystemConfiguration", "HostName", - sdbusplus::message::variant<std::string>(newHostname)); - }; - - /** - * @brief Deletes given IPv4 - * - * @param[in] ifaceId Id of interface whose IP should be deleted - * @param[in] ipIdx index of IP in input array that should be deleted - * @param[in] ipHash DBus Hash id of IP that should be deleted - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, - unsigned int ipIdx, - const std::shared_ptr<AsyncResp> &asyncResp) { - crow::connections::systemBus->async_method_call( - [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); - } else { - asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; - } - }, - "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, - "xyz.openbmc_project.Object.Delete", "Delete"); - } - - /** - * @brief Creates IPv4 with given data - * - * @param[in] ifaceId Id of interface whose IP should be deleted - * @param[in] ipIdx index of IP in input array that should be deleted - * @param[in] ipHash DBus Hash id of IP that should be deleted - * @param[io] asyncResp Response object that will be returned to client - * - * @return None - */ - void createIPv4(const std::string &ifaceId, unsigned int ipIdx, - uint8_t subnetMask, const std::string &gateway, - const std::string &address, - const std::shared_ptr<AsyncResp> &asyncResp) { - auto createIpHandler = [ - ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} - ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::internalError(), - "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); - } + /** + * @brief Disables VLAN with given ifaceId + * + * @param[in] ifaceId Id of VLAN interface that should be disabled + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + ifaceId, + "xyz.openbmc_project.Object.Delete", "Delete"); }; - crow::connections::systemBus->async_method_call( - std::move(createIpHandler), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId, - "xyz.openbmc_project.Network.IP.Create", "IP", - "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, - gateway); - } - - /** - * @brief Translates Address Origin value from D-Bus to Redfish format and - * vice-versa - * - * @param[in] inputOrigin Input value that should be translated - * @param[in] isIPv4 True for IPv4 origins, False for IPv6 - * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse - * - * @return Empty string in case of failure, translated value otherwise - */ - std::string translateAddressOriginBetweenDBusAndRedfish( - const std::string *inputOrigin, bool isIPv4, bool isFromDBus) { - // Invalid pointer - if (inputOrigin == nullptr) { - return ""; + /** + * @brief Sets given HostName of the machine through D-Bus + * + * @param[in] newHostname New name that HostName will be changed to + * @param[in] callback Function that will be called after the operation + * + * @return None. + */ + template <typename CallbackFunc> + void setHostName(const std::string &newHostname, CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + callback, "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/config", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.SystemConfiguration", "HostName", + sdbusplus::message::variant<std::string>(newHostname)); + }; + + /** + * @brief Deletes given IPv4 + * + * @param[in] ifaceId Id of interface whose IP should be deleted + * @param[in] ipIdx index of IP in input array that should be deleted + * @param[in] ipHash DBus Hash id of IP that should be deleted + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, + unsigned int ipIdx, + const std::shared_ptr<AsyncResp> &asyncResp) + { + crow::connections::systemBus->async_method_call( + [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); + } + else + { + asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; + } + }, + "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, + "xyz.openbmc_project.Object.Delete", "Delete"); } - static const constexpr unsigned int firstIPv4OnlyIdx = 1; - static const constexpr unsigned int firstIPv6OnlyIdx = 3; - - std::array<std::pair<const char *, const char *>, 6> translationTable{ - {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", - "IPv4LinkLocal"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", - "LinkLocal"}, - {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; - - for (unsigned int i = 0; i < translationTable.size(); i++) { - // Skip unrelated - if (isIPv4 && i >= firstIPv6OnlyIdx) break; - if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue; - - // When translating D-Bus to Redfish compare input to first element - if (isFromDBus && translationTable[i].first == *inputOrigin) - return translationTable[i].second; - - // When translating Redfish to D-Bus compare input to second element - if (!isFromDBus && translationTable[i].second == *inputOrigin) - return translationTable[i].first; + /** + * @brief Creates IPv4 with given data + * + * @param[in] ifaceId Id of interface whose IP should be deleted + * @param[in] ipIdx index of IP in input array that should be deleted + * @param[in] ipHash DBus Hash id of IP that should be deleted + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ + void createIPv4(const std::string &ifaceId, unsigned int ipIdx, + uint8_t subnetMask, const std::string &gateway, + const std::string &address, + const std::shared_ptr<AsyncResp> &asyncResp) + { + auto createIpHandler = [ipIdx{std::move(ipIdx)}, + asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, messages::internalError(), + "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); + } + }; + + crow::connections::systemBus->async_method_call( + std::move(createIpHandler), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId, + "xyz.openbmc_project.Network.IP.Create", "IP", + "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, + gateway); } - // If we are still here, that means that value has not been found - return ""; - } - - /** - * Function that retrieves all properties for given Ethernet Interface - * Object - * from EntityManager Network Manager - * @param ethifaceId a eth interface id to query on DBus - * @param callback a function that shall be called to convert Dbus output - * into JSON - */ - template <typename CallbackFunc> - void getEthernetIfaceData(const std::string ðifaceId, - CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - [ - this, ethifaceId{std::move(ethifaceId)}, callback{std::move(callback)} - ](const boost::system::error_code error_code, - const GetManagedObjectsType &resp) { - EthernetInterfaceData ethData{}; - std::vector<IPv4AddressData> ipv4Data; - ipv4Data.reserve(maxIpV4AddressesPerInterface); - - if (error_code) { - // Something wrong on DBus, the error_code is not important at - // this moment, just return success=false, and empty output. Since - // size of vector may vary depending on information from Network - // Manager, and empty output could not be treated same way as - // error. - callback(false, ethData, ipv4Data); - return; - } + /** + * @brief Translates Address Origin value from D-Bus to Redfish format and + * vice-versa + * + * @param[in] inputOrigin Input value that should be translated + * @param[in] isIPv4 True for IPv4 origins, False for IPv6 + * @param[in] isFromDBus True for DBus->Redfish conversion, false for + * reverse + * + * @return Empty string in case of failure, translated value otherwise + */ + std::string translateAddressOriginBetweenDBusAndRedfish( + const std::string *inputOrigin, bool isIPv4, bool isFromDBus) + { + // Invalid pointer + if (inputOrigin == nullptr) + { + return ""; + } - // Find interface - if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) == - resp.end()) { - // Interface has not been found - callback(false, ethData, ipv4Data); - return; - } + static const constexpr unsigned int firstIPv4OnlyIdx = 1; + static const constexpr unsigned int firstIPv6OnlyIdx = 3; + + std::array<std::pair<const char *, const char *>, 6> translationTable{ + {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", + "IPv4LinkLocal"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", + "LinkLocal"}, + {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; + + for (unsigned int i = 0; i < translationTable.size(); i++) + { + // Skip unrelated + if (isIPv4 && i >= firstIPv6OnlyIdx) + break; + if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) + continue; + + // When translating D-Bus to Redfish compare input to first element + if (isFromDBus && translationTable[i].first == *inputOrigin) + return translationTable[i].second; + + // When translating Redfish to D-Bus compare input to second element + if (!isFromDBus && translationTable[i].second == *inputOrigin) + return translationTable[i].first; + } - extractEthernetInterfaceData(ethifaceId, resp, ethData); - extractIPv4Data(ethifaceId, resp, ipv4Data); + // If we are still here, that means that value has not been found + return ""; + } - // Fix global GW - for (IPv4AddressData &ipv4 : ipv4Data) { - if ((ipv4.global) && - ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { - ipv4.gateway = ethData.defaultGateway; - } - } - - // Finally make a callback with useful data - callback(true, ethData, ipv4Data); - }, - "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; - - /** - * Function that retrieves all Ethernet Interfaces available through Network - * Manager - * @param callback a function that shall be called to convert Dbus output into - * JSON. - */ - template <typename CallbackFunc> - void getEthernetIfaceList(CallbackFunc &&callback) { - crow::connections::systemBus->async_method_call( - [ this, callback{std::move(callback)} ]( - const boost::system::error_code error_code, - GetManagedObjectsType &resp) { - // Callback requires vector<string> to retrieve all available ethernet - // interfaces - std::vector<std::string> ifaceList; - ifaceList.reserve(resp.size()); - if (error_code) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Network Manager, - // and empty output could not be treated same way as error. - callback(false, ifaceList); - return; - } - - // Iterate over all retrieved ObjectPaths. - for (auto &objpath : resp) { - // And all interfaces available for certain ObjectPath. - for (auto &interface : objpath.second) { - // If interface is xyz.openbmc_project.Network.EthernetInterface, - // this is what we're looking for. - if (interface.first == - "xyz.openbmc_project.Network.EthernetInterface") { - // Cut out everything until last "/", ... - const std::string &ifaceId = - static_cast<const std::string &>(objpath.first); - std::size_t lastPos = ifaceId.rfind("/"); - if (lastPos != std::string::npos) { - // and put it into output vector. - ifaceList.emplace_back(ifaceId.substr(lastPos + 1)); + /** + * Function that retrieves all properties for given Ethernet Interface + * Object + * from EntityManager Network Manager + * @param ethifaceId a eth interface id to query on DBus + * @param callback a function that shall be called to convert Dbus output + * into JSON + */ + template <typename CallbackFunc> + void getEthernetIfaceData(const std::string ðifaceId, + CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + [this, ethifaceId{std::move(ethifaceId)}, + callback{std::move(callback)}]( + const boost::system::error_code error_code, + const GetManagedObjectsType &resp) { + EthernetInterfaceData ethData{}; + std::vector<IPv4AddressData> ipv4Data; + ipv4Data.reserve(maxIpV4AddressesPerInterface); + + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Network Manager, and empty output could + // not be treated same way as error. + callback(false, ethData, ipv4Data); + return; } - } - } - } - // Finally make a callback with useful data - callback(true, ifaceList); - }, - "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; + + // Find interface + if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) == + resp.end()) + { + // Interface has not been found + callback(false, ethData, ipv4Data); + return; + } + + extractEthernetInterfaceData(ethifaceId, resp, ethData); + extractIPv4Data(ethifaceId, resp, ipv4Data); + + // Fix global GW + for (IPv4AddressData &ipv4 : ipv4Data) + { + if ((ipv4.global) && ((ipv4.gateway == nullptr) || + (*ipv4.gateway == "0.0.0.0"))) + { + ipv4.gateway = ethData.defaultGateway; + } + } + + // Finally make a callback with useful data + callback(true, ethData, ipv4Data); + }, + "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }; + + /** + * Function that retrieves all Ethernet Interfaces available through Network + * Manager + * @param callback a function that shall be called to convert Dbus output + * into JSON. + */ + template <typename CallbackFunc> + void getEthernetIfaceList(CallbackFunc &&callback) + { + crow::connections::systemBus->async_method_call( + [this, callback{std::move(callback)}]( + const boost::system::error_code error_code, + GetManagedObjectsType &resp) { + // Callback requires vector<string> to retrieve all available + // ethernet interfaces + std::vector<std::string> ifaceList; + ifaceList.reserve(resp.size()); + if (error_code) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Network Manager, and empty output could + // not be treated same way as error. + callback(false, ifaceList); + return; + } + + // Iterate over all retrieved ObjectPaths. + for (auto &objpath : resp) + { + // And all interfaces available for certain ObjectPath. + for (auto &interface : objpath.second) + { + // If interface is + // xyz.openbmc_project.Network.EthernetInterface, this + // is what we're looking for. + if (interface.first == + "xyz.openbmc_project.Network.EthernetInterface") + { + // Cut out everything until last "/", ... + const std::string &ifaceId = + static_cast<const std::string &>(objpath.first); + std::size_t lastPos = ifaceId.rfind("/"); + if (lastPos != std::string::npos) + { + // and put it into output vector. + ifaceList.emplace_back( + ifaceId.substr(lastPos + 1)); + } + } + } + } + // Finally make a callback with useful data + callback(true, ifaceList); + }, + "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }; }; /** * EthernetCollection derived class for delivering Ethernet Collection Schema */ -class EthernetCollection : public Node { - public: - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - EthernetCollection(CrowApp &app) - : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { - Node::json["@odata.type"] = - "#EthernetInterfaceCollection.EthernetInterfaceCollection"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; - Node::json["Name"] = "Ethernet Network Interface Collection"; - Node::json["Description"] = - "Collection of EthernetInterfaces for this Manager"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([&, managerId{std::move(managerId)} ]( - const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - nlohmann::json ifaceArray = nlohmann::json::array(); - for (const std::string &ifaceItem : iface_list) { - ifaceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + ifaceItem}}); - } - Node::json["Members"] = ifaceArray; - Node::json["Members@odata.count"] = ifaceArray.size(); +class EthernetCollection : public Node +{ + public: + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + EthernetCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") + { + Node::json["@odata.type"] = + "#EthernetInterfaceCollection.EthernetInterfaceCollection"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; Node::json["@odata.id"] = - "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces"; - res.jsonValue = Node::json; - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + "/redfish/v1/Managers/openbmc/EthernetInterfaces"; + Node::json["Name"] = "Ethernet Network Interface Collection"; + Node::json["Description"] = + "Collection of EthernetInterfaces for this Manager"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } + + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + nlohmann::json ifaceArray = nlohmann::json::array(); + for (const std::string &ifaceItem : iface_list) + { + ifaceArray.push_back( + {{"@odata.id", "/redfish/v1/Managers/" + managerId + + "/EthernetInterfaces/" + + ifaceItem}}); + } + Node::json["Members"] = ifaceArray; + Node::json["Members@odata.count"] = ifaceArray.size(); + Node::json["@odata.id"] = "/redfish/v1/Managers/" + + managerId + "/EthernetInterfaces"; + res.jsonValue = Node::json; + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); + } + + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; /** * EthernetInterface derived class for delivering Ethernet Schema */ -class EthernetInterface : public Node { - public: - /* - * Default Constructor - */ - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - EthernetInterface(CrowApp &app) - : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", - std::string()) { - Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; - Node::json["Name"] = "Manager Ethernet Interface"; - Node::json["Description"] = "Management Network Interface"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - // TODO(kkowalsk) Find a suitable class/namespace for this - static void handleVlanPatch(const std::string &ifaceId, - const nlohmann::json &input, - const EthernetInterfaceData ð_data, - const std::string &pathPrefix, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (!input.is_object()) { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix); - return; +class EthernetInterface : public Node +{ + public: + /* + * Default Constructor + */ + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + EthernetInterface(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", + std::string()) + { + Node::json["@odata.type"] = + "#EthernetInterface.v1_2_0.EthernetInterface"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; + Node::json["Name"] = "Manager Ethernet Interface"; + Node::json["Description"] = "Management Network Interface"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; - nlohmann::json ¶msJson = - (pathPrefix == "/") - ? asyncResp->res.jsonValue - : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>( - pathPrefix)]; - bool inputVlanEnabled; - uint64_t inputVlanId; - - json_util::Result inputVlanEnabledState = json_util::getBool( - "VLANEnable", input, inputVlanEnabled, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable")); - json_util::Result inputVlanIdState = json_util::getUnsigned( - "VLANId", input, inputVlanId, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, std::string(pathStart + "/VLANId")); - bool inputInvalid = false; - - // Do not proceed if fields in VLAN object were of wrong type - if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || - inputVlanIdState == json_util::Result::WRONG_TYPE) { - return; - } + // TODO(kkowalsk) Find a suitable class/namespace for this + static void handleVlanPatch(const std::string &ifaceId, + const nlohmann::json &input, + const EthernetInterfaceData ð_data, + const std::string &pathPrefix, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (!input.is_object()) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "VLAN"), + pathPrefix); + return; + } - // Verify input - if (eth_data.vlanId == nullptr) { - // This interface is not a VLAN. Cannot do anything with it - // TODO(kkowalsk) Change this message - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::propertyMissing("VLANEnable"), - pathPrefix); - - inputInvalid = true; - } else { - // Load actual data into field values if they were not provided - if (inputVlanEnabledState == json_util::Result::NOT_EXIST) { - inputVlanEnabled = true; - } - - if (inputVlanIdState == json_util::Result::NOT_EXIST) { - inputVlanId = *eth_data.vlanId; - } - } + const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; + nlohmann::json ¶msJson = + (pathPrefix == "/") + ? asyncResp->res.jsonValue + : asyncResp->res + .jsonValue[nlohmann::json_pointer<nlohmann::json>( + pathPrefix)]; + bool inputVlanEnabled; + uint64_t inputVlanId; + + json_util::Result inputVlanEnabledState = json_util::getBool( + "VLANEnable", input, inputVlanEnabled, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable")); + json_util::Result inputVlanIdState = json_util::getUnsigned( + "VLANId", input, inputVlanId, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, std::string(pathStart + "/VLANId")); + bool inputInvalid = false; + + // Do not proceed if fields in VLAN object were of wrong type + if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || + inputVlanIdState == json_util::Result::WRONG_TYPE) + { + return; + } - // Do not proceed if input has not been valid - if (inputInvalid) { - return; - } + // Verify input + if (eth_data.vlanId == nullptr) + { + // This interface is not a VLAN. Cannot do anything with it + // TODO(kkowalsk) Change this message + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::propertyMissing("VLANEnable"), + pathPrefix); - // VLAN is configured on the interface - if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) { - // Change VLAN Id - paramsJson["VLANId"] = inputVlanId; - OnDemandEthernetProvider::changeVlanId( - ifaceId, static_cast<uint32_t>(inputVlanId), - [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), pathPrefix); - } else { - paramsJson["VLANEnable"] = true; + inputInvalid = true; + } + else + { + // Load actual data into field values if they were not provided + if (inputVlanEnabledState == json_util::Result::NOT_EXIST) + { + inputVlanEnabled = true; } - }); - } else if (inputVlanEnabled == false) { - // Disable VLAN - OnDemandEthernetProvider::disableVlan( - ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( - const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), pathPrefix); - } else { - paramsJson["VLANEnable"] = false; + + if (inputVlanIdState == json_util::Result::NOT_EXIST) + { + inputVlanId = *eth_data.vlanId; } - }); - } - } + } - private: - void handleHostnamePatch(const nlohmann::json &input, - const EthernetInterfaceData ð_data, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (input.is_string()) { - std::string newHostname = input.get<std::string>(); - - if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) { - // Change hostname - ethernetProvider.setHostName( - newHostname, - [asyncResp, newHostname](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), - "/HostName"); - } else { - asyncResp->res.jsonValue["HostName"] = newHostname; - } - }); - } - } else { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "HostName"), - "/HostName"); - } - } - - void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input, - const std::vector<IPv4AddressData> &ipv4_data, - const std::shared_ptr<AsyncResp> &asyncResp) { - if (!input.is_array()) { - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), - "/IPv4Addresses"); - return; - } + // Do not proceed if input has not been valid + if (inputInvalid) + { + return; + } - // According to Redfish PATCH definition, size must be at least equal - if (input.size() < ipv4_data.size()) { - // TODO(kkowalsk) This should be a message indicating that not enough - // data has been provided - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), "/IPv4Addresses"); - return; + // VLAN is configured on the interface + if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) + { + // Change VLAN Id + paramsJson["VLANId"] = inputVlanId; + OnDemandEthernetProvider::changeVlanId( + ifaceId, static_cast<uint32_t>(inputVlanId), + [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + pathPrefix); + } + else + { + paramsJson["VLANEnable"] = true; + } + }); + } + else if (inputVlanEnabled == false) + { + // Disable VLAN + OnDemandEthernetProvider::disableVlan( + ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + pathPrefix); + } + else + { + paramsJson["VLANEnable"] = false; + } + }); + } } - json_util::Result addressFieldState; - json_util::Result subnetMaskFieldState; - json_util::Result addressOriginFieldState; - json_util::Result gatewayFieldState; - const std::string *addressFieldValue; - const std::string *subnetMaskFieldValue; - const std::string *addressOriginFieldValue = nullptr; - const std::string *gatewayFieldValue; - uint8_t subnetMaskAsPrefixLength; - std::string addressOriginInDBusFormat; - - bool errorDetected = false; - for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) { - // Check that entry is not of some unexpected type - if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) { - // Invalid object type - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueTypeError(input[entryIdx].dump(), - "IPv4Address"), - "/IPv4Addresses/" + std::to_string(entryIdx)); - - continue; - } - - // Try to load fields - addressFieldState = json_util::getString( - "Address", input[entryIdx], addressFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); - subnetMaskFieldState = json_util::getString( - "SubnetMask", input[entryIdx], subnetMaskFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); - addressOriginFieldState = json_util::getString( - "AddressOrigin", input[entryIdx], addressOriginFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); - gatewayFieldState = json_util::getString( - "Gateway", input[entryIdx], gatewayFieldValue, - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - asyncResp->res.jsonValue, - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); - - if (addressFieldState == json_util::Result::WRONG_TYPE || - subnetMaskFieldState == json_util::Result::WRONG_TYPE || - addressOriginFieldState == json_util::Result::WRONG_TYPE || - gatewayFieldState == json_util::Result::WRONG_TYPE) { - return; - } - - if (addressFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*addressFieldValue, "Address"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); - } - - if (subnetMaskFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount( - *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*subnetMaskFieldValue, - "SubnetMask"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); - } - - // get Address origin in proper format - addressOriginInDBusFormat = - ethernetProvider.translateAddressOriginBetweenDBusAndRedfish( - addressOriginFieldValue, true, false); - - if (addressOriginFieldState == json_util::Result::SUCCESS && - addressOriginInDBusFormat.empty()) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueNotInList(*addressOriginFieldValue, - "AddressOrigin"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); - } - - if (gatewayFieldState == json_util::Result::SUCCESS && - !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); - } - - // If any error occured do not proceed with current entry, but do not - // end loop - if (errorDetected) { - errorDetected = false; - continue; - } - - if (entryIdx >= ipv4_data.size()) { - asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx]; - - // Verify that all field were provided - if (addressFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("Address"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + private: + void handleHostnamePatch(const nlohmann::json &input, + const EthernetInterfaceData ð_data, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (input.is_string()) + { + std::string newHostname = input.get<std::string>(); + + if (eth_data.hostname == nullptr || + newHostname != *eth_data.hostname) + { + // Change hostname + ethernetProvider.setHostName( + newHostname, [asyncResp, newHostname]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::internalError(), "/HostName"); + } + else + { + asyncResp->res.jsonValue["HostName"] = newHostname; + } + }); + } } - - if (subnetMaskFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); + else + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "HostName"), + "/HostName"); } + } - if (addressOriginFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, - messages::propertyMissing("AddressOrigin"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); + void handleIPv4Patch(const std::string &ifaceId, + const nlohmann::json &input, + const std::vector<IPv4AddressData> &ipv4_data, + const std::shared_ptr<AsyncResp> &asyncResp) + { + if (!input.is_array()) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), + "/IPv4Addresses"); + return; } - if (gatewayFieldState == json_util::Result::NOT_EXIST) { - errorDetected = true; - messages::addMessageToJson( - asyncResp->res.jsonValue, messages::propertyMissing("Gateway"), - "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + // According to Redfish PATCH definition, size must be at least equal + if (input.size() < ipv4_data.size()) + { + // TODO(kkowalsk) This should be a message indicating that not + // enough data has been provided + messages::addMessageToJson(asyncResp->res.jsonValue, + messages::internalError(), + "/IPv4Addresses"); + return; } - // If any error occured do not proceed with current entry, but do not - // end loop - if (errorDetected) { - errorDetected = false; - continue; - } + json_util::Result addressFieldState; + json_util::Result subnetMaskFieldState; + json_util::Result addressOriginFieldState; + json_util::Result gatewayFieldState; + const std::string *addressFieldValue; + const std::string *subnetMaskFieldValue; + const std::string *addressOriginFieldValue = nullptr; + const std::string *gatewayFieldValue; + uint8_t subnetMaskAsPrefixLength; + std::string addressOriginInDBusFormat; + + bool errorDetected = false; + for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) + { + // Check that entry is not of some unexpected type + if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) + { + // Invalid object type + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueTypeError(input[entryIdx].dump(), + "IPv4Address"), + "/IPv4Addresses/" + std::to_string(entryIdx)); + + continue; + } - // Create IPv4 with provided data - ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength, - *gatewayFieldValue, *addressFieldValue, - asyncResp); - } else { - // Existing object that should be modified/deleted/remain unchanged - if (input[entryIdx].is_null()) { - // Object should be deleted - ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx, - asyncResp); - } else if (input[entryIdx].is_object()) { - if (input[entryIdx].size() == 0) { - // Object shall remain unchanged - continue; - } - - // Apply changes - if (addressFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].address != nullptr && - *ipv4_data[entryIdx].address != *addressFieldValue) { - ethernetProvider.changeIPv4AddressProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address", - *addressFieldValue, asyncResp); - } - - if (subnetMaskFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) { - ethernetProvider.changeIPv4SubnetMaskProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, - *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp); - } - - if (addressOriginFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].origin != *addressFieldValue) { - ethernetProvider.changeIPv4Origin( - ifaceId, entryIdx, ipv4_data[entryIdx].id, - *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp); - } - - if (gatewayFieldState == json_util::Result::SUCCESS && - ipv4_data[entryIdx].gateway != nullptr && - *ipv4_data[entryIdx].gateway != *gatewayFieldValue) { - ethernetProvider.changeIPv4AddressProperty( - ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway", - *gatewayFieldValue, asyncResp); - } + // Try to load fields + addressFieldState = json_util::getString( + "Address", input[entryIdx], addressFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + subnetMaskFieldState = json_util::getString( + "SubnetMask", input[entryIdx], subnetMaskFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); + addressOriginFieldState = json_util::getString( + "AddressOrigin", input[entryIdx], addressOriginFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + gatewayFieldState = json_util::getString( + "Gateway", input[entryIdx], gatewayFieldValue, + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + asyncResp->res.jsonValue, + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + + if (addressFieldState == json_util::Result::WRONG_TYPE || + subnetMaskFieldState == json_util::Result::WRONG_TYPE || + addressOriginFieldState == json_util::Result::WRONG_TYPE || + gatewayFieldState == json_util::Result::WRONG_TYPE) + { + return; + } + + if (addressFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *addressFieldValue)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*addressFieldValue, + "Address"), + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); + } + + if (subnetMaskFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*subnetMaskFieldValue, + "SubnetMask"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/SubnetMask"); + } + + // get Address origin in proper format + addressOriginInDBusFormat = + ethernetProvider.translateAddressOriginBetweenDBusAndRedfish( + addressOriginFieldValue, true, false); + + if (addressOriginFieldState == json_util::Result::SUCCESS && + addressOriginInDBusFormat.empty()) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueNotInList(*addressOriginFieldValue, + "AddressOrigin"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + } + + if (gatewayFieldState == json_util::Result::SUCCESS && + !ethernetProvider.ipv4VerifyIpAndGetBitcount( + *gatewayFieldValue)) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyValueFormatError(*gatewayFieldValue, + "Gateway"), + "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); + } + + // If any error occured do not proceed with current entry, but do + // not end loop + if (errorDetected) + { + errorDetected = false; + continue; + } + + if (entryIdx >= ipv4_data.size()) + { + asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = + input[entryIdx]; + + // Verify that all field were provided + if (addressFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("Address"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/Address"); + } + + if (subnetMaskFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("SubnetMask"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/SubnetMask"); + } + + if (addressOriginFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("AddressOrigin"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/AddressOrigin"); + } + + if (gatewayFieldState == json_util::Result::NOT_EXIST) + { + errorDetected = true; + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::propertyMissing("Gateway"), + "/IPv4Addresses/" + std::to_string(entryIdx) + + "/Gateway"); + } + + // If any error occured do not proceed with current entry, but + // do not end loop + if (errorDetected) + { + errorDetected = false; + continue; + } + + // Create IPv4 with provided data + ethernetProvider.createIPv4( + ifaceId, entryIdx, subnetMaskAsPrefixLength, + *gatewayFieldValue, *addressFieldValue, asyncResp); + } + else + { + // Existing object that should be modified/deleted/remain + // unchanged + if (input[entryIdx].is_null()) + { + // Object should be deleted + ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, + entryIdx, asyncResp); + } + else if (input[entryIdx].is_object()) + { + if (input[entryIdx].size() == 0) + { + // Object shall remain unchanged + continue; + } + + // Apply changes + if (addressFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].address != nullptr && + *ipv4_data[entryIdx].address != *addressFieldValue) + { + ethernetProvider.changeIPv4AddressProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + "Address", *addressFieldValue, asyncResp); + } + + if (subnetMaskFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) + { + ethernetProvider.changeIPv4SubnetMaskProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + *subnetMaskFieldValue, subnetMaskAsPrefixLength, + asyncResp); + } + + if (addressOriginFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].origin != *addressFieldValue) + { + ethernetProvider.changeIPv4Origin( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + *addressOriginFieldValue, addressOriginInDBusFormat, + asyncResp); + } + + if (gatewayFieldState == json_util::Result::SUCCESS && + ipv4_data[entryIdx].gateway != nullptr && + *ipv4_data[entryIdx].gateway != *gatewayFieldValue) + { + ethernetProvider.changeIPv4AddressProperty( + ifaceId, entryIdx, ipv4_data[entryIdx].id, + "Gateway", *gatewayFieldValue, asyncResp); + } + } + } } - } - } - } - - nlohmann::json parseInterfaceData( - const std::string &ifaceId, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - // Copy JSON object to avoid race condition - nlohmann::json jsonResponse(Node::json); - - // Fill out obvious data... - jsonResponse["Id"] = ifaceId; - jsonResponse["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId; - - // ... then the one from DBus, regarding eth iface... - if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed; - - if (eth_data.macAddress != nullptr) - jsonResponse["MACAddress"] = *eth_data.macAddress; - - if (eth_data.hostname != nullptr) - jsonResponse["HostName"] = *eth_data.hostname; - - if (eth_data.vlanId != nullptr) { - nlohmann::json &vlanObj = jsonResponse["VLAN"]; - vlanObj["VLANEnable"] = true; - vlanObj["VLANId"] = *eth_data.vlanId; - } else { - nlohmann::json &vlanObj = jsonResponse["VLANs"]; - vlanObj["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId + - "/VLANs"; } - // ... at last, check if there are IPv4 data and prepare appropriate - // collection - if (ipv4_data.size() > 0) { - nlohmann::json ipv4Array = nlohmann::json::array(); - for (auto &ipv4Config : ipv4_data) { - nlohmann::json jsonIpv4; - if (ipv4Config.address != nullptr) { - jsonIpv4["Address"] = *ipv4Config.address; - if (ipv4Config.gateway != nullptr) - jsonIpv4["Gateway"] = *ipv4Config.gateway; - - jsonIpv4["AddressOrigin"] = ipv4Config.origin; - jsonIpv4["SubnetMask"] = ipv4Config.netmask; - - ipv4Array.push_back(std::move(jsonIpv4)); + nlohmann::json + parseInterfaceData(const std::string &ifaceId, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) + { + // Copy JSON object to avoid race condition + nlohmann::json jsonResponse(Node::json); + + // Fill out obvious data... + jsonResponse["Id"] = ifaceId; + jsonResponse["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId; + + // ... then the one from DBus, regarding eth iface... + if (eth_data.speed != nullptr) + jsonResponse["SpeedMbps"] = *eth_data.speed; + + if (eth_data.macAddress != nullptr) + jsonResponse["MACAddress"] = *eth_data.macAddress; + + if (eth_data.hostname != nullptr) + jsonResponse["HostName"] = *eth_data.hostname; + + if (eth_data.vlanId != nullptr) + { + nlohmann::json &vlanObj = jsonResponse["VLAN"]; + vlanObj["VLANEnable"] = true; + vlanObj["VLANId"] = *eth_data.vlanId; + } + else + { + nlohmann::json &vlanObj = jsonResponse["VLANs"]; + vlanObj["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId + + "/VLANs"; } - } - jsonResponse["IPv4Addresses"] = std::move(ipv4Array); - } - return jsonResponse; - } - - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } + // ... at last, check if there are IPv4 data and prepare appropriate + // collection + if (ipv4_data.size() > 0) + { + nlohmann::json ipv4Array = nlohmann::json::array(); + for (auto &ipv4Config : ipv4_data) + { + nlohmann::json jsonIpv4; + if (ipv4Config.address != nullptr) + { + jsonIpv4["Address"] = *ipv4Config.address; + if (ipv4Config.gateway != nullptr) + jsonIpv4["Gateway"] = *ipv4Config.gateway; + + jsonIpv4["AddressOrigin"] = ipv4Config.origin; + jsonIpv4["SubnetMask"] = ipv4Config.netmask; + + ipv4Array.push_back(std::move(jsonIpv4)); + } + } + jsonResponse["IPv4Addresses"] = std::move(ipv4Array); + } - const std::string &ifaceId = params[0]; - - // get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, - [&, ifaceId](const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success) { - res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("EthernetInterface", ifaceId)); - res.result(boost::beast::http::status::not_found); - } - res.end(); - }); - } - - void doPatch(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + return jsonResponse; } - const std::string &ifaceId = params[0]; - - nlohmann::json patchReq; + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (!json_util::processJsonFromRequest(res, req, patchReq)) { - return; + const std::string &ifaceId = params[0]; + + // get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success) + { + res.jsonValue = + parseInterfaceData(ifaceId, eth_data, ipv4_data); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "EthernetInterface", ifaceId)); + res.result(boost::beast::http::status::not_found); + } + res.end(); + }); } - // get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, ifaceId, patchReq = std::move(patchReq) ]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (!success) { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); + void doPatch(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); res.end(); + return; + } + + const std::string &ifaceId = params[0]; + nlohmann::json patchReq; + + if (!json_util::processJsonFromRequest(res, req, patchReq)) + { return; - } - - res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data); - - std::shared_ptr<AsyncResp> asyncResp = - std::make_shared<AsyncResp>(res); - - for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); - ++propertyIt) { - if (propertyIt.key() == "VLAN") { - handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN", - asyncResp); - } else if (propertyIt.key() == "HostName") { - handleHostnamePatch(propertyIt.value(), eth_data, asyncResp); - } else if (propertyIt.key() == "IPv4Addresses") { - handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data, - asyncResp); - } else if (propertyIt.key() == "IPv6Addresses") { - // TODO(kkowalsk) IPv6 Not supported on D-Bus yet - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } else { - auto fieldInJsonIt = res.jsonValue.find(propertyIt.key()); - - if (fieldInJsonIt == res.jsonValue.end()) { - // Field not in scope of defined fields - messages::addMessageToJsonRoot( - res.jsonValue, messages::propertyUnknown(propertyIt.key())); - } else if (*fieldInJsonIt != *propertyIt) { - // User attempted to modify non-writable field - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } - } - } - }); - } + } + + // get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, ifaceId, patchReq = std::move(patchReq)]( + const bool &success, const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (!success) + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + + return; + } + + res.jsonValue = + parseInterfaceData(ifaceId, eth_data, ipv4_data); + + std::shared_ptr<AsyncResp> asyncResp = + std::make_shared<AsyncResp>(res); + + for (auto propertyIt = patchReq.begin(); + propertyIt != patchReq.end(); ++propertyIt) + { + if (propertyIt.key() == "VLAN") + { + handleVlanPatch(ifaceId, propertyIt.value(), eth_data, + "/VLAN", asyncResp); + } + else if (propertyIt.key() == "HostName") + { + handleHostnamePatch(propertyIt.value(), eth_data, + asyncResp); + } + else if (propertyIt.key() == "IPv4Addresses") + { + handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data, + asyncResp); + } + else if (propertyIt.key() == "IPv6Addresses") + { + // TODO(kkowalsk) IPv6 Not supported on D-Bus yet + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyNotWritable(propertyIt.key())); + } + else + { + auto fieldInJsonIt = + res.jsonValue.find(propertyIt.key()); + + if (fieldInJsonIt == res.jsonValue.end()) + { + // Field not in scope of defined fields + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyUnknown(propertyIt.key())); + } + else if (*fieldInJsonIt != *propertyIt) + { + // User attempted to modify non-writable field + messages::addMessageToJsonRoot( + res.jsonValue, messages::propertyNotWritable( + propertyIt.key())); + } + } + } + }); + } - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; class VlanNetworkInterfaceCollection; @@ -1350,422 +1585,499 @@ class VlanNetworkInterfaceCollection; /** * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema */ -class VlanNetworkInterface : public Node { - public: - /* - * Default Constructor - */ - template <typename CrowApp> - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - VlanNetworkInterface(CrowApp &app) - : Node( +class VlanNetworkInterface : public Node +{ + public: + /* + * Default Constructor + */ + template <typename CrowApp> + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + VlanNetworkInterface(CrowApp &app) : + Node( app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>", - std::string(), std::string()) { - Node::json["@odata.type"] = - "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; - Node::json["Name"] = "VLAN Network Interface"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - nlohmann::json parseInterfaceData( - const std::string &parent_ifaceId, const std::string &ifaceId, - const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - // Copy JSON object to avoid race condition - nlohmann::json jsonResponse(Node::json); - - // Fill out obvious data... - jsonResponse["Id"] = ifaceId; - jsonResponse["@odata.id"] = - "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId + - "/VLANs/" + ifaceId; - - jsonResponse["VLANEnable"] = true; - jsonResponse["VLANId"] = *eth_data.vlanId; - - return jsonResponse; - } - - bool verifyNames(crow::Response &res, const std::string &parent, - const std::string &iface) { - if (!boost::starts_with(iface, parent + "_")) { - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", iface)); - res.result(boost::beast::http::status::not_found); - res.end(); - - return false; - } else { - return true; - } - } - - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // TODO(Pawel) this shall be parametrized call (two params) to get - // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. - // Check if there is required param, truly entering this shall be - // impossible. - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + std::string(), std::string()) + { + Node::json["@odata.type"] = + "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; + Node::json["Name"] = "VLAN Network Interface"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + private: + nlohmann::json + parseInterfaceData(const std::string &parent_ifaceId, + const std::string &ifaceId, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) + { + // Copy JSON object to avoid race condition + nlohmann::json jsonResponse(Node::json); + + // Fill out obvious data... + jsonResponse["Id"] = ifaceId; + jsonResponse["@odata.id"] = + "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + + parent_ifaceId + "/VLANs/" + ifaceId; - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; + jsonResponse["VLANEnable"] = true; + jsonResponse["VLANId"] = *eth_data.vlanId; + + return jsonResponse; } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success && eth_data.vlanId != nullptr) { - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, and - // other errors + bool verifyNames(crow::Response &res, const std::string &parent, + const std::string &iface) + { + if (!boost::starts_with(iface, parent + "_")) + { messages::addMessageToErrorJson( res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); + messages::resourceNotFound("VLAN Network Interface", iface)); res.result(boost::beast::http::status::not_found); - } - res.end(); - }); - } - - void doPatch(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + res.end(); + + return false; + } + else + { + return true; + } } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // TODO(Pawel) this shall be parametrized call (two params) to get + // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. + // Check if there is required param, truly entering this shall be + // impossible. + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; - } + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; - nlohmann::json patchReq; + if (!verifyNames(res, parentIfaceId, ifaceId)) + { + return; + } - if (!json_util::processJsonFromRequest(res, req, patchReq)) { - return; + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, [&, parentIfaceId, + ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success && eth_data.vlanId != nullptr) + { + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + } + res.end(); + }); } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq) ]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (!success) { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, - // and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); + void doPatch(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); res.end(); + return; + } + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; + + if (!verifyNames(res, parentIfaceId, ifaceId)) + { return; - } - - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); - - std::shared_ptr<AsyncResp> asyncResp = - std::make_shared<AsyncResp>(res); - - for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); - ++propertyIt) { - if (propertyIt.key() != "VLANEnable" && - propertyIt.key() != "VLANId") { - auto fieldInJsonIt = res.jsonValue.find(propertyIt.key()); - - if (fieldInJsonIt == res.jsonValue.end()) { - // Field not in scope of defined fields - messages::addMessageToJsonRoot( - res.jsonValue, messages::propertyUnknown(propertyIt.key())); - } else if (*fieldInJsonIt != *propertyIt) { - // User attempted to modify non-writable field - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyNotWritable(propertyIt.key())); - } - } - } - - EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/", - asyncResp); - }); - } - - void doDelete(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 2) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + } + + nlohmann::json patchReq; + + if (!json_util::processJsonFromRequest(res, req, patchReq)) + { + return; + } + + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, + [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)]( + const bool &success, const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (!success) + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + + return; + } + + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + + std::shared_ptr<AsyncResp> asyncResp = + std::make_shared<AsyncResp>(res); + + for (auto propertyIt = patchReq.begin(); + propertyIt != patchReq.end(); ++propertyIt) + { + if (propertyIt.key() != "VLANEnable" && + propertyIt.key() != "VLANId") + { + auto fieldInJsonIt = + res.jsonValue.find(propertyIt.key()); + + if (fieldInJsonIt == res.jsonValue.end()) + { + // Field not in scope of defined fields + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyUnknown(propertyIt.key())); + } + else if (*fieldInJsonIt != *propertyIt) + { + // User attempted to modify non-writable field + messages::addMessageToJsonRoot( + res.jsonValue, messages::propertyNotWritable( + propertyIt.key())); + } + } + } + + EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, + "/", asyncResp); + }); } - const std::string &parentIfaceId = params[0]; - const std::string &ifaceId = params[1]; + void doDelete(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 2) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + + const std::string &parentIfaceId = params[0]; + const std::string &ifaceId = params[1]; + + if (!verifyNames(res, parentIfaceId, ifaceId)) + { + return; + } - if (!verifyNames(res, parentIfaceId, ifaceId)) { - return; + // Get single eth interface data, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceData( + ifaceId, [&, parentIfaceId, + ifaceId](const bool &success, + const EthernetInterfaceData ð_data, + const std::vector<IPv4AddressData> &ipv4_data) { + if (success && eth_data.vlanId != nullptr) + { + res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, + eth_data, ipv4_data); + + // Disable VLAN + OnDemandEthernetProvider::disableVlan( + ifaceId, [&](const boost::system::error_code ec) { + if (ec) + { + res.jsonValue = nlohmann::json::object(); + messages::addMessageToErrorJson( + res.jsonValue, messages::internalError()); + res.result(boost::beast::http::status:: + internal_server_error); + } + res.end(); + }); + } + else + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::addMessageToErrorJson( + res.jsonValue, messages::resourceNotFound( + "VLAN Network Interface", ifaceId)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + }); } - // Get single eth interface data, and call the below callback for JSON - // preparation - ethernetProvider.getEthernetIfaceData( - ifaceId, [&, parentIfaceId, ifaceId]( - const bool &success, const EthernetInterfaceData ð_data, - const std::vector<IPv4AddressData> &ipv4_data) { - if (success && eth_data.vlanId != nullptr) { - res.jsonValue = - parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data); + /** + * This allows VlanNetworkInterfaceCollection to reuse this class' doGet + * method, to maintain consistency of returned data, as Collection's doPost + * should return data for created member which should match member's doGet + * result in 100%. + */ + friend VlanNetworkInterfaceCollection; - // Disable VLAN - OnDemandEthernetProvider::disableVlan( - ifaceId, [&](const boost::system::error_code ec) { - if (ec) { - res.jsonValue = nlohmann::json::object(); - messages::addMessageToErrorJson(res.jsonValue, - messages::internalError()); - res.result( - boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } else { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non existing object, - // and - // other errors - messages::addMessageToErrorJson( - res.jsonValue, - messages::resourceNotFound("VLAN Network Interface", ifaceId)); - res.result(boost::beast::http::status::not_found); - res.end(); - } - }); - } - - /** - * This allows VlanNetworkInterfaceCollection to reuse this class' doGet - * method, to maintain consistency of returned data, as Collection's doPost - * should return data for created member which should match member's doGet - * result in 100%. - */ - friend VlanNetworkInterfaceCollection; - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; }; /** * VlanNetworkInterfaceCollection derived class for delivering * VLANNetworkInterface Collection Schema */ -class VlanNetworkInterfaceCollection : public Node { - public: - template <typename CrowApp> - // TODO(Pawel) Remove line from below, where we assume that there is only one - // manager called openbmc This shall be generic, but requires to update - // GetSubroutes method - VlanNetworkInterfaceCollection(CrowApp &app) - : Node(app, +class VlanNetworkInterfaceCollection : public Node +{ + public: + template <typename CrowApp> + // TODO(Pawel) Remove line from below, where we assume that there is only + // one manager called openbmc This shall be generic, but requires to update + // GetSubroutes method + VlanNetworkInterfaceCollection(CrowApp &app) : + Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/", std::string()), - memberVlan(app) { - Node::json["@odata.type"] = - "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata" - "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; - Node::json["Name"] = "VLAN Network Interface Collection"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 1) { - // This means there is a problem with the router - res.result(boost::beast::http::status::internal_server_error); - res.end(); - - return; + memberVlan(app) + { + Node::json["@odata.type"] = + "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata" + "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; + Node::json["Name"] = "VLAN Network Interface Collection"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - std::string rootInterfaceName = params[0]; - - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([ - &, managerId{std::move(managerId)}, - rootInterfaceName{std::move(rootInterfaceName)} - ](const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - bool rootInterfaceFound = false; - nlohmann::json ifaceArray = nlohmann::json::array(); - - for (const std::string &ifaceItem : iface_list) { - if (ifaceItem == rootInterfaceName) { - rootInterfaceFound = true; - } else if (boost::starts_with(ifaceItem, rootInterfaceName + "_")) { - ifaceArray.push_back( - {{"@odata.id", "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + rootInterfaceName + - "/VLANs/" + ifaceItem}}); - } - } + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 1) + { + // This means there is a problem with the router + res.result(boost::beast::http::status::internal_server_error); + res.end(); - if (rootInterfaceFound) { - Node::json["Members"] = ifaceArray; - Node::json["Members@odata.count"] = ifaceArray.size(); - Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId + - "/EthernetInterfaces/" + rootInterfaceName + - "/VLANs"; - res.jsonValue = Node::json; - } else { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("EthernetInterface", - rootInterfaceName)); - res.result(boost::beast::http::status::not_found); - res.end(); + return; } - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); - }); - } - - void doPost(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - if (params.size() != 1) { - // This means there is a problem with the router - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - - nlohmann::json postReq; - if (!json_util::processJsonFromRequest(res, req, postReq)) { - return; + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + std::string rootInterfaceName = params[0]; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}, + rootInterfaceName{std::move(rootInterfaceName)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + bool rootInterfaceFound = false; + nlohmann::json ifaceArray = nlohmann::json::array(); + + for (const std::string &ifaceItem : iface_list) + { + if (ifaceItem == rootInterfaceName) + { + rootInterfaceFound = true; + } + else if (boost::starts_with(ifaceItem, + rootInterfaceName + "_")) + { + ifaceArray.push_back( + {{"@odata.id", "/redfish/v1/Managers/" + + managerId + + "/EthernetInterfaces/" + + rootInterfaceName + + "/VLANs/" + ifaceItem}}); + } + } + + if (rootInterfaceFound) + { + Node::json["Members"] = ifaceArray; + Node::json["Members@odata.count"] = ifaceArray.size(); + Node::json["@odata.id"] = "/redfish/v1/Managers/" + + managerId + + "/EthernetInterfaces/" + + rootInterfaceName + "/VLANs"; + res.jsonValue = Node::json; + } + else + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("EthernetInterface", + rootInterfaceName)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + } + res.end(); + }); } - // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for - // any Manager, not only hardcoded 'openbmc'. - std::string managerId = "openbmc"; - std::string rootInterfaceName = params[0]; - uint64_t vlanId; - bool errorDetected; + void doPost(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + if (params.size() != 1) + { + // This means there is a problem with the router + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - if (json_util::getUnsigned( - "VLANId", postReq, vlanId, - static_cast<uint8_t>(json_util::MessageSetting::MISSING) | - static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), - res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) { - res.end(); - return; - } + nlohmann::json postReq; - // get eth interface list, and call the below callback for JSON preparation - ethernetProvider.getEthernetIfaceList([ - &, managerId{std::move(managerId)}, - rootInterfaceName{std::move(rootInterfaceName)} - ](const bool &success, const std::vector<std::string> &iface_list) { - if (success) { - bool rootInterfaceFound = false; - - for (const std::string &ifaceItem : iface_list) { - if (ifaceItem == rootInterfaceName) { - rootInterfaceFound = true; - break; - } + if (!json_util::processJsonFromRequest(res, req, postReq)) + { + return; } - if (rootInterfaceFound) { - ethernetProvider.createVlan( - rootInterfaceName, vlanId, - [&, vlanId, rootInterfaceName, - req{std::move(req)} ](const boost::system::error_code ec) { - if (ec) { - messages::addMessageToErrorJson(res.jsonValue, - messages::internalError()); - res.end(); - } else { - memberVlan.doGet( - res, req, - {rootInterfaceName, - rootInterfaceName + "_" + std::to_string(vlanId)}); - } - }); - } else { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("EthernetInterface", - rootInterfaceName)); - res.result(boost::beast::http::status::not_found); - res.end(); + // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces + // for any Manager, not only hardcoded 'openbmc'. + std::string managerId = "openbmc"; + std::string rootInterfaceName = params[0]; + uint64_t vlanId; + bool errorDetected; + + if (json_util::getUnsigned( + "VLANId", postReq, vlanId, + static_cast<uint8_t>(json_util::MessageSetting::MISSING) | + static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), + res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) + { + res.end(); + return; } - } else { - // No success, best what we can do is return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - res.end(); - } - }); - } - - // Ethernet Provider object - // TODO(Pawel) consider move it to singleton - OnDemandEthernetProvider ethernetProvider; - VlanNetworkInterface memberVlan; + + // get eth interface list, and call the below callback for JSON + // preparation + ethernetProvider.getEthernetIfaceList( + [&, managerId{std::move(managerId)}, + rootInterfaceName{std::move(rootInterfaceName)}]( + const bool &success, + const std::vector<std::string> &iface_list) { + if (success) + { + bool rootInterfaceFound = false; + + for (const std::string &ifaceItem : iface_list) + { + if (ifaceItem == rootInterfaceName) + { + rootInterfaceFound = true; + break; + } + } + + if (rootInterfaceFound) + { + ethernetProvider.createVlan( + rootInterfaceName, vlanId, + [&, vlanId, rootInterfaceName, req{std::move(req)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::internalError()); + res.end(); + } + else + { + memberVlan.doGet( + res, req, + {rootInterfaceName, + rootInterfaceName + "_" + + std::to_string(vlanId)}); + } + }); + } + else + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("EthernetInterface", + rootInterfaceName)); + res.result(boost::beast::http::status::not_found); + res.end(); + } + } + else + { + // No success, best what we can do is return INTERNALL ERROR + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + } + }); + } + + // Ethernet Provider object + // TODO(Pawel) consider move it to singleton + OnDemandEthernetProvider ethernetProvider; + VlanNetworkInterface memberVlan; }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 7a861b4a3d..86fe567f3a 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -17,101 +17,111 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class Manager : public Node { - public: - Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/openbmc/") { - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc"; - Node::json["@odata.type"] = "#Manager.v1_3_0.Manager"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager"; - Node::json["Id"] = "openbmc"; - Node::json["Name"] = "OpenBmc Manager"; - Node::json["Description"] = "Baseboard Management Controller"; - Node::json["PowerState"] = "On"; - Node::json["UUID"] = - app.template getMiddleware<crow::persistent_data::Middleware>() - .systemUuid; - Node::json["Model"] = "OpenBmc"; // TODO(ed), get model - Node::json["FirmwareVersion"] = "1234456789"; // TODO(ed), get fwversion - Node::json["EthernetInterfaces"] = nlohmann::json( - {{"@odata.id", - "/redfish/v1/Managers/openbmc/EthernetInterfaces"}}); // TODO(Pawel), - // remove this - // when - // subroutes - // will work - // correctly +class Manager : public Node +{ + public: + Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/openbmc/") + { + Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc"; + Node::json["@odata.type"] = "#Manager.v1_3_0.Manager"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager"; + Node::json["Id"] = "openbmc"; + Node::json["Name"] = "OpenBmc Manager"; + Node::json["Description"] = "Baseboard Management Controller"; + Node::json["PowerState"] = "On"; + Node::json["UUID"] = + app.template getMiddleware<crow::persistent_data::Middleware>() + .systemUuid; + Node::json["Model"] = "OpenBmc"; // TODO(ed), get model + Node::json["FirmwareVersion"] = "1234456789"; // TODO(ed), get fwversion + Node::json["EthernetInterfaces"] = nlohmann::json( + {{"@odata.id", "/redfish/v1/Managers/openbmc/" + "EthernetInterfaces"}}); // TODO(Pawel), + // remove this + // when + // subroutes + // will work + // correctly - 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"}}}}; - } + 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - Node::json["DateTime"] = getDateTime(); - // Copy over the static data to include the entries added by SubRoute - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + Node::json["DateTime"] = getDateTime(); + // Copy over the static data to include the entries added by SubRoute + res.jsonValue = Node::json; + res.end(); + } - std::string getDateTime() const { - std::array<char, 128> dateTime; - std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); - std::time_t time = std::time(nullptr); + std::string getDateTime() const + { + std::array<char, 128> dateTime; + std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); + std::time_t time = std::time(nullptr); - if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", - std::localtime(&time))) { - // insert the colon required by the ISO 8601 standard - redfishDateTime = std::string(dateTime.data()); - redfishDateTime.insert(redfishDateTime.end() - 2, ':'); - } + if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", + std::localtime(&time))) + { + // insert the colon required by the ISO 8601 standard + redfishDateTime = std::string(dateTime.data()); + redfishDateTime.insert(redfishDateTime.end() - 2, ':'); + } - return redfishDateTime; - } + return redfishDateTime; + } }; -class ManagerCollection : public Node { - public: - ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/") { - Node::json["@odata.id"] = "/redfish/v1/Managers"; - Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; - Node::json["Name"] = "Manager Collection"; - Node::json["Members@odata.count"] = 1; - Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; +class ManagerCollection : public Node +{ + public: + ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/") + { + Node::json["@odata.id"] = "/redfish/v1/Managers"; + Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; + Node::json["Name"] = "Manager Collection"; + Node::json["Members@odata.count"] = 1; + Node::json["Members"] = { + {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; - 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"}}}}; - } + 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - // Collections don't include the static data added by SubRoute because it - // has a duplicate entry for members - res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; - res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection"; - res.jsonValue["@odata.context"] = - "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; - res.jsonValue["Name"] = "Manager Collection"; - res.jsonValue["Members@odata.count"] = 1; - res.jsonValue["Members"] = { - {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + // Collections don't include the static data added by SubRoute because + // it has a duplicate entry for members + res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; + res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection"; + res.jsonValue["@odata.context"] = + "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; + res.jsonValue["Name"] = "Manager Collection"; + res.jsonValue["Members@odata.count"] = 1; + res.jsonValue["Members"] = { + {{"@odata.id", "/redfish/v1/Managers/openbmc"}}}; + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp index ae29df05f6..bb021f5297 100644 --- a/redfish-core/lib/network_protocol.hpp +++ b/redfish-core/lib/network_protocol.hpp @@ -18,24 +18,27 @@ #include "error_messages.hpp" #include "node.hpp" -namespace redfish { - -enum NetworkProtocolUnitStructFields { - NET_PROTO_UNIT_NAME, - NET_PROTO_UNIT_DESC, - NET_PROTO_UNIT_LOAD_STATE, - NET_PROTO_UNIT_ACTIVE_STATE, - NET_PROTO_UNIT_SUB_STATE, - NET_PROTO_UNIT_DEVICE, - NET_PROTO_UNIT_OBJ_PATH, - NET_PROTO_UNIT_ALWAYS_0, - NET_PROTO_UNIT_ALWAYS_EMPTY, - NET_PROTO_UNIT_ALWAYS_ROOT_PATH +namespace redfish +{ + +enum NetworkProtocolUnitStructFields +{ + NET_PROTO_UNIT_NAME, + NET_PROTO_UNIT_DESC, + NET_PROTO_UNIT_LOAD_STATE, + NET_PROTO_UNIT_ACTIVE_STATE, + NET_PROTO_UNIT_SUB_STATE, + NET_PROTO_UNIT_DEVICE, + NET_PROTO_UNIT_OBJ_PATH, + NET_PROTO_UNIT_ALWAYS_0, + NET_PROTO_UNIT_ALWAYS_EMPTY, + NET_PROTO_UNIT_ALWAYS_ROOT_PATH }; -enum NetworkProtocolListenResponseElements { - NET_PROTO_LISTEN_TYPE, - NET_PROTO_LISTEN_STREAM +enum NetworkProtocolListenResponseElements +{ + NET_PROTO_LISTEN_TYPE, + NET_PROTO_LISTEN_STREAM }; /** @@ -46,9 +49,10 @@ using UnitStruct = std::string, sdbusplus::message::object_path, uint32_t, std::string, sdbusplus::message::object_path>; -struct ServiceConfiguration { - const char* serviceName; - const char* socketPath; +struct ServiceConfiguration +{ + const char* serviceName; + const char* socketPath; }; const static boost::container::flat_map<const char*, ServiceConfiguration> @@ -63,128 +67,161 @@ const static boost::container::flat_map<const char*, ServiceConfiguration> {"phosphor-ipmi-net.service", "/org/freedesktop/systemd1/unit/phosphor_2dipmi_2dnet_2esocket"}}}; -class NetworkProtocol : public Node { - public: - NetworkProtocol(CrowApp& app) - : Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") { - Node::json["@odata.type"] = - "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; - Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/NetworkProtocol"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; - Node::json["Id"] = "NetworkProtocol"; - Node::json["Name"] = "Manager Network Protocol"; - Node::json["Description"] = "Manager Network Service"; - Node::json["Status"]["Health"] = "OK"; - Node::json["Status"]["HealthRollup"] = "OK"; - Node::json["Status"]["State"] = "Enabled"; - - for (auto& protocol : protocolToDBus) { - Node::json[protocol.first]["ProtocolEnabled"] = false; +class NetworkProtocol : public Node +{ + public: + NetworkProtocol(CrowApp& app) : + Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") + { + Node::json["@odata.type"] = + "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; + Node::json["@odata.id"] = + "/redfish/v1/Managers/openbmc/NetworkProtocol"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; + Node::json["Id"] = "NetworkProtocol"; + Node::json["Name"] = "Manager Network Protocol"; + Node::json["Description"] = "Manager Network Service"; + Node::json["Status"]["Health"] = "OK"; + Node::json["Status"]["HealthRollup"] = "OK"; + Node::json["Status"]["State"] = "Enabled"; + + for (auto& protocol : protocolToDBus) + { + Node::json[protocol.first]["ProtocolEnabled"] = false; + } + + 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"}}}}; } - 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - - getData(asyncResp); - } - - std::string getHostName() const { - std::string hostName; - - std::array<char, HOST_NAME_MAX> hostNameCStr; - if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) { - hostName = hostNameCStr.data(); + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + + getData(asyncResp); } - return hostName; - } - - void getData(const std::shared_ptr<AsyncResp>& asyncResp) { - Node::json["HostName"] = getHostName(); - asyncResp->res.jsonValue = Node::json; - - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code ec, - const std::vector<UnitStruct>& resp) { - if (ec) { - asyncResp->res.jsonValue = nlohmann::json::object(); - messages::addMessageToErrorJson(asyncResp->res.jsonValue, - messages::internalError()); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - for (auto& unit : resp) { - for (auto& kv : protocolToDBus) { - if (kv.second.serviceName == - std::get<NET_PROTO_UNIT_NAME>(unit)) { - continue; - } - const char* service = kv.first; - const char* socketPath = kv.second.socketPath; - - asyncResp->res.jsonValue[service]["ProtocolEnabled"] = - std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == "running"; - - crow::connections::systemBus->async_method_call( - [ asyncResp, service{std::string(service)}, socketPath ]( - const boost::system::error_code ec, - const sdbusplus::message::variant<std::vector< - std::tuple<std::string, std::string>>>& resp) { - if (ec) { - messages::addMessageToJson(asyncResp->res.jsonValue, - messages::internalError(), - "/" + service); - return; - } - const std::vector<std::tuple<std::string, std::string>>* - responsePtr = mapbox::getPtr<const std::vector< - std::tuple<std::string, std::string>>>(resp); - if (responsePtr == nullptr || responsePtr->size() < 1) { - return; - } - const std::string& listenStream = - std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]); - std::size_t lastColonPos = listenStream.rfind(":"); - if (lastColonPos == std::string::npos) { - // Not a port - return; - } - std::string portStr = listenStream.substr(lastColonPos + 1); - char* endPtr = nullptr; - // Use strtol instead of stroi to avoid exceptions - long port = std::strtol(portStr.c_str(), &endPtr, 10); - - if (*endPtr != '\0' || portStr.empty()) { - // Invalid value - asyncResp->res.jsonValue[service]["Port"] = nullptr; - } else { - // Everything OK - asyncResp->res.jsonValue[service]["Port"] = port; + std::string getHostName() const + { + std::string hostName; + + std::array<char, HOST_NAME_MAX> hostNameCStr; + if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) + { + hostName = hostNameCStr.data(); + } + return hostName; + } + + void getData(const std::shared_ptr<AsyncResp>& asyncResp) + { + Node::json["HostName"] = getHostName(); + asyncResp->res.jsonValue = Node::json; + + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code ec, + const std::vector<UnitStruct>& resp) { + if (ec) + { + asyncResp->res.jsonValue = nlohmann::json::object(); + messages::addMessageToErrorJson(asyncResp->res.jsonValue, + messages::internalError()); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + for (auto& unit : resp) + { + for (auto& kv : protocolToDBus) + { + if (kv.second.serviceName == + std::get<NET_PROTO_UNIT_NAME>(unit)) + { + continue; + } + const char* service = kv.first; + const char* socketPath = kv.second.socketPath; + + asyncResp->res.jsonValue[service]["ProtocolEnabled"] = + std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == + "running"; + + crow::connections::systemBus->async_method_call( + [asyncResp, service{std::string(service)}, + socketPath]( + const boost::system::error_code ec, + const sdbusplus::message::variant<std::vector< + std::tuple<std::string, std::string>>>& + resp) { + if (ec) + { + messages::addMessageToJson( + asyncResp->res.jsonValue, + messages::internalError(), + "/" + service); + return; + } + const std::vector<std::tuple< + std::string, std::string>>* responsePtr = + mapbox::getPtr<const std::vector< + std::tuple<std::string, std::string>>>( + resp); + if (responsePtr == nullptr || + responsePtr->size() < 1) + { + return; + } + + const std::string& listenStream = + std::get<NET_PROTO_LISTEN_STREAM>( + (*responsePtr)[0]); + std::size_t lastColonPos = + listenStream.rfind(":"); + if (lastColonPos == std::string::npos) + { + // Not a port + return; + } + std::string portStr = + listenStream.substr(lastColonPos + 1); + char* endPtr = nullptr; + // Use strtol instead of stroi to avoid + // exceptions + long port = + std::strtol(portStr.c_str(), &endPtr, 10); + + if (*endPtr != '\0' || portStr.empty()) + { + // Invalid value + asyncResp->res.jsonValue[service]["Port"] = + nullptr; + } + else + { + // Everything OK + asyncResp->res.jsonValue[service]["Port"] = + port; + } + }, + "org.freedesktop.systemd1", socketPath, + "org.freedesktop.DBus.Properties", "Get", + "org.freedesktop.systemd1.Socket", "Listen"); } - }, - "org.freedesktop.systemd1", socketPath, - "org.freedesktop.DBus.Properties", "Get", - "org.freedesktop.systemd1.Socket", "Listen"); - } - } - }, - "org.freedesktop.systemd1", "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", "ListUnits"); - } + } + }, + "org.freedesktop.systemd1", "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "ListUnits"); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp index ca1c3757da..2406250fa3 100644 --- a/redfish-core/lib/redfish_sessions.hpp +++ b/redfish-core/lib/redfish_sessions.hpp @@ -19,312 +19,348 @@ #include "node.hpp" #include "persistent_data_middleware.hpp" -namespace redfish { +namespace redfish +{ class SessionCollection; -class Sessions : public Node { - public: - Sessions(CrowApp& app) - : Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) { - Node::json["@odata.type"] = "#Session.v1_0_2.Session"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; - Node::json["Name"] = "User Session"; - Node::json["Description"] = "Manager User Session"; - - 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - auto session = - crow::persistent_data::SessionStore::getInstance().getSessionByUid( - params[0]); - - if (session == nullptr) { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("Session", params[0])); - - res.result(boost::beast::http::status::not_found); - res.end(); - return; +class Sessions : public Node +{ + public: + Sessions(CrowApp& app) : + Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) + { + Node::json["@odata.type"] = "#Session.v1_0_2.Session"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; + Node::json["Name"] = "User Session"; + Node::json["Description"] = "Manager User Session"; + + 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"}}}}; } - Node::json["Id"] = session->uniqueId; - Node::json["UserName"] = session->username; - Node::json["@odata.id"] = - "/redfish/v1/SessionService/Sessions/" + session->uniqueId; - - res.jsonValue = Node::json; - res.end(); - } - - void doDelete(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - // Need only 1 param which should be id of session to be deleted - if (params.size() != 1) { - // This should be handled by crow and never happen - BMCWEB_LOG_ERROR - << "Session DELETE has been called with invalid number of params"; - - res.result(boost::beast::http::status::bad_request); - messages::addMessageToErrorJson(res.jsonValue, messages::generalError()); - - res.end(); - return; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + auto session = + crow::persistent_data::SessionStore::getInstance().getSessionByUid( + params[0]); + + if (session == nullptr) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("Session", params[0])); + + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + Node::json["Id"] = session->uniqueId; + Node::json["UserName"] = session->username; + Node::json["@odata.id"] = + "/redfish/v1/SessionService/Sessions/" + session->uniqueId; + + res.jsonValue = Node::json; + res.end(); } - auto session = - crow::persistent_data::SessionStore::getInstance().getSessionByUid( - params[0]); - - if (session == nullptr) { - messages::addMessageToErrorJson( - res.jsonValue, messages::resourceNotFound("Session", params[0])); - - res.result(boost::beast::http::status::not_found); - res.end(); - return; + void doDelete(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + // Need only 1 param which should be id of session to be deleted + if (params.size() != 1) + { + // This should be handled by crow and never happen + BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " + "number of params"; + + res.result(boost::beast::http::status::bad_request); + messages::addMessageToErrorJson(res.jsonValue, + messages::generalError()); + + res.end(); + return; + } + + auto session = + crow::persistent_data::SessionStore::getInstance().getSessionByUid( + params[0]); + + if (session == nullptr) + { + messages::addMessageToErrorJson( + res.jsonValue, + messages::resourceNotFound("Session", params[0])); + + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + // DELETE should return representation of object that will be removed + doGet(res, req, params); + + crow::persistent_data::SessionStore::getInstance().removeSession( + session); } - // DELETE should return representation of object that will be removed - doGet(res, req, params); - - crow::persistent_data::SessionStore::getInstance().removeSession(session); - } - - /** - * This allows SessionCollection to reuse this class' doGet method, to - * maintain consistency of returned data, as Collection's doPost should return - * data for created member which should match member's doGet result in 100% - */ - friend SessionCollection; + /** + * This allows SessionCollection to reuse this class' doGet method, to + * maintain consistency of returned data, as Collection's doPost should + * return data for created member which should match member's doGet result + * in 100% + */ + friend SessionCollection; }; -class SessionCollection : public Node { - public: - SessionCollection(CrowApp& app) - : Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) { - Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; - Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SessionCollection.SessionCollection"; - Node::json["Name"] = "Session Collection"; - Node::json["Description"] = "Session Collection"; - Node::json["Members@odata.count"] = 0; - Node::json["Members"] = nlohmann::json::array(); - - 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, {}}}; - } - - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - std::vector<const std::string*> sessionIds = - crow::persistent_data::SessionStore::getInstance().getUniqueIds( - false, crow::persistent_data::PersistenceType::TIMEOUT); - - Node::json["Members@odata.count"] = sessionIds.size(); - Node::json["Members"] = nlohmann::json::array(); - for (const std::string* uid : sessionIds) { - Node::json["Members"].push_back( - {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); +class SessionCollection : public Node +{ + public: + SessionCollection(CrowApp& app) : + Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) + { + Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; + Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SessionCollection.SessionCollection"; + Node::json["Name"] = "Session Collection"; + Node::json["Description"] = "Session Collection"; + Node::json["Members@odata.count"] = 0; + Node::json["Members"] = nlohmann::json::array(); + + 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, {}}}; } - res.jsonValue = Node::json; - res.end(); - } - - void doPost(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - boost::beast::http::status status; - std::string username; - bool userAuthSuccessful = - authenticateUser(req, status, username, res.jsonValue); - res.result(status); - - if (!userAuthSuccessful) { - res.end(); - return; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + std::vector<const std::string*> sessionIds = + crow::persistent_data::SessionStore::getInstance().getUniqueIds( + false, crow::persistent_data::PersistenceType::TIMEOUT); + + Node::json["Members@odata.count"] = sessionIds.size(); + Node::json["Members"] = nlohmann::json::array(); + for (const std::string* uid : sessionIds) + { + Node::json["Members"].push_back( + {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); + } + + res.jsonValue = Node::json; + res.end(); } - // User is authenticated - create session for him - auto session = - crow::persistent_data::SessionStore::getInstance().generateUserSession( - username); - res.addHeader("X-Auth-Token", session->sessionToken); - - res.addHeader("Location", - "/redfish/v1/SessionService/Sessions/" + session->uniqueId); - - // Return data for created session - memberSession.doGet(res, req, {session->uniqueId}); - - // No need for res.end(), as it is called by doGet() - } - - /** - * @brief Verifies data provided in request and tries to authenticate user - * - * @param[in] req Crow request containing authentication data - * @param[out] httpRespCode HTTP Code that should be returned in response - * @param[out] user Retrieved username - not filled on failure - * @param[out] errJson JSON to which error messages will be written - * - * @return true if authentication was successful, false otherwise - */ - bool authenticateUser(const crow::Request& req, - boost::beast::http::status& httpRespCode, - std::string& user, nlohmann::json& errJson) { - // We need only UserName and Password - nothing more, nothing less - static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; - - // call with exceptions disabled - auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); - if (loginCredentials.is_discarded()) { - httpRespCode = boost::beast::http::status::bad_request; - - messages::addMessageToErrorJson(errJson, messages::malformedJSON()); - - return false; + void doPost(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + boost::beast::http::status status; + std::string username; + bool userAuthSuccessful = + authenticateUser(req, status, username, res.jsonValue); + res.result(status); + + if (!userAuthSuccessful) + { + res.end(); + return; + } + + // User is authenticated - create session for him + auto session = crow::persistent_data::SessionStore::getInstance() + .generateUserSession(username); + res.addHeader("X-Auth-Token", session->sessionToken); + + res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + + session->uniqueId); + + // Return data for created session + memberSession.doGet(res, req, {session->uniqueId}); + + // No need for res.end(), as it is called by doGet() } - // Check that there are only as many fields as there should be - if (loginCredentials.size() != numberOfRequiredFieldsInReq) { - httpRespCode = boost::beast::http::status::bad_request; - - messages::addMessageToErrorJson(errJson, messages::malformedJSON()); - - return false; - } - - // Find fields that we need - UserName and Password - auto userIt = loginCredentials.find("UserName"); - auto passIt = loginCredentials.find("Password"); - if (userIt == loginCredentials.end() || passIt == loginCredentials.end()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (userIt == loginCredentials.end()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("UserName")); - } - - if (passIt == loginCredentials.end()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("Password")); - } - - return false; + /** + * @brief Verifies data provided in request and tries to authenticate user + * + * @param[in] req Crow request containing authentication data + * @param[out] httpRespCode HTTP Code that should be returned in response + * @param[out] user Retrieved username - not filled on failure + * @param[out] errJson JSON to which error messages will be written + * + * @return true if authentication was successful, false otherwise + */ + bool authenticateUser(const crow::Request& req, + boost::beast::http::status& httpRespCode, + std::string& user, nlohmann::json& errJson) + { + // We need only UserName and Password - nothing more, nothing less + static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; + + // call with exceptions disabled + auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); + if (loginCredentials.is_discarded()) + { + httpRespCode = boost::beast::http::status::bad_request; + + messages::addMessageToErrorJson(errJson, messages::malformedJSON()); + + return false; + } + + // Check that there are only as many fields as there should be + if (loginCredentials.size() != numberOfRequiredFieldsInReq) + { + httpRespCode = boost::beast::http::status::bad_request; + + messages::addMessageToErrorJson(errJson, messages::malformedJSON()); + + return false; + } + + // Find fields that we need - UserName and Password + auto userIt = loginCredentials.find("UserName"); + auto passIt = loginCredentials.find("Password"); + if (userIt == loginCredentials.end() || + passIt == loginCredentials.end()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (userIt == loginCredentials.end()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("UserName")); + } + + if (passIt == loginCredentials.end()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("Password")); + } + + return false; + } + + // Check that given data is of valid type (string) + if (!userIt->is_string() || !passIt->is_string()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (!userIt->is_string()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyValueTypeError(userIt->dump(), + "UserName")); + } + + if (!passIt->is_string()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyValueTypeError(userIt->dump(), + "Password")); + } + + return false; + } + + // Extract username and password + std::string username = userIt->get<const std::string>(); + std::string password = passIt->get<const std::string>(); + + // Verify that required fields are not empty + if (username.empty() || password.empty()) + { + httpRespCode = boost::beast::http::status::bad_request; + + if (username.empty()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("UserName")); + } + + if (password.empty()) + { + messages::addMessageToErrorJson( + errJson, messages::propertyMissing("Password")); + } + + return false; + } + + // Finally - try to authenticate user + if (!pamAuthenticateUser(username, password)) + { + httpRespCode = boost::beast::http::status::unauthorized; + + messages::addMessageToErrorJson( + errJson, + messages::resourceAtUriUnauthorized( + std::string(req.url), "Invalid username or password")); + + return false; + } + + // User authenticated successfully + httpRespCode = boost::beast::http::status::ok; + user = username; + + return true; } - // Check that given data is of valid type (string) - if (!userIt->is_string() || !passIt->is_string()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (!userIt->is_string()) { - messages::addMessageToErrorJson( - errJson, - messages::propertyValueTypeError(userIt->dump(), "UserName")); - } - - if (!passIt->is_string()) { - messages::addMessageToErrorJson( - errJson, - messages::propertyValueTypeError(userIt->dump(), "Password")); - } + /** + * Member session to ensure consistency between collection's doPost and + * member's doGet, as they should return 100% matching data + */ + Sessions memberSession; +}; - return false; +class SessionService : public Node +{ + public: + SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") + { + Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; + Node::json["@odata.id"] = "/redfish/v1/SessionService/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SessionService.SessionService"; + Node::json["Name"] = "Session Service"; + Node::json["Id"] = "SessionService"; + Node::json["Description"] = "Session Service"; + Node::json["SessionTimeout"] = + crow::persistent_data::SessionStore::getInstance() + .getTimeoutInSeconds(); + Node::json["ServiceEnabled"] = true; + + 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"}}}}; } - // Extract username and password - std::string username = userIt->get<const std::string>(); - std::string password = passIt->get<const std::string>(); - - // Verify that required fields are not empty - if (username.empty() || password.empty()) { - httpRespCode = boost::beast::http::status::bad_request; - - if (username.empty()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("UserName")); - } - - if (password.empty()) { - messages::addMessageToErrorJson(errJson, - messages::propertyMissing("Password")); - } - - return false; + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); } - - // Finally - try to authenticate user - if (!pamAuthenticateUser(username, password)) { - httpRespCode = boost::beast::http::status::unauthorized; - - messages::addMessageToErrorJson( - errJson, messages::resourceAtUriUnauthorized( - std::string(req.url), "Invalid username or password")); - - return false; - } - - // User authenticated successfully - httpRespCode = boost::beast::http::status::ok; - user = username; - - return true; - } - - /** - * Member session to ensure consistency between collection's doPost and - * member's doGet, as they should return 100% matching data - */ - Sessions memberSession; -}; - -class SessionService : public Node { - public: - SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") { - Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; - Node::json["@odata.id"] = "/redfish/v1/SessionService/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SessionService.SessionService"; - Node::json["Name"] = "Session Service"; - Node::json["Id"] = "SessionService"; - Node::json["Description"] = "Session Service"; - Node::json["SessionTimeout"] = - crow::persistent_data::SessionStore::getInstance() - .getTimeoutInSeconds(); - Node::json["ServiceEnabled"] = true; - - 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/roles.hpp b/redfish-core/lib/roles.hpp index 72d79486bb..258eed31b2 100644 --- a/redfish-core/lib/roles.hpp +++ b/redfish-core/lib/roles.hpp @@ -17,74 +17,82 @@ #include "node.hpp" -namespace redfish { +namespace redfish +{ -class Roles : public Node { - public: - Roles(CrowApp& app) - : Node(app, "/redfish/v1/AccountService/Roles/Administrator/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles/Administrator"; - Node::json["@odata.type"] = "#Role.v1_0_2.Role"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Role.Role"; - Node::json["Id"] = "Administrator"; - Node::json["Name"] = "User Role"; - Node::json["Description"] = "Administrator User Role"; - Node::json["IsPredefined"] = true; - Node::json["AssignedPrivileges"] = {"Login", "ConfigureManager", - "ConfigureUsers", "ConfigureSelf", - "ConfigureComponents"}; - Node::json["OemPrivileges"] = nlohmann::json::array(); - 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"}}}}; - } +class Roles : public Node +{ + public: + Roles(CrowApp& app) : + Node(app, "/redfish/v1/AccountService/Roles/Administrator/") + { + Node::json["@odata.id"] = + "/redfish/v1/AccountService/Roles/Administrator"; + Node::json["@odata.type"] = "#Role.v1_0_2.Role"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Role.Role"; + Node::json["Id"] = "Administrator"; + Node::json["Name"] = "User Role"; + Node::json["Description"] = "Administrator User Role"; + Node::json["IsPredefined"] = true; + Node::json["AssignedPrivileges"] = {"Login", "ConfigureManager", + "ConfigureUsers", "ConfigureSelf", + "ConfigureComponents"}; + Node::json["OemPrivileges"] = nlohmann::json::array(); + 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } }; -class RoleCollection : public Node { - public: - RoleCollection(CrowApp& app) - : Node(app, "/redfish/v1/AccountService/Roles/") { - Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles"; - Node::json["@odata.type"] = "#RoleCollection.RoleCollection"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#RoleCollection.RoleCollection"; - Node::json["Name"] = "Roles Collection"; - Node::json["Description"] = "BMC User Roles"; - Node::json["Members@odata.count"] = 1; - Node::json["Members"] = { - {{"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"}}}; +class RoleCollection : public Node +{ + public: + RoleCollection(CrowApp& app) : + Node(app, "/redfish/v1/AccountService/Roles/") + { + Node::json["@odata.id"] = "/redfish/v1/AccountService/Roles"; + Node::json["@odata.type"] = "#RoleCollection.RoleCollection"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#RoleCollection.RoleCollection"; + Node::json["Name"] = "Roles Collection"; + Node::json["Description"] = "BMC User Roles"; + Node::json["Members@odata.count"] = 1; + Node::json["Members"] = { + {{"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"}}}; - 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"}}}}; - } + 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - // This is a short term solution to work around a bug. GetSubroutes - // accidentally recognizes the Roles/Administrator route as a subroute - // (because it's hardcoded to a single entity). Remove this line when that - // is resolved - res.jsonValue.erase("Administrator"); - res.end(); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + // This is a short term solution to work around a bug. GetSubroutes + // accidentally recognizes the Roles/Administrator route as a subroute + // (because it's hardcoded to a single entity). Remove this line when + // that is resolved + res.jsonValue.erase("Administrator"); + res.end(); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp index 7f9fa684bc..c390cd7908 100644 --- a/redfish-core/lib/sensors.hpp +++ b/redfish-core/lib/sensors.hpp @@ -16,13 +16,15 @@ #pragma once #include <math.h> -#include <dbus_singleton.hpp> + #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/container/flat_map.hpp> #include <boost/range/algorithm/replace_copy_if.hpp> +#include <dbus_singleton.hpp> -namespace redfish { +namespace redfish +{ constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/"; @@ -41,32 +43,38 @@ using ManagedObjectsVectorType = std::vector<std::pair< * SensorsAsyncResp * Gathers data needed for response processing after async calls are done */ -class SensorsAsyncResp { - public: - SensorsAsyncResp(crow::Response& response, const std::string& chassisId, - const std::initializer_list<const char*> types) - : res(response), chassisId(chassisId), types(types) { - res.jsonValue["@odata.id"] = - "/redfish/v1/Chassis/" + chassisId + "/Thermal"; - } - - ~SensorsAsyncResp() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +class SensorsAsyncResp +{ + public: + SensorsAsyncResp(crow::Response& response, const std::string& chassisId, + const std::initializer_list<const char*> types) : + res(response), + chassisId(chassisId), types(types) + { + res.jsonValue["@odata.id"] = + "/redfish/v1/Chassis/" + chassisId + "/Thermal"; } - res.end(); - } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + ~SensorsAsyncResp() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } + res.end(); + } + + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response& res; - std::string chassisId{}; - const std::vector<const char*> types; + crow::Response& res; + std::string chassisId{}; + const std::vector<const char*> types; }; /** @@ -78,70 +86,84 @@ class SensorsAsyncResp { template <typename Callback> void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, const boost::container::flat_set<std::string>& sensorNames, - Callback&& callback) { - BMCWEB_LOG_DEBUG << "getConnections enter"; - const std::string path = "/xyz/openbmc_project/sensors"; - const std::array<std::string, 1> interfaces = { - "xyz.openbmc_project.Sensor.Value"}; - - // Response handler for parsing objects subtree - auto respHandler = - [ callback{std::move(callback)}, SensorsAsyncResp, sensorNames ]( - const boost::system::error_code ec, const GetSubTreeType& subtree) { - BMCWEB_LOG_DEBUG << "getConnections resp_handler enter"; - if (ec) { - SensorsAsyncResp->setErrorStatus(); - BMCWEB_LOG_ERROR << "getConnections resp_handler: Dbus error " << ec; - return; - } + Callback&& callback) +{ + BMCWEB_LOG_DEBUG << "getConnections enter"; + const std::string path = "/xyz/openbmc_project/sensors"; + const std::array<std::string, 1> interfaces = { + "xyz.openbmc_project.Sensor.Value"}; + + // Response handler for parsing objects subtree + auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp, + sensorNames](const boost::system::error_code ec, + const GetSubTreeType& subtree) { + BMCWEB_LOG_DEBUG << "getConnections resp_handler enter"; + if (ec) + { + SensorsAsyncResp->setErrorStatus(); + BMCWEB_LOG_ERROR << "getConnections resp_handler: Dbus error " + << ec; + return; + } - BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; + BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; - // Make unique list of connections only for requested sensor types and - // found in the chassis - boost::container::flat_set<std::string> connections; - // Intrinsic to avoid malloc. Most systems will have < 8 sensor producers - connections.reserve(8); + // Make unique list of connections only for requested sensor types and + // found in the chassis + boost::container::flat_set<std::string> connections; + // Intrinsic to avoid malloc. Most systems will have < 8 sensor + // producers + connections.reserve(8); - BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size(); - for (const std::string& tsensor : sensorNames) { - BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; - } + BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size(); + for (const std::string& tsensor : sensorNames) + { + BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; + } - for (const std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>& - object : subtree) { - for (const char* type : SensorsAsyncResp->types) { - if (boost::starts_with(object.first, type)) { - auto lastPos = object.first.rfind('/'); - if (lastPos != std::string::npos) { - std::string sensorName = object.first.substr(lastPos + 1); - - if (sensorNames.find(sensorName) != sensorNames.end()) { - // For each Connection name - for (const std::pair<std::string, std::vector<std::string>>& - objData : object.second) { - BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first; - connections.insert(objData.first); - } + for (const std::pair< + std::string, + std::vector<std::pair<std::string, std::vector<std::string>>>>& + object : subtree) + { + for (const char* type : SensorsAsyncResp->types) + { + if (boost::starts_with(object.first, type)) + { + auto lastPos = object.first.rfind('/'); + if (lastPos != std::string::npos) + { + std::string sensorName = + object.first.substr(lastPos + 1); + + if (sensorNames.find(sensorName) != sensorNames.end()) + { + // For each Connection name + for (const std::pair<std::string, + std::vector<std::string>>& + objData : object.second) + { + BMCWEB_LOG_DEBUG << "Adding connection: " + << objData.first; + connections.insert(objData.first); + } + } + } + break; + } } - } - break; } - } - } - BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; - callback(std::move(connections)); - BMCWEB_LOG_DEBUG << "getConnections resp_handler exit"; - }; - - // Make call to ObjectMapper to find all sensors objects - crow::connections::systemBus->async_method_call( - std::move(respHandler), "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", - "GetSubTree", path, 2, interfaces); - BMCWEB_LOG_DEBUG << "getConnections exit"; + BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; + callback(std::move(connections)); + BMCWEB_LOG_DEBUG << "getConnections resp_handler exit"; + }; + + // Make call to ObjectMapper to find all sensors objects + crow::connections::systemBus->async_method_call( + std::move(respHandler), "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces); + BMCWEB_LOG_DEBUG << "getConnections exit"; } /** @@ -151,64 +173,74 @@ void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, */ template <typename Callback> void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, - Callback&& callback) { - BMCWEB_LOG_DEBUG << "getChassis enter"; - // Process response from EntityManager and extract chassis data - auto respHandler = [ callback{std::move(callback)}, SensorsAsyncResp ]( - const boost::system::error_code ec, ManagedObjectsVectorType& resp) { - BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; - if (ec) { - BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; - SensorsAsyncResp->setErrorStatus(); - return; - } - boost::container::flat_set<std::string> sensorNames; - - // SensorsAsyncResp->chassisId - bool foundChassis = false; - std::vector<std::string> split; - // Reserve space for - // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames - split.reserve(8); - - for (const auto& objDictEntry : resp) { - const std::string& objectPath = - static_cast<const std::string&>(objDictEntry.first); - boost::algorithm::split(split, objectPath, boost::is_any_of("/")); - if (split.size() < 2) { - BMCWEB_LOG_ERROR << "Got path that isn't long enough " << objectPath; - split.clear(); - continue; - } - const std::string& sensorName = split.end()[-1]; - const std::string& chassisName = split.end()[-2]; - - if (chassisName != SensorsAsyncResp->chassisId) { - split.clear(); - continue; - } - BMCWEB_LOG_DEBUG << "New sensor: " << sensorName; - foundChassis = true; - sensorNames.emplace(sensorName); - split.clear(); + Callback&& callback) +{ + BMCWEB_LOG_DEBUG << "getChassis enter"; + // Process response from EntityManager and extract chassis data + auto respHandler = [callback{std::move(callback)}, + SensorsAsyncResp](const boost::system::error_code ec, + ManagedObjectsVectorType& resp) { + BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; + if (ec) + { + BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; + SensorsAsyncResp->setErrorStatus(); + return; + } + boost::container::flat_set<std::string> sensorNames; + + // SensorsAsyncResp->chassisId + bool foundChassis = false; + std::vector<std::string> split; + // Reserve space for + // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames + split.reserve(8); + + for (const auto& objDictEntry : resp) + { + const std::string& objectPath = + static_cast<const std::string&>(objDictEntry.first); + boost::algorithm::split(split, objectPath, boost::is_any_of("/")); + if (split.size() < 2) + { + BMCWEB_LOG_ERROR << "Got path that isn't long enough " + << objectPath; + split.clear(); + continue; + } + const std::string& sensorName = split.end()[-1]; + const std::string& chassisName = split.end()[-2]; + + if (chassisName != SensorsAsyncResp->chassisId) + { + split.clear(); + continue; + } + BMCWEB_LOG_DEBUG << "New sensor: " << sensorName; + foundChassis = true; + sensorNames.emplace(sensorName); + split.clear(); + }; + BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names"; + + if (!foundChassis) + { + BMCWEB_LOG_INFO << "Unable to find chassis named " + << SensorsAsyncResp->chassisId; + SensorsAsyncResp->res.result(boost::beast::http::status::not_found); + } + else + { + callback(sensorNames); + } + BMCWEB_LOG_DEBUG << "getChassis respHandler exit"; }; - BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names"; - - if (!foundChassis) { - BMCWEB_LOG_INFO << "Unable to find chassis named " - << SensorsAsyncResp->chassisId; - SensorsAsyncResp->res.result(boost::beast::http::status::not_found); - } else { - callback(sensorNames); - } - BMCWEB_LOG_DEBUG << "getChassis respHandler exit"; - }; - - // Make call to EntityManager to find all chassis objects - crow::connections::systemBus->async_method_call( - respHandler, "xyz.openbmc_project.EntityManager", "/", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - BMCWEB_LOG_DEBUG << "getChassis exit"; + + // Make call to EntityManager to find all chassis objects + crow::connections::systemBus->async_method_call( + respHandler, "xyz.openbmc_project.EntityManager", "/", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + BMCWEB_LOG_DEBUG << "getChassis exit"; } /** @@ -225,116 +257,146 @@ void objectInterfacesToJson( const boost::container::flat_map< std::string, boost::container::flat_map<std::string, SensorVariant>>& interfacesDict, - nlohmann::json& sensor_json) { - // We need a value interface before we can do anything with it - auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); - if (valueIt == interfacesDict.end()) { - BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; - return; - } - - // Assume values exist as is (10^0 == 1) if no scale exists - int64_t scaleMultiplier = 0; - - auto scaleIt = valueIt->second.find("Scale"); - // If a scale exists, pull value as int64, and use the scaling. - if (scaleIt != valueIt->second.end()) { - const int64_t* int64Value = mapbox::getPtr<const int64_t>(scaleIt->second); - if (int64Value != nullptr) { - scaleMultiplier = *int64Value; + nlohmann::json& sensor_json) +{ + // We need a value interface before we can do anything with it + auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); + if (valueIt == interfacesDict.end()) + { + BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; + return; } - } - - sensor_json["MemberId"] = sensorName; - sensor_json["Name"] = sensorName; - sensor_json["Status"]["State"] = "Enabled"; - sensor_json["Status"]["Health"] = "OK"; - - // Parameter to set to override the type we get from dbus, and force it to - // int, regardless of what is available. This is used for schemas like fan, - // that require integers, not floats. - bool forceToInt = false; - - const char* unit = "Reading"; - if (sensorType == "temperature") { - unit = "ReadingCelsius"; - sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature"; - // TODO(ed) Documentation says that path should be type fan_tach, - // implementation seems to implement fan - } else if (sensorType == "fan" || sensorType == "fan_tach") { - unit = "Reading"; - sensor_json["ReadingUnits"] = "RPM"; - sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; - forceToInt = true; - } else if (sensorType == "voltage") { - unit = "ReadingVolts"; - sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage"; - } else { - BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; - return; - } - // Map of dbus interface name, dbus property name and redfish property_name - std::vector<std::tuple<const char*, const char*, const char*>> properties; - properties.reserve(7); - - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningHigh", "UpperThresholdNonCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningLow", "LowerThresholdNonCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalHigh", "UpperThresholdCritical"); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalLow", "LowerThresholdCritical"); - - if (sensorType == "temperature") { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "MinReadingRangeTemp"); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "MaxReadingRangeTemp"); - } else { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "MinReadingRange"); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "MaxReadingRange"); - } - - for (const std::tuple<const char*, const char*, const char*>& p : - properties) { - auto interfaceProperties = interfacesDict.find(std::get<0>(p)); - if (interfaceProperties != interfacesDict.end()) { - auto valueIt = interfaceProperties->second.find(std::get<1>(p)); - if (valueIt != interfaceProperties->second.end()) { - const SensorVariant& valueVariant = valueIt->second; - nlohmann::json& valueIt = sensor_json[std::get<2>(p)]; - - // Attempt to pull the int64 directly - const int64_t* int64Value = mapbox::getPtr<const int64_t>(valueVariant); - - if (int64Value != nullptr) { - if (forceToInt || scaleMultiplier >= 0) { - valueIt = *int64Value * std::pow(10, scaleMultiplier); - } else { - valueIt = *int64Value * - std::pow(10, static_cast<double>(scaleMultiplier)); - } + + // Assume values exist as is (10^0 == 1) if no scale exists + int64_t scaleMultiplier = 0; + + auto scaleIt = valueIt->second.find("Scale"); + // If a scale exists, pull value as int64, and use the scaling. + if (scaleIt != valueIt->second.end()) + { + const int64_t* int64Value = + mapbox::getPtr<const int64_t>(scaleIt->second); + if (int64Value != nullptr) + { + scaleMultiplier = *int64Value; } - // Attempt to pull the float directly - const double* doubleValue = mapbox::getPtr<const double>(valueVariant); - - if (doubleValue != nullptr) { - if (!forceToInt) { - valueIt = *doubleValue * - std::pow(10, static_cast<double>(scaleMultiplier)); - } else { - valueIt = static_cast<int64_t>(*doubleValue * - std::pow(10, scaleMultiplier)); - } + } + + sensor_json["MemberId"] = sensorName; + sensor_json["Name"] = sensorName; + sensor_json["Status"]["State"] = "Enabled"; + sensor_json["Status"]["Health"] = "OK"; + + // Parameter to set to override the type we get from dbus, and force it to + // int, regardless of what is available. This is used for schemas like fan, + // that require integers, not floats. + bool forceToInt = false; + + const char* unit = "Reading"; + if (sensorType == "temperature") + { + unit = "ReadingCelsius"; + sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature"; + // TODO(ed) Documentation says that path should be type fan_tach, + // implementation seems to implement fan + } + else if (sensorType == "fan" || sensorType == "fan_tach") + { + unit = "Reading"; + sensor_json["ReadingUnits"] = "RPM"; + sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; + forceToInt = true; + } + else if (sensorType == "voltage") + { + unit = "ReadingVolts"; + sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage"; + } + else + { + BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; + return; + } + // Map of dbus interface name, dbus property name and redfish property_name + std::vector<std::tuple<const char*, const char*, const char*>> properties; + properties.reserve(7); + + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningHigh", "UpperThresholdNonCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningLow", "LowerThresholdNonCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalHigh", "UpperThresholdCritical"); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalLow", "LowerThresholdCritical"); + + if (sensorType == "temperature") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "MinReadingRangeTemp"); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "MaxReadingRangeTemp"); + } + else + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "MinReadingRange"); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "MaxReadingRange"); + } + + for (const std::tuple<const char*, const char*, const char*>& p : + properties) + { + auto interfaceProperties = interfacesDict.find(std::get<0>(p)); + if (interfaceProperties != interfacesDict.end()) + { + auto valueIt = interfaceProperties->second.find(std::get<1>(p)); + if (valueIt != interfaceProperties->second.end()) + { + const SensorVariant& valueVariant = valueIt->second; + nlohmann::json& valueIt = sensor_json[std::get<2>(p)]; + + // Attempt to pull the int64 directly + const int64_t* int64Value = + mapbox::getPtr<const int64_t>(valueVariant); + + if (int64Value != nullptr) + { + if (forceToInt || scaleMultiplier >= 0) + { + valueIt = *int64Value * std::pow(10, scaleMultiplier); + } + else + { + valueIt = + *int64Value * + std::pow(10, static_cast<double>(scaleMultiplier)); + } + } + // Attempt to pull the float directly + const double* doubleValue = + mapbox::getPtr<const double>(valueVariant); + + if (doubleValue != nullptr) + { + if (!forceToInt) + { + valueIt = + *doubleValue * + std::pow(10, static_cast<double>(scaleMultiplier)); + } + else + { + valueIt = static_cast<int64_t>( + *doubleValue * std::pow(10, scaleMultiplier)); + } + } + } } - } } - } - BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; + BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; } /** @@ -342,106 +404,140 @@ void objectInterfacesToJson( * chassis. * @param SensorsAsyncResp Pointer to object holding response data */ -void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp) { - BMCWEB_LOG_DEBUG << "getChassisData enter"; - auto getChassisCb = [&, SensorsAsyncResp]( - boost::container::flat_set<std::string>& - sensorNames) { - BMCWEB_LOG_DEBUG << "getChassisCb enter"; - auto getConnectionCb = - [&, SensorsAsyncResp, sensorNames]( - const boost::container::flat_set<std::string>& connections) { - BMCWEB_LOG_DEBUG << "getConnectionCb enter"; - // Get managed objects from all services exposing sensors - for (const std::string& connection : connections) { - // Response handler to process managed objects - auto getManagedObjectsCb = [&, SensorsAsyncResp, sensorNames]( - const boost::system::error_code ec, - ManagedObjectsVectorType& resp) { - BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; - if (ec) { - BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec; - SensorsAsyncResp->setErrorStatus(); - return; - } - // Go through all objects and update response with - // sensor data - for (const auto& objDictEntry : resp) { - const std::string& objPath = - static_cast<const std::string&>(objDictEntry.first); - BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object " - << objPath; - - std::vector<std::string> split; - // Reserve space for - // /xyz/openbmc_project/sensors/<name>/<subname> - split.reserve(6); - boost::algorithm::split(split, objPath, boost::is_any_of("/")); - if (split.size() < 6) { - BMCWEB_LOG_ERROR << "Got path that isn't long enough " - << objPath; - continue; - } - // These indexes aren't intuitive, as boost::split puts an empty - // string at the beggining - const std::string& sensorType = split[4]; - const std::string& sensorName = split[5]; - BMCWEB_LOG_DEBUG << "sensorName " << sensorName - << " sensorType " << sensorType; - if (sensorNames.find(sensorName) == sensorNames.end()) { - BMCWEB_LOG_ERROR << sensorName << " not in sensor list "; - continue; - } - - const char* fieldName = nullptr; - if (sensorType == "temperature") { - fieldName = "Temperatures"; - } else if (sensorType == "fan" || sensorType == "fan_tach") { - fieldName = "Fans"; - } else if (sensorType == "voltage") { - fieldName = "Voltages"; - } else if (sensorType == "current") { - fieldName = "PowerSupply"; - } else if (sensorType == "power") { - fieldName = "PowerSupply"; - } else { - BMCWEB_LOG_ERROR << "Unsure how to handle sensorType " - << sensorType; - continue; - } - - nlohmann::json& tempArray = - SensorsAsyncResp->res.jsonValue[fieldName]; - - // Create the array if it doesn't yet exist - if (tempArray.is_array() == false) { - tempArray = nlohmann::json::array(); - } - - tempArray.push_back( - {{"@odata.id", "/redfish/v1/Chassis/" + - SensorsAsyncResp->chassisId + - "/Thermal#/" + sensorName}}); - nlohmann::json& sensorJson = tempArray.back(); - objectInterfacesToJson(sensorName, sensorType, - objDictEntry.second, sensorJson); - } - BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; +void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp) +{ + BMCWEB_LOG_DEBUG << "getChassisData enter"; + auto getChassisCb = [&, SensorsAsyncResp]( + boost::container::flat_set<std::string>& + sensorNames) { + BMCWEB_LOG_DEBUG << "getChassisCb enter"; + auto getConnectionCb = + [&, SensorsAsyncResp, sensorNames]( + const boost::container::flat_set<std::string>& connections) { + BMCWEB_LOG_DEBUG << "getConnectionCb enter"; + // Get managed objects from all services exposing sensors + for (const std::string& connection : connections) + { + // Response handler to process managed objects + auto getManagedObjectsCb = + [&, SensorsAsyncResp, + sensorNames](const boost::system::error_code ec, + ManagedObjectsVectorType& resp) { + BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; + if (ec) + { + BMCWEB_LOG_ERROR + << "getManagedObjectsCb DBUS error: " << ec; + SensorsAsyncResp->setErrorStatus(); + return; + } + // Go through all objects and update response with + // sensor data + for (const auto& objDictEntry : resp) + { + const std::string& objPath = + static_cast<const std::string&>( + objDictEntry.first); + BMCWEB_LOG_DEBUG + << "getManagedObjectsCb parsing object " + << objPath; + + std::vector<std::string> split; + // Reserve space for + // /xyz/openbmc_project/sensors/<name>/<subname> + split.reserve(6); + boost::algorithm::split(split, objPath, + boost::is_any_of("/")); + if (split.size() < 6) + { + BMCWEB_LOG_ERROR + << "Got path that isn't long enough " + << objPath; + continue; + } + // These indexes aren't intuitive, as + // boost::split puts an empty string at the + // beggining + const std::string& sensorType = split[4]; + const std::string& sensorName = split[5]; + BMCWEB_LOG_DEBUG << "sensorName " << sensorName + << " sensorType " + << sensorType; + if (sensorNames.find(sensorName) == + sensorNames.end()) + { + BMCWEB_LOG_ERROR << sensorName + << " not in sensor list "; + continue; + } + + const char* fieldName = nullptr; + if (sensorType == "temperature") + { + fieldName = "Temperatures"; + } + else if (sensorType == "fan" || + sensorType == "fan_tach") + { + fieldName = "Fans"; + } + else if (sensorType == "voltage") + { + fieldName = "Voltages"; + } + else if (sensorType == "current") + { + fieldName = "PowerSupply"; + } + else if (sensorType == "power") + { + fieldName = "PowerSupply"; + } + else + { + BMCWEB_LOG_ERROR + << "Unsure how to handle sensorType " + << sensorType; + continue; + } + + nlohmann::json& tempArray = + SensorsAsyncResp->res.jsonValue[fieldName]; + + // Create the array if it doesn't yet exist + if (tempArray.is_array() == false) + { + tempArray = nlohmann::json::array(); + } + + tempArray.push_back( + {{"@odata.id", + "/redfish/v1/Chassis/" + + SensorsAsyncResp->chassisId + + "/Thermal#/" + sensorName}}); + nlohmann::json& sensorJson = tempArray.back(); + objectInterfacesToJson(sensorName, sensorType, + objDictEntry.second, + sensorJson); + } + BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; + }; + crow::connections::systemBus->async_method_call( + getManagedObjectsCb, connection, "/", + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); + }; + BMCWEB_LOG_DEBUG << "getConnectionCb exit"; }; - crow::connections::systemBus->async_method_call( - getManagedObjectsCb, connection, "/", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }; - BMCWEB_LOG_DEBUG << "getConnectionCb exit"; - }; - // get connections and then pass it to get sensors - getConnections(SensorsAsyncResp, sensorNames, std::move(getConnectionCb)); - BMCWEB_LOG_DEBUG << "getChassisCb exit"; - }; - - // get chassis information related to sensors - getChassis(SensorsAsyncResp, std::move(getChassisCb)); - BMCWEB_LOG_DEBUG << "getChassisData exit"; + // get connections and then pass it to get sensors + getConnections(SensorsAsyncResp, sensorNames, + std::move(getConnectionCb)); + BMCWEB_LOG_DEBUG << "getChassisCb exit"; + }; + + // get chassis information related to sensors + getChassis(SensorsAsyncResp, std::move(getChassisCb)); + BMCWEB_LOG_DEBUG << "getChassisData exit"; }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp index 6fa9632c9b..d2c5163a8a 100644 --- a/redfish-core/lib/service_root.hpp +++ b/redfish-core/lib/service_root.hpp @@ -15,62 +15,69 @@ */ #pragma once -#include <systemd/sd-id128.h> #include "node.hpp" -namespace redfish { +#include <systemd/sd-id128.h> -class ServiceRoot : public Node { - public: - ServiceRoot(CrowApp& app) : Node(app, "/redfish/v1/") { - Node::json["@odata.type"] = "#ServiceRoot.v1_1_1.ServiceRoot"; - Node::json["@odata.id"] = "/redfish/v1/"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"; - Node::json["Id"] = "RootService"; - Node::json["Name"] = "Root Service"; - Node::json["RedfishVersion"] = "1.1.0"; - Node::json["Links"]["Sessions"] = { - {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; +namespace redfish +{ - Node::json["UUID"] = getUuid(); +class ServiceRoot : public Node +{ + public: + ServiceRoot(CrowApp& app) : Node(app, "/redfish/v1/") + { + Node::json["@odata.type"] = "#ServiceRoot.v1_1_1.ServiceRoot"; + Node::json["@odata.id"] = "/redfish/v1/"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"; + Node::json["Id"] = "RootService"; + Node::json["Name"] = "Root Service"; + Node::json["RedfishVersion"] = "1.1.0"; + Node::json["Links"]["Sessions"] = { + {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; - entityPrivileges = { - {boost::beast::http::verb::get, {}}, - {boost::beast::http::verb::head, {}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + Node::json["UUID"] = getUuid(); - private: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - res.jsonValue = Node::json; - res.end(); - } + entityPrivileges = { + {boost::beast::http::verb::get, {}}, + {boost::beast::http::verb::head, {}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - const std::string getUuid() { - // If we are using a version of systemd that can get the app specific uuid, - // use that + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + res.jsonValue = Node::json; + res.end(); + } + + const std::string getUuid() + { + // If we are using a version of systemd that can get the app specific + // uuid, use that #ifdef sd_id128_get_machine_app_specific - std::array<char, SD_ID128_STRING_MAX> string; - sd_id128_t id = SD_ID128_NULL; + std::array<char, SD_ID128_STRING_MAX> string; + sd_id128_t id = SD_ID128_NULL; - // This ID needs to match the one in ipmid - int r = sd_id128_get_machine_app_specific( - SD_ID128_MAKE(e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, - 45, 78), - &id); - if (r < 0) { - return "00000000-0000-0000-0000-000000000000"; - } - return string.data(); + // This ID needs to match the one in ipmid + int r = sd_id128_get_machine_app_specific( + SD_ID128_MAKE(e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, + 12, 45, 78), + &id); + if (r < 0) + { + return "00000000-0000-0000-0000-000000000000"; + } + return string.data(); #else - return "00000000-0000-0000-0000-000000000000"; + return "00000000-0000-0000-0000-000000000000"; #endif - } + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index b7f91b9916..cd49883859 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -15,36 +15,44 @@ */ #pragma once +#include "boost/container/flat_map.hpp" +#include "node.hpp" + #include <error_messages.hpp> #include <utils/json_utils.hpp> -#include "node.hpp" -#include "boost/container/flat_map.hpp" -namespace redfish { +namespace redfish +{ /** * SystemAsyncResp * Gathers data needed for response processing after async calls are done */ -class SystemAsyncResp { - public: - SystemAsyncResp(crow::Response &response) : res(response) {} +class SystemAsyncResp +{ + public: + SystemAsyncResp(crow::Response &response) : res(response) + { + } - ~SystemAsyncResp() { - if (res.result() != (boost::beast::http::status::ok)) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = messages::internalError(); + ~SystemAsyncResp() + { + if (res.result() != (boost::beast::http::status::ok)) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = messages::internalError(); + } + res.end(); } - res.end(); - } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response &res; + crow::Response &res; }; /** @@ -59,628 +67,853 @@ class SystemAsyncResp { * This perhaps shall be different file, which has to be chosen on compile time * depending on OEM needs */ -class OnDemandSystemsProvider { - public: - template <typename CallbackFunc> - void getBaseboardList(CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get list of available boards."; - crow::connections::systemBus->async_method_call( - [callback{std::move(callback)}](const boost::system::error_code ec, - const std::vector<std::string> &resp) { - // Callback requires vector<string> to retrieve all available board - // list. - std::vector<std::string> boardList; - if (ec) { - // Something wrong on DBus, the error_code is not important at this - // moment, just return success=false, and empty output. Since size - // of vector may vary depending on information from Entity Manager, - // and empty output could not be treated same way as error. - callback(false, boardList); - return; - } - BMCWEB_LOG_DEBUG << "Got " << resp.size() << " boards."; - // Iterate over all retrieved ObjectPaths. - for (const std::string &objpath : resp) { - std::size_t lastPos = objpath.rfind("/"); - if (lastPos != std::string::npos) { - boardList.emplace_back(objpath.substr(lastPos + 1)); - } - } - // Finally make a callback with useful data - callback(true, boardList); - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", - "/xyz/openbmc_project/inventory", int32_t(0), - std::array<const char *, 1>{ - "xyz.openbmc_project.Inventory.Item.Board"}); - }; - - /** - * @brief Retrieves computer system properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls - * @param[in] name Computer system name from request - * - * @return None. - */ - void getComputerSystem(std::shared_ptr<SystemAsyncResp> aResp, - const std::string &name) { - const std::array<const char *, 5> interfaces = { - "xyz.openbmc_project.Inventory.Decorator.Asset", - "xyz.openbmc_project.Inventory.Item.Cpu", - "xyz.openbmc_project.Inventory.Item.Dimm", - "xyz.openbmc_project.Inventory.Item.System", - "xyz.openbmc_project.Common.UUID", - }; - BMCWEB_LOG_DEBUG << "Get available system components."; - crow::connections::systemBus->async_method_call( - [ name, aResp{std::move(aResp)} ]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error"; - aResp->setErrorStatus(); - return; - } - bool foundName = false; - // Iterate over all retrieved ObjectPaths. - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &object : subtree) { - const std::string &path = object.first; - BMCWEB_LOG_DEBUG << "Got path: " << path; - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connectionNames = object.second; - if (connectionNames.size() < 1) { - continue; - } - // Check if computer system exist - if (boost::ends_with(path, name)) { - foundName = true; - BMCWEB_LOG_DEBUG << "Found name: " << name; - const std::string connectionName = connectionNames[0].first; - crow::connections::systemBus->async_method_call( - [ aResp, name(std::string(name)) ]( - const boost::system::error_code ec, - const std::vector<std::pair<std::string, VariantType>> - &propertiesList) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error: " << ec; - aResp->setErrorStatus(); - return; +class OnDemandSystemsProvider +{ + public: + template <typename CallbackFunc> + void getBaseboardList(CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get list of available boards."; + crow::connections::systemBus->async_method_call( + [callback{std::move(callback)}]( + const boost::system::error_code ec, + const std::vector<std::string> &resp) { + // Callback requires vector<string> to retrieve all available + // board list. + std::vector<std::string> boardList; + if (ec) + { + // Something wrong on DBus, the error_code is not important + // at this moment, just return success=false, and empty + // output. Since size of vector may vary depending on + // information from Entity Manager, and empty output could + // not be treated same way as error. + callback(false, boardList); + return; + } + BMCWEB_LOG_DEBUG << "Got " << resp.size() << " boards."; + // Iterate over all retrieved ObjectPaths. + for (const std::string &objpath : resp) + { + std::size_t lastPos = objpath.rfind("/"); + if (lastPos != std::string::npos) + { + boardList.emplace_back(objpath.substr(lastPos + 1)); } - BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() - << "properties for system"; - for (const std::pair<std::string, VariantType> &property : - propertiesList) { - const std::string *value = - mapbox::getPtr<const std::string>(property.second); - if (value != nullptr) { - aResp->res.jsonValue[property.first] = *value; - } + } + // Finally make a callback with useful data + callback(true, boardList); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + "/xyz/openbmc_project/inventory", int32_t(0), + std::array<const char *, 1>{ + "xyz.openbmc_project.Inventory.Item.Board"}); + }; + + /** + * @brief Retrieves computer system properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls + * @param[in] name Computer system name from request + * + * @return None. + */ + void getComputerSystem(std::shared_ptr<SystemAsyncResp> aResp, + const std::string &name) + { + const std::array<const char *, 5> interfaces = { + "xyz.openbmc_project.Inventory.Decorator.Asset", + "xyz.openbmc_project.Inventory.Item.Cpu", + "xyz.openbmc_project.Inventory.Item.Dimm", + "xyz.openbmc_project.Inventory.Item.System", + "xyz.openbmc_project.Common.UUID", + }; + BMCWEB_LOG_DEBUG << "Get available system components."; + crow::connections::systemBus->async_method_call( + [name, aResp{std::move(aResp)}]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error"; + aResp->setErrorStatus(); + return; + } + bool foundName = false; + // Iterate over all retrieved ObjectPaths. + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &object : subtree) + { + const std::string &path = object.first; + BMCWEB_LOG_DEBUG << "Got path: " << path; + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connectionNames = object.second; + if (connectionNames.size() < 1) + { + continue; } - aResp->res.jsonValue["Name"] = name; - aResp->res.jsonValue["Id"] = - aResp->res.jsonValue["SerialNumber"]; - }, - connectionName, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); - } else { - // This is not system, so check if it's cpu, dimm, UUID or BiosVer - for (auto const &s : connectionNames) { - for (auto const &i : s.second) { - if (boost::ends_with(i, "Dimm")) { - BMCWEB_LOG_DEBUG << "Found Dimm, now get it properties."; - crow::connections::systemBus->async_method_call( - [&, aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "Dimm properties."; - for (const auto &p : properties) { - if (p.first == "MemorySize") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if ((value != nullptr) && (*value != "NULL")) { - // Remove units char - int32_t unitCoeff; - if (boost::ends_with(*value, "MB")) { - unitCoeff = 1000; - } else if (boost::ends_with(*value, "KB")) { - unitCoeff = 1000000; - } else { - BMCWEB_LOG_ERROR - << "Unsupported memory units"; - aResp->setErrorStatus(); - return; + // Check if computer system exist + if (boost::ends_with(path, name)) + { + foundName = true; + BMCWEB_LOG_DEBUG << "Found name: " << name; + const std::string connectionName = + connectionNames[0].first; + crow::connections::systemBus->async_method_call( + [aResp, name(std::string(name))]( + const boost::system::error_code ec, + const std::vector< + std::pair<std::string, VariantType>> + &propertiesList) { + if (ec) + { + BMCWEB_LOG_ERROR << "DBUS response error: " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " + << propertiesList.size() + << "properties for system"; + for (const std::pair<std::string, VariantType> + &property : propertiesList) + { + const std::string *value = + mapbox::getPtr<const std::string>( + property.second); + if (value != nullptr) + { + aResp->res.jsonValue[property.first] = + *value; + } } + aResp->res.jsonValue["Name"] = name; + aResp->res.jsonValue["Id"] = + aResp->res.jsonValue["SerialNumber"]; + }, + connectionName, path, + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Inventory.Decorator.Asset"); + } + else + { + // This is not system, so check if it's cpu, dimm, UUID + // or BiosVer + for (auto const &s : connectionNames) + { + for (auto const &i : s.second) + { + if (boost::ends_with(i, "Dimm")) + { + BMCWEB_LOG_DEBUG + << "Found Dimm, now get it properties."; + crow::connections::systemBus->async_method_call( + [&, aResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR + << "DBUS response error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " << properties.size() + << "Dimm properties."; + for (const auto &p : properties) + { + if (p.first == "MemorySize") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + if ((value != nullptr) && + (*value != "NULL")) + { + // Remove units char + int32_t unitCoeff; + if (boost::ends_with( + *value, "MB")) + { + unitCoeff = 1000; + } + else if (boost:: + ends_with( + *value, + "KB")) + { + unitCoeff = 1000000; + } + else + { + BMCWEB_LOG_ERROR + << "Unsupported" + " memory " + "units"; + aResp + ->setErrorStatus(); + return; + } - auto memSize = boost::lexical_cast<int>( - value->substr(0, value->length() - 2)); - aResp->res.jsonValue["TotalSystemMemoryGiB"] += - memSize * unitCoeff; - aResp->res.jsonValue["MemorySummary"]["Status"] - ["State"] = "Enabled"; - } - } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Item.Dimm"); - } else if (boost::ends_with(i, "Cpu")) { - BMCWEB_LOG_DEBUG << "Found Cpu, now get it properties."; - crow::connections::systemBus->async_method_call( - [&, aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "Cpu properties."; - for (const auto &p : properties) { - if (p.first == "ProcessorFamily") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if (value != nullptr) { - aResp->res - .jsonValue["ProcessorSummary"]["Count"] = - aResp->res - .jsonValue["ProcessorSummary"]["Count"] - .get<int>() + - 1; - aResp->res.jsonValue["ProcessorSummary"] - ["Status"]["State"] = - "Enabled"; - aResp->res - .jsonValue["ProcessorSummary"]["Model"] = - *value; - } - } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Inventory.Item.Cpu"); - } else if (boost::ends_with(i, "UUID")) { - BMCWEB_LOG_DEBUG << "Found UUID, now get it properties."; - crow::connections::systemBus->async_method_call( - [aResp](const boost::system::error_code ec, - const std::vector<std::pair< - std::string, VariantType>> &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() - << "UUID properties."; - for (const std::pair<std::string, VariantType> &p : - properties) { - if (p.first == "BIOSVer") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - if (value != nullptr) { - aResp->res.jsonValue["BiosVersion"] = *value; - } - } - if (p.first == "UUID") { - const std::string *value = - mapbox::getPtr<const std::string>(p.second); - BMCWEB_LOG_DEBUG << "UUID = " << *value - << " length " << value->length(); - if (value != nullptr) { - // Workaround for to short return str in smbios - // demo app, 32 bytes are described by spec - if (value->length() > 0 && - value->length() < 32) { - std::string correctedValue = *value; - correctedValue.append(32 - value->length(), - '0'); - value = &correctedValue; - } else if (value->length() == 32) { - aResp->res.jsonValue["UUID"] = - value->substr(0, 8) + "-" + - value->substr(8, 4) + "-" + - value->substr(12, 4) + "-" + - value->substr(16, 4) + "-" + - value->substr(20, 12); + auto memSize = + boost::lexical_cast< + int>(value->substr( + 0, + value->length() - + 2)); + aResp->res.jsonValue + ["TotalSystemMemory" + "GiB"] += + memSize * unitCoeff; + aResp->res.jsonValue + ["MemorySummary"] + ["Status"] + ["State"] = + "Enabled"; + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory.Item." + "Dimm"); + } + else if (boost::ends_with(i, "Cpu")) + { + BMCWEB_LOG_DEBUG + << "Found Cpu, now get it properties."; + crow::connections::systemBus + ->async_method_call( + [&, aResp]( + const boost::system::error_code + ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR + << "DBUS response " + "error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " + << properties.size() + << "Cpu properties."; + for (const auto &p : properties) + { + if (p.first == + "ProcessorFamily") + { + const std::string + *value = + mapbox::getPtr< + const std:: + string>( + p.second); + if (value != nullptr) + { + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Count"] = + aResp->res + .jsonValue + ["Proce" + "ssorS" + "ummar" + "y"] + ["Coun" + "t"] + .get< + int>() + + 1; + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Status"] + ["State"] = + "Enabled"; + aResp->res.jsonValue + ["ProcessorSumm" + "ary"] + ["Model"] = + *value; + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Inventory." + "Item.Cpu"); + } + else if (boost::ends_with(i, "UUID")) + { + BMCWEB_LOG_DEBUG + << "Found UUID, now get it properties."; + crow::connections::systemBus->async_method_call( + [aResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, VariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG + << "DBUS response error " + << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG + << "Got " << properties.size() + << "UUID properties."; + for (const std::pair<std::string, + VariantType> + &p : properties) + { + if (p.first == "BIOSVer") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + if (value != nullptr) + { + aResp->res.jsonValue + ["BiosVersion"] = + *value; + } + } + if (p.first == "UUID") + { + const std::string *value = + mapbox::getPtr< + const std::string>( + p.second); + BMCWEB_LOG_DEBUG + << "UUID = " << *value + << " length " + << value->length(); + if (value != nullptr) + { + // Workaround for to + // short return str in + // smbios demo app, 32 + // bytes are described + // by spec + if (value->length() > + 0 && + value->length() < + 32) + { + std::string + correctedValue = + *value; + correctedValue.append( + 32 - + value + ->length(), + '0'); + value = + &correctedValue; + } + else if ( + value->length() == + 32) + { + aResp->res.jsonValue + ["UUID"] = + value->substr( + 0, 8) + + "-" + + value->substr( + 8, 4) + + "-" + + value->substr( + 12, 4) + + "-" + + value->substr( + 16, 4) + + "-" + + value->substr( + 20, 12); + } + } + } + } + }, + s.first, path, + "org.freedesktop.DBus.Properties", + "GetAll", + "xyz.openbmc_project.Common.UUID"); } - } } - } - }, - s.first, path, "org.freedesktop.DBus.Properties", - "GetAll", "xyz.openbmc_project.Common.UUID"); - } + } + } } - } - } - } - if (foundName == false) { - aResp->setErrorStatus(); - } - }, - "xyz.openbmc_project.ObjectMapper", - "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", - "/xyz/openbmc_project/inventory", int32_t(0), interfaces); - } + if (foundName == false) + { + aResp->setErrorStatus(); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/inventory", int32_t(0), interfaces); + } - /** - * @brief Retrieves identify led group properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls. - * @param[in] callback Callback for process retrieved data. - * - * @return None. - */ - template <typename CallbackFunc> - void getLedGroupIdentify(std::shared_ptr<SystemAsyncResp> aResp, - CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get led groups"; - crow::connections::systemBus->async_method_call( - [ - aResp{std::move(aResp)}, &callback - ](const boost::system::error_code &ec, const ManagedObjectsType &resp) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << resp.size() << "led group objects."; - for (const auto &objPath : resp) { - const std::string &path = objPath.first; - if (path.rfind("enclosure_identify") != std::string::npos) { - for (const auto &interface : objPath.second) { - if (interface.first == "xyz.openbmc_project.Led.Group") { - for (const auto &property : interface.second) { - if (property.first == "Asserted") { - const bool *asserted = - mapbox::getPtr<const bool>(property.second); - if (nullptr != asserted) { - callback(*asserted, aResp); - } else { - callback(false, aResp); - } + /** + * @brief Retrieves identify led group properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls. + * @param[in] callback Callback for process retrieved data. + * + * @return None. + */ + template <typename CallbackFunc> + void getLedGroupIdentify(std::shared_ptr<SystemAsyncResp> aResp, + CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get led groups"; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}, + &callback](const boost::system::error_code &ec, + const ManagedObjectsType &resp) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " << resp.size() + << "led group objects."; + for (const auto &objPath : resp) + { + const std::string &path = objPath.first; + if (path.rfind("enclosure_identify") != std::string::npos) + { + for (const auto &interface : objPath.second) + { + if (interface.first == + "xyz.openbmc_project.Led.Group") + { + for (const auto &property : interface.second) + { + if (property.first == "Asserted") + { + const bool *asserted = + mapbox::getPtr<const bool>( + property.second); + if (nullptr != asserted) + { + callback(*asserted, aResp); + } + else + { + callback(false, aResp); + } + } + } + } + } } - } } - } - } - } - }, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups", "org.freedesktop.DBus.ObjectManager", - "GetManagedObjects"); - } + }, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + } - template <typename CallbackFunc> - void getLedIdentify(std::shared_ptr<SystemAsyncResp> aResp, - CallbackFunc &&callback) { - BMCWEB_LOG_DEBUG << "Get identify led properties"; - crow::connections::systemBus->async_method_call( - [ aResp{std::move(aResp)}, &callback ]( - const boost::system::error_code ec, - const PropertiesType &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() << "led properties."; - std::string output; - for (const auto &property : properties) { - if (property.first == "State") { - const std::string *s = - mapbox::getPtr<std::string>(property.second); - if (nullptr != s) { - BMCWEB_LOG_DEBUG << "Identify Led State: " << *s; - const auto pos = s->rfind('.'); - if (pos != std::string::npos) { - auto led = s->substr(pos + 1); - for (const std::pair<const char *, const char *> &p : - std::array<std::pair<const char *, const char *>, 3>{ - {{"On", "Lit"}, - {"Blink", "Blinking"}, - {"Off", "Off"}}}) { - if (led == p.first) { - output = p.second; + template <typename CallbackFunc> + void getLedIdentify(std::shared_ptr<SystemAsyncResp> aResp, + CallbackFunc &&callback) + { + BMCWEB_LOG_DEBUG << "Get identify led properties"; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}, + &callback](const boost::system::error_code ec, + const PropertiesType &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Got " << properties.size() + << "led properties."; + std::string output; + for (const auto &property : properties) + { + if (property.first == "State") + { + const std::string *s = + mapbox::getPtr<std::string>(property.second); + if (nullptr != s) + { + BMCWEB_LOG_DEBUG << "Identify Led State: " << *s; + const auto pos = s->rfind('.'); + if (pos != std::string::npos) + { + auto led = s->substr(pos + 1); + for (const std::pair<const char *, const char *> + &p : + std::array< + std::pair<const char *, const char *>, + 3>{{{"On", "Lit"}, + {"Blink", "Blinking"}, + {"Off", "Off"}}}) + { + if (led == p.first) + { + output = p.second; + } + } + } + } } - } } - } - } - } - callback(output, aResp); - }, - "xyz.openbmc_project.LED.Controller.identify", - "/xyz/openbmc_project/led/physical/identify", - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.Led.Physical"); - } + callback(output, aResp); + }, + "xyz.openbmc_project.LED.Controller.identify", + "/xyz/openbmc_project/led/physical/identify", + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Led.Physical"); + } - /** - * @brief Retrieves host state properties over dbus - * - * @param[in] aResp Shared pointer for completing asynchronous calls. - * - * @return None. - */ - void getHostState(std::shared_ptr<SystemAsyncResp> aResp) { - BMCWEB_LOG_DEBUG << "Get host information."; - crow::connections::systemBus->async_method_call( - [aResp{std::move(aResp)}](const boost::system::error_code ec, - const PropertiesType &properties) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - aResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Got " << properties.size() << "host properties."; - for (const auto &property : properties) { - if (property.first == "CurrentHostState") { - const std::string *s = - mapbox::getPtr<const std::string>(property.second); - BMCWEB_LOG_DEBUG << "Host state: " << *s; - if (nullptr != s) { - const auto pos = s->rfind('.'); - if (pos != std::string::npos) { - // Verify Host State - if (s->substr(pos + 1) == "Running") { - aResp->res.jsonValue["PowerState"] = "On"; - aResp->res.jsonValue["Status"]["State"] = "Enabled"; - } else { - aResp->res.jsonValue["PowerState"] = "Off"; - aResp->res.jsonValue["Status"]["State"] = "Disabled"; - } + /** + * @brief Retrieves host state properties over dbus + * + * @param[in] aResp Shared pointer for completing asynchronous calls. + * + * @return None. + */ + void getHostState(std::shared_ptr<SystemAsyncResp> aResp) + { + BMCWEB_LOG_DEBUG << "Get host information."; + crow::connections::systemBus->async_method_call( + [aResp{std::move(aResp)}](const boost::system::error_code ec, + const PropertiesType &properties) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + aResp->setErrorStatus(); + return; } - } - } - } - }, - "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.State.Host"); - } + BMCWEB_LOG_DEBUG << "Got " << properties.size() + << "host properties."; + for (const auto &property : properties) + { + if (property.first == "CurrentHostState") + { + const std::string *s = + mapbox::getPtr<const std::string>(property.second); + BMCWEB_LOG_DEBUG << "Host state: " << *s; + if (nullptr != s) + { + const auto pos = s->rfind('.'); + if (pos != std::string::npos) + { + // Verify Host State + if (s->substr(pos + 1) == "Running") + { + aResp->res.jsonValue["PowerState"] = "On"; + aResp->res.jsonValue["Status"]["State"] = + "Enabled"; + } + else + { + aResp->res.jsonValue["PowerState"] = "Off"; + aResp->res.jsonValue["Status"]["State"] = + "Disabled"; + } + } + } + } + } + }, + "xyz.openbmc_project.State.Host", + "/xyz/openbmc_project/state/host0", + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.State.Host"); + } }; /** * SystemsCollection derived class for delivering ComputerSystems Collection * Schema */ -class SystemsCollection : public Node { - public: - SystemsCollection(CrowApp &app) : Node(app, "/redfish/v1/Systems/") { - Node::json["@odata.type"] = - "#ComputerSystemCollection.ComputerSystemCollection"; - Node::json["@odata.id"] = "/redfish/v1/Systems"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#ComputerSystemCollection.ComputerSystemCollection"; - Node::json["Name"] = "Computer System Collection"; +class SystemsCollection : public Node +{ + public: + SystemsCollection(CrowApp &app) : Node(app, "/redfish/v1/Systems/") + { + Node::json["@odata.type"] = + "#ComputerSystemCollection.ComputerSystemCollection"; + Node::json["@odata.id"] = "/redfish/v1/Systems"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#ComputerSystemCollection.ComputerSystemCollection"; + Node::json["Name"] = "Computer System Collection"; - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - private: - /** - * Functions triggers appropriate requests on DBus - */ - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - // Get board list, and call the below callback for JSON preparation - provider.getBaseboardList( - [&](const bool &success, const std::vector<std::string> &output) { - if (success) { - // ... prepare json array with appropriate @odata.id links - nlohmann::json boardArray = nlohmann::json::array(); - for (const std::string &boardItem : output) { - boardArray.push_back( - {{"@odata.id", "/redfish/v1/Systems/" + boardItem}}); + private: + /** + * Functions triggers appropriate requests on DBus + */ + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + // Get board list, and call the below callback for JSON preparation + provider.getBaseboardList([&](const bool &success, + const std::vector<std::string> &output) { + if (success) + { + // ... prepare json array with appropriate @odata.id links + nlohmann::json boardArray = nlohmann::json::array(); + for (const std::string &boardItem : output) + { + boardArray.push_back( + {{"@odata.id", "/redfish/v1/Systems/" + boardItem}}); + } + // Then attach members, count size and return, + Node::json["Members"] = boardArray; + Node::json["Members@odata.count"] = boardArray.size(); + res.jsonValue = Node::json; } - // Then attach members, count size and return, - Node::json["Members"] = boardArray; - Node::json["Members@odata.count"] = boardArray.size(); - res.jsonValue = Node::json; - } else { - // ... otherwise, return INTERNALL ERROR - res.result(boost::beast::http::status::internal_server_error); - } - res.end(); + else + { + // ... otherwise, return INTERNALL ERROR + res.result(boost::beast::http::status::internal_server_error); + } + res.end(); }); - } + } - OnDemandSystemsProvider provider; + OnDemandSystemsProvider provider; }; /** * Systems override class for delivering ComputerSystems Schema */ -class Systems : public Node { - public: - /* - * Default Constructor - */ - Systems(CrowApp &app) - : Node(app, "/redfish/v1/Systems/<str>/", std::string()) { - Node::json["@odata.type"] = "#ComputerSystem.v1_3_0.ComputerSystem"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#ComputerSystem.ComputerSystem"; - Node::json["SystemType"] = "Physical"; - Node::json["Description"] = "Computer System"; - Node::json["Boot"]["BootSourceOverrideEnabled"] = - "Disabled"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideTarget"] = - "None"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideMode"] = - "Legacy"; // TODO(Dawid), get real boot data - Node::json["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] = { - "None", "Pxe", "Hdd", "Cd", - "BiosSetup", "UefiShell", "Usb"}; // TODO(Dawid), get real boot data - Node::json["ProcessorSummary"]["Count"] = int(0); - Node::json["ProcessorSummary"]["Status"]["State"] = "Disabled"; - Node::json["MemorySummary"]["TotalSystemMemoryGiB"] = int(0); - Node::json["MemorySummary"]["Status"]["State"] = "Disabled"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } +class Systems : public Node +{ + public: + /* + * Default Constructor + */ + Systems(CrowApp &app) : + Node(app, "/redfish/v1/Systems/<str>/", std::string()) + { + Node::json["@odata.type"] = "#ComputerSystem.v1_3_0.ComputerSystem"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#ComputerSystem.ComputerSystem"; + Node::json["SystemType"] = "Physical"; + Node::json["Description"] = "Computer System"; + Node::json["Boot"]["BootSourceOverrideEnabled"] = + "Disabled"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideTarget"] = + "None"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideMode"] = + "Legacy"; // TODO(Dawid), get real boot data + Node::json["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] = + {"None", "Pxe", "Hdd", "Cd", + "BiosSetup", "UefiShell", "Usb"}; // TODO(Dawid), get real boot + // data + Node::json["ProcessorSummary"]["Count"] = int(0); + Node::json["ProcessorSummary"]["Status"]["State"] = "Disabled"; + Node::json["MemorySummary"]["TotalSystemMemoryGiB"] = int(0); + Node::json["MemorySummary"]["Status"]["State"] = "Disabled"; - private: - OnDemandSystemsProvider provider; - - /** - * 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() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - const std::string &name = params[0]; + private: + OnDemandSystemsProvider provider; + + /** + * 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() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } - res.jsonValue = Node::json; - res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; + const std::string &name = params[0]; - auto asyncResp = std::make_shared<SystemAsyncResp>(res); + res.jsonValue = Node::json; + res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; - provider.getLedGroupIdentify( - asyncResp, [&](const bool &asserted, - const std::shared_ptr<SystemAsyncResp> &aResp) { - if (asserted) { - // If led group is asserted, then another call is needed to - // get led status - provider.getLedIdentify( - aResp, [](const std::string &ledStatus, - const std::shared_ptr<SystemAsyncResp> &aResp) { - if (!ledStatus.empty()) { - aResp->res.jsonValue["IndicatorLED"] = ledStatus; - } - }); - } else { - aResp->res.jsonValue["IndicatorLED"] = "Off"; - } - }); - provider.getComputerSystem(asyncResp, name); - provider.getHostState(asyncResp); - } + auto asyncResp = std::make_shared<SystemAsyncResp>(res); - void doPatch(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() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - // Parse JSON request body - nlohmann::json patch; - if (!json_util::processJsonFromRequest(res, req, patch)) { - return; - } - // Find key with new led value - const std::string &name = params[0]; - const std::string *reqLedState = nullptr; - json_util::Result r = json_util::getString( - "IndicatorLED", patch, reqLedState, - static_cast<int>(json_util::MessageSetting::TYPE_ERROR) | - static_cast<int>(json_util::MessageSetting::MISSING), - res.jsonValue, std::string("/" + name + "/IndicatorLED")); - if ((r != json_util::Result::SUCCESS) || (reqLedState == nullptr)) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - // Verify key value - std::string dbusLedState; - for (const auto &p : boost::container::flat_map<const char *, const char *>{ - {"On", "Lit"}, {"Blink", "Blinking"}, {"Off", "Off"}}) { - if (*reqLedState == p.second) { - dbusLedState = p.first; - } + provider.getLedGroupIdentify( + asyncResp, [&](const bool &asserted, + const std::shared_ptr<SystemAsyncResp> &aResp) { + if (asserted) + { + // If led group is asserted, then another call is needed to + // get led status + provider.getLedIdentify( + aResp, + [](const std::string &ledStatus, + const std::shared_ptr<SystemAsyncResp> &aResp) { + if (!ledStatus.empty()) + { + aResp->res.jsonValue["IndicatorLED"] = + ledStatus; + } + }); + } + else + { + aResp->res.jsonValue["IndicatorLED"] = "Off"; + } + }); + provider.getComputerSystem(asyncResp, name); + provider.getHostState(asyncResp); } - // Update led status - auto asyncResp = std::make_shared<SystemAsyncResp>(res); - res.jsonValue = Node::json; - res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; + void doPatch(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() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + // Parse JSON request body + nlohmann::json patch; + if (!json_util::processJsonFromRequest(res, req, patch)) + { + return; + } + // Find key with new led value + const std::string &name = params[0]; + const std::string *reqLedState = nullptr; + json_util::Result r = json_util::getString( + "IndicatorLED", patch, reqLedState, + static_cast<int>(json_util::MessageSetting::TYPE_ERROR) | + static_cast<int>(json_util::MessageSetting::MISSING), + res.jsonValue, std::string("/" + name + "/IndicatorLED")); + if ((r != json_util::Result::SUCCESS) || (reqLedState == nullptr)) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + // Verify key value + std::string dbusLedState; + for (const auto &p : + boost::container::flat_map<const char *, const char *>{ + {"On", "Lit"}, {"Blink", "Blinking"}, {"Off", "Off"}}) + { + if (*reqLedState == p.second) + { + dbusLedState = p.first; + } + } - provider.getHostState(asyncResp); - provider.getComputerSystem(asyncResp, name); + // Update led status + auto asyncResp = std::make_shared<SystemAsyncResp>(res); + res.jsonValue = Node::json; + res.jsonValue["@odata.id"] = "/redfish/v1/Systems/" + name; - if (dbusLedState.empty()) { - messages::addMessageToJsonRoot( - res.jsonValue, - messages::propertyValueNotInList(*reqLedState, "IndicatorLED")); - } else { - // Update led group - BMCWEB_LOG_DEBUG << "Update led group."; - crow::connections::systemBus->async_method_call( - [&, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - asyncResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Led group update done."; - }, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups/enclosure_identify", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Led.Group", "Asserted", - sdbusplus::message::variant<bool>( - (dbusLedState == "Off" ? false : true))); - // Update identify led status - BMCWEB_LOG_DEBUG << "Update led SoftwareInventoryCollection."; - crow::connections::systemBus->async_method_call( - [&, asyncResp{std::move(asyncResp)} ]( - const boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_DEBUG << "DBUS response error " << ec; - asyncResp->setErrorStatus(); - return; - } - BMCWEB_LOG_DEBUG << "Led state update done."; - res.jsonValue["IndicatorLED"] = *reqLedState; - }, - "xyz.openbmc_project.LED.Controller.identify", - "/xyz/openbmc_project/led/physical/identify", - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Led.Physical", "State", - sdbusplus::message::variant<std::string>( - "xyz.openbmc_project.Led.Physical.Action." + dbusLedState)); + provider.getHostState(asyncResp); + provider.getComputerSystem(asyncResp, name); + + if (dbusLedState.empty()) + { + messages::addMessageToJsonRoot( + res.jsonValue, + messages::propertyValueNotInList(*reqLedState, "IndicatorLED")); + } + else + { + // Update led group + BMCWEB_LOG_DEBUG << "Update led group."; + crow::connections::systemBus->async_method_call( + [&, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + asyncResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Led group update done."; + }, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups/enclosure_identify", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Led.Group", "Asserted", + sdbusplus::message::variant<bool>( + (dbusLedState == "Off" ? false : true))); + // Update identify led status + BMCWEB_LOG_DEBUG << "Update led SoftwareInventoryCollection."; + crow::connections::systemBus->async_method_call( + [&, asyncResp{std::move(asyncResp)}]( + const boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_DEBUG << "DBUS response error " << ec; + asyncResp->setErrorStatus(); + return; + } + BMCWEB_LOG_DEBUG << "Led state update done."; + res.jsonValue["IndicatorLED"] = *reqLedState; + }, + "xyz.openbmc_project.LED.Controller.identify", + "/xyz/openbmc_project/led/physical/identify", + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Led.Physical", "State", + sdbusplus::message::variant<std::string>( + "xyz.openbmc_project.Led.Physical.Action." + dbusLedState)); + } } - } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp index 8fb291b9bb..37b0f43f3a 100644 --- a/redfish-core/lib/thermal.hpp +++ b/redfish-core/lib/thermal.hpp @@ -18,44 +18,49 @@ #include "node.hpp" #include "sensors.hpp" -namespace redfish { +namespace redfish +{ -class Thermal : public Node { - public: - Thermal(CrowApp& app) - : Node((app), "/redfish/v1/Chassis/<str>/Thermal/", std::string()) { - Node::json["@odata.type"] = "#Thermal.v1_4_0.Thermal"; - Node::json["@odata.context"] = "/redfish/v1/$metadata#Thermal.Thermal"; - Node::json["Id"] = "Thermal"; - Node::json["Name"] = "Thermal"; +class Thermal : public Node +{ + public: + Thermal(CrowApp& app) : + Node((app), "/redfish/v1/Chassis/<str>/Thermal/", std::string()) + { + Node::json["@odata.type"] = "#Thermal.v1_4_0.Thermal"; + Node::json["@odata.context"] = "/redfish/v1/$metadata#Thermal.Thermal"; + Node::json["Id"] = "Thermal"; + Node::json["Name"] = "Thermal"; - 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: - void doGet(crow::Response& res, const crow::Request& req, - const std::vector<std::string>& params) override { - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + 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"}}}}; } - const std::string& chassisName = params[0]; - res.jsonValue = Node::json; - auto asyncResp = std::make_shared<SensorsAsyncResp>( - res, chassisName, - std::initializer_list<const char*>{ - "/xyz/openbmc_project/sensors/fan", - "/xyz/openbmc_project/sensors/temperature"}); - getChassisData(asyncResp); - } + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + const std::string& chassisName = params[0]; + + res.jsonValue = Node::json; + auto asyncResp = std::make_shared<SensorsAsyncResp>( + res, chassisName, + std::initializer_list<const char*>{ + "/xyz/openbmc_project/sensors/fan", + "/xyz/openbmc_project/sensors/temperature"}); + getChassisData(asyncResp); + } }; -} // namespace redfish +} // namespace redfish diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp index 677b6d1741..658937d9a5 100644 --- a/redfish-core/lib/update_service.hpp +++ b/redfish-core/lib/update_service.hpp @@ -16,381 +16,450 @@ #pragma once #include "node.hpp" + #include <boost/container/flat_map.hpp> -namespace redfish { +namespace redfish +{ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; -class UpdateService : public Node { - public: - UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") { - Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; - Node::json["@odata.id"] = "/redfish/v1/UpdateService"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#UpdateService.UpdateService"; - Node::json["Id"] = "UpdateService"; - Node::json["Description"] = "Service for Software Update"; - Node::json["Name"] = "Update Service"; - Node::json["HttpPushUri"] = "/redfish/v1/UpdateService"; - // UpdateService cannot be disabled - Node::json["ServiceEnabled"] = true; - Node::json["FirmwareInventory"] = { - {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - res.jsonValue = Node::json; - res.end(); - } - static void activateImage(const std::string &objPath) { - crow::connections::systemBus->async_method_call( - [objPath](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", objPath, - "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; +class UpdateService : public Node +{ + public: + UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") + { + Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; + Node::json["@odata.id"] = "/redfish/v1/UpdateService"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#UpdateService.UpdateService"; + Node::json["Id"] = "UpdateService"; + Node::json["Description"] = "Service for Software Update"; + Node::json["Name"] = "Update Service"; + Node::json["HttpPushUri"] = "/redfish/v1/UpdateService"; + // UpdateService cannot be disabled + Node::json["ServiceEnabled"] = true; + Node::json["FirmwareInventory"] = { + {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - // 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!!!"; + + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + res.jsonValue = Node::json; 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 objPath; - - m.read(objPath, interfaces_properties); // Read in the object path - // that was just created - // std::string str_objpath = objPath.str; // keep a copy for - // constructing response message - BMCWEB_LOG_DEBUG << "obj path = " << objPath.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(objPath.str); // str_objpath); - res.jsonValue = redfish::messages::success(); - BMCWEB_LOG_DEBUG << "ending response"; - res.end(); - fwUpdateMatcher = nullptr; + } + static void activateImage(const std::string &objPath) + { + crow::connections::systemBus->async_method_call( + [objPath](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", objPath, + "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; } - } - }; - - 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!!"; - } -}; + // 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; + } -class SoftwareInventoryCollection : public Node { - public: - template <typename CrowApp> - SoftwareInventoryCollection(CrowApp &app) - : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") { - Node::json["@odata.type"] = - "#SoftwareInventoryCollection.SoftwareInventoryCollection"; - Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; - Node::json["@odata.context"] = - "/redfish/v1/" - "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; - Node::json["Name"] = "Software Inventory Collection"; - - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - res.jsonValue = Node::json; - - crow::connections::systemBus->async_method_call( - [asyncResp]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - if (ec) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = 0; - - for (auto &obj : subtree) { - const std::vector<std::pair<std::string, std::vector<std::string>>> - &connections = obj.second; - - for (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( - [asyncResp](const boost::system::error_code error_code, - const VariantType &activation) { - BMCWEB_LOG_DEBUG << "safe returned in lambda function"; - if (error_code) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } + res.result(boost::beast::http::status::internal_server_error); + res.jsonValue = redfish::messages::internalError(); + res.end(); + }); - const std::string *sw_inv_purpose = - mapbox::getPtr<const std::string>(activation); - if (sw_inv_purpose == nullptr) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - std::size_t last_pos = sw_inv_purpose->rfind("."); - if (last_pos == std::string::npos) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + 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 objPath; + + m.read(objPath, interfaces_properties); // Read in the object path + // that was just created + // std::string str_objpath = objPath.str; // keep a copy for + // constructing response message + BMCWEB_LOG_DEBUG << "obj path = " << objPath.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; } - nlohmann::json &members = - asyncResp->res.jsonValue["Members"]; - members.push_back( - {{"@odata.id", - "/redfish/v1/UpdateService/FirmwareInventory/" + - sw_inv_purpose->substr(last_pos + 1)}}); - asyncResp->res.jsonValue["Members@odata.count"] = - members.size(); - }, - connectionName, obj.first, "org.freedesktop.DBus.Properties", - "Get", "xyz.openbmc_project.Software.Activation", - "Activation"); + UpdateService::activateImage(objPath.str); // str_objpath); + res.jsonValue = redfish::messages::success(); + BMCWEB_LOG_DEBUG << "ending response"; + res.end(); + fwUpdateMatcher = nullptr; + } } - } - }, - "xyz.openbmc_project.ObjectMapper", - "/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"}); - } + }; + + 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 SoftwareInventory : public Node { - public: - template <typename CrowApp> - SoftwareInventory(CrowApp &app) - : Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", - std::string()) { - Node::json["@odata.type"] = "#SoftwareInventory.v1_1_0.SoftwareInventory"; - Node::json["@odata.context"] = - "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; - Node::json["Name"] = "Software Inventory"; - 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"}}}, - {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, - {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; - } - - private: - void doGet(crow::Response &res, const crow::Request &req, - const std::vector<std::string> ¶ms) override { - std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); - res.jsonValue = Node::json; - - if (params.size() != 1) { - res.result(boost::beast::http::status::internal_server_error); - res.jsonValue = messages::internalError(); - res.end(); - return; +class SoftwareInventoryCollection : public Node +{ + public: + template <typename CrowApp> + SoftwareInventoryCollection(CrowApp &app) : + Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") + { + Node::json["@odata.type"] = + "#SoftwareInventoryCollection.SoftwareInventoryCollection"; + Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; + Node::json["@odata.context"] = + "/redfish/v1/" + "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; + Node::json["Name"] = "Software Inventory Collection"; + + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } - std::shared_ptr<std::string> sw_id = - std::make_shared<std::string>(params[0]); + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + res.jsonValue = Node::json; + + crow::connections::systemBus->async_method_call( + [asyncResp]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + if (ec) + { + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + + for (auto &obj : subtree) + { + const std::vector< + std::pair<std::string, std::vector<std::string>>> + &connections = obj.second; + + for (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( + [asyncResp]( + const boost::system::error_code error_code, + const VariantType &activation) { + BMCWEB_LOG_DEBUG + << "safe returned in lambda function"; + if (error_code) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + const std::string *sw_inv_purpose = + mapbox::getPtr<const std::string>( + activation); + if (sw_inv_purpose == nullptr) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + std::size_t last_pos = + sw_inv_purpose->rfind("."); + if (last_pos == std::string::npos) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + nlohmann::json &members = + asyncResp->res.jsonValue["Members"]; + members.push_back( + {{"@odata.id", "/redfish/v1/UpdateService/" + "FirmwareInventory/" + + sw_inv_purpose->substr( + last_pos + 1)}}); + asyncResp->res + .jsonValue["Members@odata.count"] = + members.size(); + }, + connectionName, obj.first, + "org.freedesktop.DBus.Properties", "Get", + "xyz.openbmc_project.Software.Activation", + "Activation"); + } + } + }, + "xyz.openbmc_project.ObjectMapper", + "/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"}); + } +}; - res.jsonValue["@odata.id"] = - "/redfish/v1/UpdateService/FirmwareInventory/" + *sw_id; +class SoftwareInventory : public Node +{ + public: + template <typename CrowApp> + SoftwareInventory(CrowApp &app) : + Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", + std::string()) + { + Node::json["@odata.type"] = + "#SoftwareInventory.v1_1_0.SoftwareInventory"; + Node::json["@odata.context"] = + "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; + Node::json["Name"] = "Software Inventory"; + 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"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } - crow::connections::systemBus->async_method_call( - [asyncResp, sw_id]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>> - &subtree) { - BMCWEB_LOG_DEBUG << "doGet callback..."; - if (ec) { - asyncResp->res.result( - boost::beast::http::status::internal_server_error); + private: + void doGet(crow::Response &res, const crow::Request &req, + const std::vector<std::string> ¶ms) override + { + std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); + res.jsonValue = Node::json; + + if (params.size() != 1) + { + res.result(boost::beast::http::status::internal_server_error); + res.jsonValue = messages::internalError(); + res.end(); return; - } - - for (const std::pair<std::string, - std::vector<std::pair<std::string, - std::vector<std::string>>>> - &obj : subtree) { - if (boost::ends_with(obj.first, *sw_id) != true) { - continue; - } - - if (obj.second.size() <= 1) { - continue; - } + } - crow::connections::systemBus->async_method_call( - [asyncResp, sw_id]( - const boost::system::error_code error_code, - const boost::container::flat_map<std::string, VariantType> - &propertiesList) { - if (error_code) { + std::shared_ptr<std::string> sw_id = + std::make_shared<std::string>(params[0]); + + res.jsonValue["@odata.id"] = + "/redfish/v1/UpdateService/FirmwareInventory/" + *sw_id; + + crow::connections::systemBus->async_method_call( + [asyncResp, sw_id]( + const boost::system::error_code ec, + const std::vector<std::pair< + std::string, std::vector<std::pair< + std::string, std::vector<std::string>>>>> + &subtree) { + BMCWEB_LOG_DEBUG << "doGet callback..."; + if (ec) + { asyncResp->res.result( boost::beast::http::status::internal_server_error); 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\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - const std::string *sw_inv_purpose = - mapbox::getPtr<const std::string>(it->second); - if (sw_inv_purpose == nullptr) { - BMCWEB_LOG_DEBUG << "wrong types for property\"Purpose\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - BMCWEB_LOG_DEBUG << "sw_inv_purpose = " << *sw_inv_purpose; - if (boost::ends_with(*sw_inv_purpose, "." + *sw_id)) { - it = propertiesList.find("Version"); - if (it == propertiesList.end()) { - BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + } + + for (const std::pair< + std::string, + std::vector< + std::pair<std::string, std::vector<std::string>>>> + &obj : subtree) + { + if (boost::ends_with(obj.first, *sw_id) != true) + { + continue; } - const std::string *version = - mapbox::getPtr<const std::string>(it->second); - - if (version != nullptr) { - BMCWEB_LOG_DEBUG << "Can't find property \"Version\"!"; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; + if (obj.second.size() <= 1) + { + continue; } - asyncResp->res.jsonValue["Version"] = *version; - asyncResp->res.jsonValue["Id"] = *sw_id; - } - }, - obj.second[0].first, obj.first, - "org.freedesktop.DBus.Properties", "GetAll", - "xyz.openbmc_project.Software.Version"); - } - }, - "xyz.openbmc_project.ObjectMapper", - "/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"}); - } + + crow::connections::systemBus->async_method_call( + [asyncResp, + sw_id](const boost::system::error_code error_code, + const boost::container::flat_map< + std::string, VariantType> &propertiesList) { + if (error_code) + { + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + 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\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + const std::string *sw_inv_purpose = + mapbox::getPtr<const std::string>(it->second); + if (sw_inv_purpose == nullptr) + { + BMCWEB_LOG_DEBUG + << "wrong types for property\"Purpose\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + BMCWEB_LOG_DEBUG << "sw_inv_purpose = " + << *sw_inv_purpose; + if (boost::ends_with(*sw_inv_purpose, "." + *sw_id)) + { + it = propertiesList.find("Version"); + if (it == propertiesList.end()) + { + BMCWEB_LOG_DEBUG + << "Can't find property \"Version\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + + const std::string *version = + mapbox::getPtr<const std::string>( + it->second); + + if (version != nullptr) + { + BMCWEB_LOG_DEBUG + << "Can't find property \"Version\"!"; + asyncResp->res.result( + boost::beast::http::status:: + internal_server_error); + return; + } + asyncResp->res.jsonValue["Version"] = *version; + asyncResp->res.jsonValue["Id"] = *sw_id; + } + }, + obj.second[0].first, obj.first, + "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Software.Version"); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/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"}); + } }; -} // namespace redfish +} // namespace redfish |