diff options
Diffstat (limited to 'redfish-core/lib')
46 files changed, 12434 insertions, 12022 deletions
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index 05cfbe762d..8bb746eda8 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -1,21 +1,22 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" +#include "boost_formatters.hpp" #include "certificate_service.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" @@ -23,6 +24,7 @@ #include "persistent_data.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" +#include "sessions.hpp" #include "utils/collection.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" @@ -175,10 +177,9 @@ inline bool translateUserGroup(const std::vector<std::string>& userGroups, * * @return true if Account Types mapped to User Groups, false otherwise. */ -inline bool - getUserGroupFromAccountType(crow::Response& res, - const std::vector<std::string>& accountTypes, - std::vector<std::string>& userGroups) +inline bool getUserGroupFromAccountType( + crow::Response& res, const std::vector<std::string>& accountTypes, + std::vector<std::string>& userGroups) { // Need both Redfish and WebUI Account Types to map to 'redfish' User Group bool redfishType = false; @@ -383,24 +384,25 @@ inline void handleRoleMapPatch( crow::connections::systemBus->async_method_call( [asyncResp, roleMapObjData, serverType, index](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"] - [index] = nullptr; - }, + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res + .jsonValue[serverType]["RemoteRoleMapping"][index] = + nullptr; + }, ldapDbusService, roleMapObjData[index].first, "xyz.openbmc_project.Object.Delete", "Delete"); } else { BMCWEB_LOG_ERROR("Can't delete the object"); - messages::propertyValueTypeError(asyncResp->res, "null", - "RemoteRoleMapping/" + - std::to_string(index)); + messages::propertyValueTypeError( + asyncResp->res, "null", + "RemoteRoleMapping/" + std::to_string(index)); return; } } @@ -439,12 +441,21 @@ inline void handleRoleMapPatch( // If "LocalRole" info is provided if (localRole) { + std::string priv = getPrivilegeFromRoleId(*localRole); + if (priv.empty()) + { + messages::propertyValueNotInList( + asyncResp->res, *localRole, + std::format("RemoteRoleMapping/{}/LocalRole", + index)); + return; + } setDbusProperty( asyncResp, std::format("RemoteRoleMapping/{}/LocalRole", index), ldapDbusService, roleMapObjData[index].first, "xyz.openbmc_project.User.PrivilegeMapperEntry", - "Privilege", *localRole); + "Privilege", priv); } } // Create a new RoleMapping Object. @@ -452,8 +463,8 @@ inline void handleRoleMapPatch( { BMCWEB_LOG_DEBUG( "setRoleMappingProperties: Creating new Object"); - std::string pathString = "RemoteRoleMapping/" + - std::to_string(index); + std::string pathString = + "RemoteRoleMapping/" + std::to_string(index); if (!localRole) { @@ -484,20 +495,20 @@ inline void handleRoleMapPatch( crow::connections::systemBus->async_method_call( [asyncResp, serverType, localRole, remoteGroup](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - nlohmann::json& remoteRoleJson = - asyncResp->res - .jsonValue[serverType]["RemoteRoleMapping"]; - nlohmann::json::object_t roleMapEntry; - roleMapEntry["LocalRole"] = *localRole; - roleMapEntry["RemoteGroup"] = *remoteGroup; - remoteRoleJson.emplace_back(std::move(roleMapEntry)); - }, + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + nlohmann::json& remoteRoleJson = + asyncResp->res + .jsonValue[serverType]["RemoteRoleMapping"]; + nlohmann::json::object_t roleMapEntry; + roleMapEntry["LocalRole"] = *localRole; + roleMapEntry["RemoteGroup"] = *remoteGroup; + remoteRoleJson.emplace_back(std::move(roleMapEntry)); + }, ldapDbusService, dbusObjectPath, ldapPrivMapperInterface, "Create", *remoteGroup, getPrivilegeFromRoleId(std::move(*localRole))); @@ -511,8 +522,8 @@ inline void handleRoleMapPatch( * into JSON */ template <typename CallbackFunc> -inline void getLDAPConfigData(const std::string& ldapType, - CallbackFunc&& callback) +inline void + getLDAPConfigData(const std::string& ldapType, CallbackFunc&& callback) { constexpr std::array<std::string_view, 2> interfaces = { ldapEnableInterface, ldapConfigInterface}; @@ -522,156 +533,165 @@ inline void getLDAPConfigData(const std::string& ldapType, [callback = std::forward<CallbackFunc>(callback), ldapType](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& resp) mutable { - if (ec || resp.empty()) - { - BMCWEB_LOG_WARNING( - "DBUS response error during getting of service name: {}", ec); - LDAPConfigData empty{}; - callback(false, empty, ldapType); - return; - } - std::string service = resp.begin()->first; - sdbusplus::message::object_path path(ldapRootObject); - dbus::utility::getManagedObjects( - service, path, - [callback, ldapType]( - const boost::system::error_code& ec2, - const dbus::utility::ManagedObjectType& ldapObjects) mutable { - LDAPConfigData confData{}; - if (ec2) + if (ec || resp.empty()) { - callback(false, confData, ldapType); - BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2); + BMCWEB_LOG_WARNING( + "DBUS response error during getting of service name: {}", + ec); + LDAPConfigData empty{}; + callback(false, empty, ldapType); return; } + std::string service = resp.begin()->first; + sdbusplus::message::object_path path(ldapRootObject); + dbus::utility::getManagedObjects( + service, path, + [callback, ldapType](const boost::system::error_code& ec2, + const dbus::utility::ManagedObjectType& + ldapObjects) mutable { + LDAPConfigData confData{}; + if (ec2) + { + callback(false, confData, ldapType); + BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2); + return; + } - std::string ldapDbusType; - std::string searchString; - - if (ldapType == "LDAP") - { - ldapDbusType = - "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap"; - searchString = "openldap"; - } - else if (ldapType == "ActiveDirectory") - { - ldapDbusType = - "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory"; - searchString = "active_directory"; - } - else - { - BMCWEB_LOG_ERROR("Can't get the DbusType for the given type={}", - ldapType); - callback(false, confData, ldapType); - return; - } - - std::string ldapEnableInterfaceStr = ldapEnableInterface; - std::string ldapConfigInterfaceStr = ldapConfigInterface; + std::string ldapDbusType; + std::string searchString; - for (const auto& object : ldapObjects) - { - // let's find the object whose ldap type is equal to the - // given type - if (object.first.str.find(searchString) == std::string::npos) - { - continue; - } - - for (const auto& interface : object.second) - { - if (interface.first == ldapEnableInterfaceStr) + if (ldapType == "LDAP") { - // rest of the properties are string. - for (const auto& property : interface.second) - { - if (property.first == "Enabled") - { - const bool* value = - std::get_if<bool>(&property.second); - if (value == nullptr) - { - continue; - } - confData.serviceEnabled = *value; - break; - } - } + ldapDbusType = + "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap"; + searchString = "openldap"; } - else if (interface.first == ldapConfigInterfaceStr) + else if (ldapType == "ActiveDirectory") { - for (const auto& property : interface.second) - { - const std::string* strValue = - std::get_if<std::string>(&property.second); - if (strValue == nullptr) - { - continue; - } - if (property.first == "LDAPServerURI") - { - confData.uri = *strValue; - } - else if (property.first == "LDAPBindDN") - { - confData.bindDN = *strValue; - } - else if (property.first == "LDAPBaseDN") - { - confData.baseDN = *strValue; - } - else if (property.first == "LDAPSearchScope") - { - confData.searchScope = *strValue; - } - else if (property.first == "GroupNameAttribute") - { - confData.groupAttribute = *strValue; - } - else if (property.first == "UserNameAttribute") - { - confData.userNameAttribute = *strValue; - } - else if (property.first == "LDAPType") - { - confData.serverType = *strValue; - } - } + ldapDbusType = + "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory"; + searchString = "active_directory"; + } + else + { + BMCWEB_LOG_ERROR( + "Can't get the DbusType for the given type={}", + ldapType); + callback(false, confData, ldapType); + return; } - else if (interface.first == - "xyz.openbmc_project.User.PrivilegeMapperEntry") + + std::string ldapEnableInterfaceStr = ldapEnableInterface; + std::string ldapConfigInterfaceStr = ldapConfigInterface; + + for (const auto& object : ldapObjects) { - LDAPRoleMapData roleMapData{}; - for (const auto& property : interface.second) + // let's find the object whose ldap type is equal to the + // given type + if (object.first.str.find(searchString) == + std::string::npos) { - const std::string* strValue = - std::get_if<std::string>(&property.second); + continue; + } - if (strValue == nullptr) + for (const auto& interface : object.second) + { + if (interface.first == ldapEnableInterfaceStr) { - continue; + // rest of the properties are string. + for (const auto& property : interface.second) + { + if (property.first == "Enabled") + { + const bool* value = + std::get_if<bool>(&property.second); + if (value == nullptr) + { + continue; + } + confData.serviceEnabled = *value; + break; + } + } } - - if (property.first == "GroupName") + else if (interface.first == ldapConfigInterfaceStr) { - roleMapData.groupName = *strValue; + for (const auto& property : interface.second) + { + const std::string* strValue = + std::get_if<std::string>( + &property.second); + if (strValue == nullptr) + { + continue; + } + if (property.first == "LDAPServerURI") + { + confData.uri = *strValue; + } + else if (property.first == "LDAPBindDN") + { + confData.bindDN = *strValue; + } + else if (property.first == "LDAPBaseDN") + { + confData.baseDN = *strValue; + } + else if (property.first == + "LDAPSearchScope") + { + confData.searchScope = *strValue; + } + else if (property.first == + "GroupNameAttribute") + { + confData.groupAttribute = *strValue; + } + else if (property.first == + "UserNameAttribute") + { + confData.userNameAttribute = *strValue; + } + else if (property.first == "LDAPType") + { + confData.serverType = *strValue; + } + } } - else if (property.first == "Privilege") + else if ( + interface.first == + "xyz.openbmc_project.User.PrivilegeMapperEntry") { - roleMapData.privilege = *strValue; + LDAPRoleMapData roleMapData{}; + for (const auto& property : interface.second) + { + const std::string* strValue = + std::get_if<std::string>( + &property.second); + + if (strValue == nullptr) + { + continue; + } + + if (property.first == "GroupName") + { + roleMapData.groupName = *strValue; + } + else if (property.first == "Privilege") + { + roleMapData.privilege = *strValue; + } + } + + confData.groupRoleList.emplace_back( + object.first.str, roleMapData); } } - - confData.groupRoleList.emplace_back(object.first.str, - roleMapData); } - } - } - callback(true, confData, ldapType); + callback(true, confData, ldapType); + }); }); - }); } /** @@ -764,17 +784,17 @@ inline void server(openLDAP/ActiveDirectory) */ -inline void - handleUserNameAttrPatch(const std::string& userNameAttribute, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& ldapServerElementName, - const std::string& ldapConfigObject) +inline void handleUserNameAttrPatch( + const std::string& userNameAttribute, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& ldapServerElementName, + const std::string& ldapConfigObject) { - setDbusProperty(asyncResp, - ldapServerElementName + - "LDAPService/SearchSettings/UsernameAttribute", - ldapDbusService, ldapConfigObject, ldapConfigInterface, - "UserNameAttribute", userNameAttribute); + setDbusProperty( + asyncResp, + ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute", + ldapDbusService, ldapConfigObject, ldapConfigInterface, + "UserNameAttribute", userNameAttribute); } /** * @brief updates the LDAP group attribute and updates the @@ -791,11 +811,11 @@ inline void handleGroupNameAttrPatch( const std::string& ldapServerElementName, const std::string& ldapConfigObject) { - setDbusProperty(asyncResp, - ldapServerElementName + - "/LDAPService/SearchSettings/GroupsAttribute", - ldapDbusService, ldapConfigObject, ldapConfigInterface, - "GroupNameAttribute", groupsAttribute); + setDbusProperty( + asyncResp, + ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute", + ldapDbusService, ldapConfigObject, ldapConfigInterface, + "GroupNameAttribute", groupsAttribute); } /** * @brief updates the LDAP service enable and updates the @@ -991,11 +1011,11 @@ inline void handleLDAPPatch(LdapPatchParams&& input, // Get the existing resource first then keep modifying // whenever any property gets updated. - getLDAPConfigData(serverType, - [asyncResp, input = std::move(input), - dbusObjectPath = std::move(dbusObjectPath)]( - bool success, const LDAPConfigData& confData, - const std::string& serverT) mutable { + getLDAPConfigData(serverType, [asyncResp, input = std::move(input), + dbusObjectPath = std::move(dbusObjectPath)]( + bool success, + const LDAPConfigData& confData, + const std::string& serverT) mutable { if (!success) { messages::internalError(asyncResp->res); @@ -1085,89 +1105,91 @@ inline void updateUserProperties( [dbusObjectPath, username, password, roleId, enabled, locked, accountTypes(std::move(accountTypes)), userSelf, session, asyncResp{std::move(asyncResp)}](int rc) { - if (rc <= 0) - { - messages::resourceNotFound(asyncResp->res, "ManagerAccount", - username); - return; - } - - if (password) - { - int retval = pamUpdatePassword(username, *password); - - if (retval == PAM_USER_UNKNOWN) + if (rc <= 0) { messages::resourceNotFound(asyncResp->res, "ManagerAccount", username); + return; } - else if (retval == PAM_AUTHTOK_ERR) + + if (password) { - // If password is invalid - messages::propertyValueFormatError(asyncResp->res, nullptr, - "Password"); - BMCWEB_LOG_ERROR("pamUpdatePassword Failed"); + int retval = pamUpdatePassword(username, *password); + + if (retval == PAM_USER_UNKNOWN) + { + messages::resourceNotFound(asyncResp->res, "ManagerAccount", + username); + } + else if (retval == PAM_AUTHTOK_ERR) + { + // If password is invalid + messages::propertyValueFormatError(asyncResp->res, nullptr, + "Password"); + BMCWEB_LOG_ERROR("pamUpdatePassword Failed"); + } + else if (retval != PAM_SUCCESS) + { + messages::internalError(asyncResp->res); + return; + } + else + { + // Remove existing sessions of the user when password + // changed + persistent_data::SessionStore::getInstance() + .removeSessionsByUsernameExceptSession(username, + session); + messages::success(asyncResp->res); + } } - else if (retval != PAM_SUCCESS) + + if (enabled) { - messages::internalError(asyncResp->res); - return; + setDbusProperty( + asyncResp, "Enabled", "xyz.openbmc_project.User.Manager", + dbusObjectPath, "xyz.openbmc_project.User.Attributes", + "UserEnabled", *enabled); } - else + + if (roleId) { - // Remove existing sessions of the user when password changed - persistent_data::SessionStore::getInstance() - .removeSessionsByUsernameExceptSession(username, session); - messages::success(asyncResp->res); + std::string priv = getPrivilegeFromRoleId(*roleId); + if (priv.empty()) + { + messages::propertyValueNotInList(asyncResp->res, true, + "Locked"); + return; + } + setDbusProperty( + asyncResp, "RoleId", "xyz.openbmc_project.User.Manager", + dbusObjectPath, "xyz.openbmc_project.User.Attributes", + "UserPrivilege", priv); } - } - if (enabled) - { - setDbusProperty(asyncResp, "Enabled", - "xyz.openbmc_project.User.Manager", dbusObjectPath, - "xyz.openbmc_project.User.Attributes", - "UserEnabled", *enabled); - } - - if (roleId) - { - std::string priv = getPrivilegeFromRoleId(*roleId); - if (priv.empty()) + if (locked) { - messages::propertyValueNotInList(asyncResp->res, true, - "Locked"); - return; + // admin can unlock the account which is locked by + // successive authentication failures but admin should + // not be allowed to lock an account. + if (*locked) + { + messages::propertyValueNotInList(asyncResp->res, "true", + "Locked"); + return; + } + setDbusProperty( + asyncResp, "Locked", "xyz.openbmc_project.User.Manager", + dbusObjectPath, "xyz.openbmc_project.User.Attributes", + "UserLockedForFailedAttempt", *locked); } - setDbusProperty(asyncResp, "RoleId", - "xyz.openbmc_project.User.Manager", dbusObjectPath, - "xyz.openbmc_project.User.Attributes", - "UserPrivilege", priv); - } - if (locked) - { - // admin can unlock the account which is locked by - // successive authentication failures but admin should - // not be allowed to lock an account. - if (*locked) + if (accountTypes) { - messages::propertyValueNotInList(asyncResp->res, "true", - "Locked"); - return; + patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath, + userSelf); } - setDbusProperty(asyncResp, "Locked", - "xyz.openbmc_project.User.Manager", dbusObjectPath, - "xyz.openbmc_project.User.Attributes", - "UserLockedForFailedAttempt", *locked); - } - - if (accountTypes) - { - patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath, - userSelf); - } - }); + }); } inline void handleAccountServiceHead( @@ -1257,6 +1279,45 @@ inline void handleAccountServiceClientCertificatesGet( getClientCertificates(asyncResp, "/Members"_json_pointer); } +using account_service::CertificateMappingAttribute; +using persistent_data::MTLSCommonNameParseMode; +inline CertificateMappingAttribute + getCertificateMapping(MTLSCommonNameParseMode parse) +{ + switch (parse) + { + case MTLSCommonNameParseMode::CommonName: + { + return CertificateMappingAttribute::CommonName; + } + break; + case MTLSCommonNameParseMode::Whole: + { + return CertificateMappingAttribute::Whole; + } + break; + case MTLSCommonNameParseMode::UserPrincipalName: + { + return CertificateMappingAttribute::UserPrincipalName; + } + break; + + case MTLSCommonNameParseMode::Meta: + { + if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING) + { + return CertificateMappingAttribute::CommonName; + } + } + break; + default: + { + return CertificateMappingAttribute::Invalid; + } + break; + } +} + inline void handleAccountServiceGet(App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -1300,9 +1361,21 @@ inline void nlohmann::json::object_t clientCertificate; clientCertificate["Enabled"] = authMethodsConfig.tls; - clientCertificate["RespondToUnauthenticatedClients"] = true; - clientCertificate["CertificateMappingAttribute"] = - account_service::CertificateMappingAttribute::CommonName; + clientCertificate["RespondToUnauthenticatedClients"] = + !authMethodsConfig.tlsStrict; + + using account_service::CertificateMappingAttribute; + + CertificateMappingAttribute mapping = + getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode); + if (mapping == CertificateMappingAttribute::Invalid) + { + messages::internalError(asyncResp->res); + } + else + { + clientCertificate["CertificateMappingAttribute"] = mapping; + } nlohmann::json::object_t certificates; certificates["@odata.id"] = "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates"; @@ -1344,48 +1417,49 @@ inline void "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + messages::internalError(asyncResp->res); + return; + } - BMCWEB_LOG_DEBUG("Got {} properties for AccountService", - propertiesList.size()); + BMCWEB_LOG_DEBUG("Got {} properties for AccountService", + propertiesList.size()); - const uint8_t* minPasswordLength = nullptr; - const uint32_t* accountUnlockTimeout = nullptr; - const uint16_t* maxLoginAttemptBeforeLockout = nullptr; + const uint8_t* minPasswordLength = nullptr; + const uint32_t* accountUnlockTimeout = nullptr; + const uint16_t* maxLoginAttemptBeforeLockout = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, - "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout", - accountUnlockTimeout, "MaxLoginAttemptBeforeLockout", - maxLoginAttemptBeforeLockout); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, + "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout", + accountUnlockTimeout, "MaxLoginAttemptBeforeLockout", + maxLoginAttemptBeforeLockout); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (minPasswordLength != nullptr) - { - asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength; - } + if (minPasswordLength != nullptr) + { + asyncResp->res.jsonValue["MinPasswordLength"] = + *minPasswordLength; + } - if (accountUnlockTimeout != nullptr) - { - asyncResp->res.jsonValue["AccountLockoutDuration"] = - *accountUnlockTimeout; - } + if (accountUnlockTimeout != nullptr) + { + asyncResp->res.jsonValue["AccountLockoutDuration"] = + *accountUnlockTimeout; + } - if (maxLoginAttemptBeforeLockout != nullptr) - { - asyncResp->res.jsonValue["AccountLockoutThreshold"] = - *maxLoginAttemptBeforeLockout; - } - }); + if (maxLoginAttemptBeforeLockout != nullptr) + { + asyncResp->res.jsonValue["AccountLockoutThreshold"] = + *maxLoginAttemptBeforeLockout; + } + }); auto callback = [asyncResp](bool success, const LDAPConfigData& confData, const std::string& ldapType) { @@ -1400,6 +1474,55 @@ inline void getLDAPConfigData("ActiveDirectory", callback); } +inline void handleCertificateMappingAttributePatch( + crow::Response& res, const std::string& certMapAttribute) +{ + MTLSCommonNameParseMode parseMode = + persistent_data::getMTLSCommonNameParseMode(certMapAttribute); + if (parseMode == MTLSCommonNameParseMode::Invalid) + { + messages::propertyValueNotInList(res, "CertificateMappingAttribute", + certMapAttribute); + return; + } + + persistent_data::AuthConfigMethods& authMethodsConfig = + persistent_data::SessionStore::getInstance().getAuthMethodsConfig(); + authMethodsConfig.mTLSCommonNameParsingMode = parseMode; +} + +inline void handleRespondToUnauthenticatedClientsPatch( + App& app, const crow::Request& req, crow::Response& res, + bool respondToUnauthenticatedClients) +{ + if (req.session != nullptr) + { + // Sanity check. If the user isn't currently authenticated with mutual + // TLS, they very likely are about to permanently lock themselves out. + // Make sure they're using mutual TLS before allowing locking. + if (req.session->sessionType != persistent_data::SessionType::MutualTLS) + { + messages::propertyValueExternalConflict( + res, + "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients", + respondToUnauthenticatedClients); + return; + } + } + + persistent_data::AuthConfigMethods& authMethodsConfig = + persistent_data::SessionStore::getInstance().getAuthMethodsConfig(); + + // Change the settings + authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients; + + // Write settings to disk + persistent_data::getConfig().writeData(); + + // Trigger a reload, to apply the new settings to new connections + app.loadCertificate(); +} + inline void handleAccountServicePatch( App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -1413,9 +1536,12 @@ inline void handleAccountServicePatch( std::optional<uint8_t> minPasswordLength; std::optional<uint16_t> maxPasswordLength; LdapPatchParams ldapObject; + std::optional<std::string> certificateMappingAttribute; + std::optional<bool> respondToUnauthenticatedClients; LdapPatchParams activeDirectoryObject; AuthMethods auth; std::optional<std::string> httpBasicAuth; + // clang-format off if (!json_util::readJsonPatch( req, asyncResp->res, @@ -1430,6 +1556,8 @@ inline void handleAccountServicePatch( "ActiveDirectory/RemoteRoleMapping", activeDirectoryObject.remoteRoleMapData, "ActiveDirectory/ServiceAddresses", activeDirectoryObject.serviceAddressList, "ActiveDirectory/ServiceEnabled", activeDirectoryObject.serviceEnabled, + "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute", certificateMappingAttribute, + "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients", respondToUnauthenticatedClients, "LDAP/Authentication/AuthenticationType", ldapObject.authType, "LDAP/Authentication/Password", ldapObject.password, "LDAP/Authentication/Username", ldapObject.userName, @@ -1469,6 +1597,18 @@ inline void handleAccountServicePatch( } } + if (respondToUnauthenticatedClients) + { + handleRespondToUnauthenticatedClientsPatch( + app, req, asyncResp->res, *respondToUnauthenticatedClients); + } + + if (certificateMappingAttribute) + { + handleCertificateMappingAttributePatch(asyncResp->res, + *certificateMappingAttribute); + } + if (minPasswordLength) { setDbusProperty( @@ -1562,47 +1702,49 @@ inline void handleAccountCollectionGet( [asyncResp, thisUser, effectiveUserPrivileges]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& users) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + messages::internalError(asyncResp->res); + return; + } - bool userCanSeeAllAccounts = - effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"}); + bool userCanSeeAllAccounts = + effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"}); - bool userCanSeeSelf = - effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"}); + bool userCanSeeSelf = + effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"}); - nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; - memberArray = nlohmann::json::array(); + nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; + memberArray = nlohmann::json::array(); - for (const auto& userpath : users) - { - std::string user = userpath.first.filename(); - if (user.empty()) + for (const auto& userpath : users) { - messages::internalError(asyncResp->res); - BMCWEB_LOG_ERROR("Invalid firmware ID"); + std::string user = userpath.first.filename(); + if (user.empty()) + { + messages::internalError(asyncResp->res); + BMCWEB_LOG_ERROR("Invalid firmware ID"); - return; - } + return; + } - // As clarified by Redfish here: - // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration - // Users without ConfigureUsers, only see their own - // account. Users with ConfigureUsers, see all - // accounts. - if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf)) - { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/AccountService/Accounts/{}", user); - memberArray.emplace_back(std::move(member)); + // As clarified by Redfish here: + // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration + // Users without ConfigureUsers, only see their own + // account. Users with ConfigureUsers, see all + // accounts. + if (userCanSeeAllAccounts || + (thisUser == user && userCanSeeSelf)) + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/AccountService/Accounts/{}", user); + memberArray.emplace_back(std::move(member)); + } } - } - asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size(); - }); + asyncResp->res.jsonValue["Members@odata.count"] = + memberArray.size(); + }); } inline void processAfterCreateUser( @@ -1628,16 +1770,16 @@ inline void processAfterCreateUser( crow::connections::systemBus->async_method_call( [asyncResp, password](const boost::system::error_code& ec3) { - if (ec3) - { - messages::internalError(asyncResp->res); - return; - } + if (ec3) + { + messages::internalError(asyncResp->res); + return; + } - // If password is invalid - messages::propertyValueFormatError(asyncResp->res, nullptr, - "Password"); - }, + // If password is invalid + messages::propertyValueFormatError(asyncResp->res, nullptr, + "Password"); + }, "xyz.openbmc_project.User.Manager", userPath, "xyz.openbmc_project.Object.Delete", "Delete"); @@ -1721,8 +1863,8 @@ inline void processAfterGetAllGroups( crow::connections::systemBus->async_method_call( [asyncResp, username, password](const boost::system::error_code& ec2, sdbusplus::message_t& m) { - processAfterCreateUser(asyncResp, username, password, ec2, m); - }, + processAfterCreateUser(asyncResp, username, password, ec2, m); + }, "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups, roleId, enabled); @@ -1741,10 +1883,10 @@ inline void handleAccountCollectionPost( std::optional<std::string> roleIdJson; std::optional<bool> enabledJson; std::optional<std::vector<std::string>> accountTypes; - if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username, - "Password", password, "RoleId", roleIdJson, - "Enabled", enabledJson, "AccountTypes", - accountTypes)) + if (!json_util::readJsonPatch( + req, asyncResp->res, "UserName", username, "Password", password, + "RoleId", roleIdJson, "Enabled", enabledJson, "AccountTypes", + accountTypes)) { return; } @@ -1768,22 +1910,22 @@ inline void handleAccountCollectionPost( [asyncResp, username, password{std::move(password)}, roleId, enabled, accountTypes](const boost::system::error_code& ec, const std::vector<std::string>& allGroupsList) { - if (ec) - { - BMCWEB_LOG_DEBUG("ERROR with async_method_call"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("D-Bus response error {}", ec); + messages::internalError(asyncResp->res); + return; + } - if (allGroupsList.empty()) - { - messages::internalError(asyncResp->res); - return; - } + if (allGroupsList.empty()) + { + messages::internalError(asyncResp->res); + return; + } - processAfterGetAllGroups(asyncResp, username, password, roleId, enabled, - accountTypes, allGroupsList); - }); + processAfterGetAllGroups(asyncResp, username, password, roleId, + enabled, accountTypes, allGroupsList); + }); } inline void @@ -1851,138 +1993,140 @@ inline void [asyncResp, accountName](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& users) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - const auto userIt = std::ranges::find_if( - users, - [accountName]( - const std::pair<sdbusplus::message::object_path, - dbus::utility::DBusInterfacesMap>& user) { - return accountName == user.first.filename(); - }); - - if (userIt == users.end()) - { - messages::resourceNotFound(asyncResp->res, "ManagerAccount", - accountName); - return; - } + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + const auto userIt = std::ranges::find_if( + users, + [accountName]( + const std::pair<sdbusplus::message::object_path, + dbus::utility::DBusInterfacesMap>& user) { + return accountName == user.first.filename(); + }); + + if (userIt == users.end()) + { + messages::resourceNotFound(asyncResp->res, "ManagerAccount", + accountName); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#ManagerAccount.v1_7_0.ManagerAccount"; - asyncResp->res.jsonValue["Name"] = "User Account"; - asyncResp->res.jsonValue["Description"] = "User Account"; - asyncResp->res.jsonValue["Password"] = nullptr; - asyncResp->res.jsonValue["StrictAccountTypes"] = true; + asyncResp->res.jsonValue["@odata.type"] = + "#ManagerAccount.v1_7_0.ManagerAccount"; + asyncResp->res.jsonValue["Name"] = "User Account"; + asyncResp->res.jsonValue["Description"] = "User Account"; + asyncResp->res.jsonValue["Password"] = nullptr; + asyncResp->res.jsonValue["StrictAccountTypes"] = true; - for (const auto& interface : userIt->second) - { - if (interface.first == "xyz.openbmc_project.User.Attributes") + for (const auto& interface : userIt->second) { - for (const auto& property : interface.second) + if (interface.first == "xyz.openbmc_project.User.Attributes") { - if (property.first == "UserEnabled") - { - const bool* userEnabled = - std::get_if<bool>(&property.second); - if (userEnabled == nullptr) - { - BMCWEB_LOG_ERROR("UserEnabled wasn't a bool"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["Enabled"] = *userEnabled; - } - else if (property.first == "UserLockedForFailedAttempt") + for (const auto& property : interface.second) { - const bool* userLocked = - std::get_if<bool>(&property.second); - if (userLocked == nullptr) + if (property.first == "UserEnabled") { - BMCWEB_LOG_ERROR("UserLockedForF" - "ailedAttempt " - "wasn't a bool"); - messages::internalError(asyncResp->res); - return; + const bool* userEnabled = + std::get_if<bool>(&property.second); + if (userEnabled == nullptr) + { + BMCWEB_LOG_ERROR("UserEnabled wasn't a bool"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Enabled"] = *userEnabled; } - asyncResp->res.jsonValue["Locked"] = *userLocked; - nlohmann::json::array_t allowed; - // can only unlock accounts - allowed.emplace_back("false"); - asyncResp->res - .jsonValue["Locked@Redfish.AllowableValues"] = - std::move(allowed); - } - else if (property.first == "UserPrivilege") - { - const std::string* userPrivPtr = - std::get_if<std::string>(&property.second); - if (userPrivPtr == nullptr) + else if (property.first == "UserLockedForFailedAttempt") { - BMCWEB_LOG_ERROR("UserPrivilege wasn't a " - "string"); - messages::internalError(asyncResp->res); - return; + const bool* userLocked = + std::get_if<bool>(&property.second); + if (userLocked == nullptr) + { + BMCWEB_LOG_ERROR("UserLockedForF" + "ailedAttempt " + "wasn't a bool"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Locked"] = *userLocked; + nlohmann::json::array_t allowed; + // can only unlock accounts + allowed.emplace_back("false"); + asyncResp->res + .jsonValue["Locked@Redfish.AllowableValues"] = + std::move(allowed); } - std::string role = getRoleIdFromPrivilege(*userPrivPtr); - if (role.empty()) + else if (property.first == "UserPrivilege") { - BMCWEB_LOG_ERROR("Invalid user role"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["RoleId"] = role; + const std::string* userPrivPtr = + std::get_if<std::string>(&property.second); + if (userPrivPtr == nullptr) + { + BMCWEB_LOG_ERROR("UserPrivilege wasn't a " + "string"); + messages::internalError(asyncResp->res); + return; + } + std::string role = + getRoleIdFromPrivilege(*userPrivPtr); + if (role.empty()) + { + BMCWEB_LOG_ERROR("Invalid user role"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["RoleId"] = role; - nlohmann::json& roleEntry = - asyncResp->res.jsonValue["Links"]["Role"]; - roleEntry["@odata.id"] = boost::urls::format( - "/redfish/v1/AccountService/Roles/{}", role); - } - else if (property.first == "UserPasswordExpired") - { - const bool* userPasswordExpired = - std::get_if<bool>(&property.second); - if (userPasswordExpired == nullptr) - { - BMCWEB_LOG_ERROR( - "UserPasswordExpired wasn't a bool"); - messages::internalError(asyncResp->res); - return; + nlohmann::json& roleEntry = + asyncResp->res.jsonValue["Links"]["Role"]; + roleEntry["@odata.id"] = boost::urls::format( + "/redfish/v1/AccountService/Roles/{}", role); } - asyncResp->res.jsonValue["PasswordChangeRequired"] = - *userPasswordExpired; - } - else if (property.first == "UserGroups") - { - const std::vector<std::string>* userGroups = - std::get_if<std::vector<std::string>>( - &property.second); - if (userGroups == nullptr) + else if (property.first == "UserPasswordExpired") { - BMCWEB_LOG_ERROR( - "userGroups wasn't a string vector"); - messages::internalError(asyncResp->res); - return; + const bool* userPasswordExpired = + std::get_if<bool>(&property.second); + if (userPasswordExpired == nullptr) + { + BMCWEB_LOG_ERROR( + "UserPasswordExpired wasn't a bool"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["PasswordChangeRequired"] = + *userPasswordExpired; } - if (!translateUserGroup(*userGroups, asyncResp->res)) + else if (property.first == "UserGroups") { - BMCWEB_LOG_ERROR("userGroups mapping failed"); - messages::internalError(asyncResp->res); - return; + const std::vector<std::string>* userGroups = + std::get_if<std::vector<std::string>>( + &property.second); + if (userGroups == nullptr) + { + BMCWEB_LOG_ERROR( + "userGroups wasn't a string vector"); + messages::internalError(asyncResp->res); + return; + } + if (!translateUserGroup(*userGroups, + asyncResp->res)) + { + BMCWEB_LOG_ERROR("userGroups mapping failed"); + messages::internalError(asyncResp->res); + return; + } } } } } - } - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/AccountService/Accounts/{}", accountName); - asyncResp->res.jsonValue["Id"] = accountName; - asyncResp->res.jsonValue["UserName"] = accountName; - }); + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/AccountService/Accounts/{}", accountName); + asyncResp->res.jsonValue["Id"] = accountName; + asyncResp->res.jsonValue["UserName"] = accountName; + }); } inline void @@ -2007,15 +2151,15 @@ inline void crow::connections::systemBus->async_method_call( [asyncResp, username](const boost::system::error_code& ec) { - if (ec) - { - messages::resourceNotFound(asyncResp->res, "ManagerAccount", - username); - return; - } + if (ec) + { + messages::resourceNotFound(asyncResp->res, "ManagerAccount", + username); + return; + } - messages::accountRemoved(asyncResp->res); - }, + messages::accountRemoved(asyncResp->res); + }, "xyz.openbmc_project.User.Manager", userPath, "xyz.openbmc_project.Object.Delete", "Delete"); } @@ -2099,16 +2243,16 @@ inline void roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)}, locked, userSelf, req, accountTypes(std::move(accountTypes))]( const boost::system::error_code& ec, sdbusplus::message_t& m) { - if (ec) - { - userErrorMessageHandler(m.get_error(), asyncResp, newUser, - username); - return; - } + if (ec) + { + userErrorMessageHandler(m.get_error(), asyncResp, newUser, + username); + return; + } - updateUserProperties(asyncResp, newUser, password, enabled, roleId, - locked, accountTypes, userSelf, req.session); - }, + updateUserProperties(asyncResp, newUser, password, enabled, roleId, + locked, accountTypes, userSelf, req.session); + }, "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager", "RenameUser", username, *newUserName); diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp index 1ec967b6eb..ee922af854 100644 --- a/redfish-core/lib/bios.hpp +++ b/redfish-core/lib/bios.hpp @@ -91,13 +91,13 @@ inline void crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Failed to reset bios: {}", ec); - messages::internalError(asyncResp->res); - return; - } - }, + if (ec) + { + BMCWEB_LOG_ERROR("Failed to reset bios: {}", ec); + messages::internalError(asyncResp->res); + return; + } + }, "org.open_power.Software.Host.Updater", "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.FactoryReset", "Reset"); } diff --git a/redfish-core/lib/cable.hpp b/redfish-core/lib/cable.hpp index bc73267101..27ee276a55 100644 --- a/redfish-core/lib/cable.hpp +++ b/redfish-core/lib/cable.hpp @@ -1,6 +1,7 @@ #pragma once #include "dbus_utility.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/collection.hpp" @@ -23,10 +24,9 @@ namespace redfish * @param[in] ec Error code corresponding to Async method call. * @param[in] properties List of Cable Properties key/value pairs. */ -inline void - fillCableProperties(crow::Response& resp, - const boost::system::error_code& ec, - const dbus::utility::DBusPropertiesMap& properties) +inline void fillCableProperties( + crow::Response& resp, const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& properties) { if (ec) { @@ -97,8 +97,8 @@ inline void [asyncResp]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - fillCableProperties(asyncResp->res, ec, properties); - }); + fillCableProperties(asyncResp->res, ec, properties); + }); } else if (interface == "xyz.openbmc_project.Inventory.Item") { @@ -107,23 +107,24 @@ inline void interface, "Present", [asyncResp, cableObjectPath]( const boost::system::error_code& ec, bool present) { - if (ec) - { - BMCWEB_LOG_DEBUG( - "get presence failed for Cable {} with error {}", - cableObjectPath, ec); - if (ec.value() != EBADR) + if (ec) { - messages::internalError(asyncResp->res); + BMCWEB_LOG_DEBUG( + "get presence failed for Cable {} with error {}", + cableObjectPath, ec); + if (ec.value() != EBADR) + { + messages::internalError(asyncResp->res); + } + return; + } + + if (!present) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; } - return; - } - - if (!present) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + }); } } } @@ -140,52 +141,59 @@ inline void requestRoutesCable(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& cableId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - BMCWEB_LOG_DEBUG("Cable Id: {}", cableId); - constexpr std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.Inventory.Item.Cable"}; - dbus::utility::getSubTree( - "/xyz/openbmc_project/inventory", 0, interfaces, - [asyncResp, - cableId](const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec.value() == EBADR) - { - messages::resourceNotFound(asyncResp->res, "Cable", cableId); - return; - } - - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - for (const auto& [objectPath, serviceMap] : subtree) - { - sdbusplus::message::object_path path(objectPath); - if (path.filename() != cableId) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - continue; + return; } + BMCWEB_LOG_DEBUG("Cable Id: {}", cableId); + constexpr std::array<std::string_view, 1> interfaces = { + "xyz.openbmc_project.Inventory.Item.Cable"}; + dbus::utility::getSubTree( + "/xyz/openbmc_project/inventory", 0, interfaces, + [asyncResp, + cableId](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& + subtree) { + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, "Cable", + cableId); + return; + } - asyncResp->res.jsonValue["@odata.type"] = "#Cable.v1_0_0.Cable"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Cables/{}", cableId); - asyncResp->res.jsonValue["Id"] = cableId; - asyncResp->res.jsonValue["Name"] = "Cable"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } - getCableProperties(asyncResp, objectPath, serviceMap); - return; - } - messages::resourceNotFound(asyncResp->res, "Cable", cableId); - }); - }); + for (const auto& [objectPath, serviceMap] : subtree) + { + sdbusplus::message::object_path path(objectPath); + if (path.filename() != cableId) + { + continue; + } + + asyncResp->res.jsonValue["@odata.type"] = + "#Cable.v1_0_0.Cable"; + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format("/redfish/v1/Cables/{}", + cableId); + asyncResp->res.jsonValue["Id"] = cableId; + asyncResp->res.jsonValue["Name"] = "Cable"; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + + getCableProperties(asyncResp, objectPath, + serviceMap); + return; + } + messages::resourceNotFound(asyncResp->res, "Cable", + cableId); + }); + }); } /** @@ -198,21 +206,22 @@ inline void requestRoutesCableCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#CableCollection.CableCollection"; - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Cables"; - asyncResp->res.jsonValue["Name"] = "Cable Collection"; - asyncResp->res.jsonValue["Description"] = "Collection of Cable Entries"; - constexpr std::array<std::string_view, 1> interfaces{ - "xyz.openbmc_project.Inventory.Item.Cable"}; - collection_util::getCollectionMembers( - asyncResp, boost::urls::url("/redfish/v1/Cables"), interfaces, - "/xyz/openbmc_project/inventory"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#CableCollection.CableCollection"; + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Cables"; + asyncResp->res.jsonValue["Name"] = "Cable Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of Cable Entries"; + constexpr std::array<std::string_view, 1> interfaces{ + "xyz.openbmc_project.Inventory.Item.Cable"}; + collection_util::getCollectionMembers( + asyncResp, boost::urls::url("/redfish/v1/Cables"), + interfaces, "/xyz/openbmc_project/inventory"); + }); } } // namespace redfish diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp index af35450ab7..7ae194cdf2 100644 --- a/redfish-core/lib/certificate_service.hpp +++ b/redfish-core/lib/certificate_service.hpp @@ -106,9 +106,9 @@ class CertificateFile { certDirectory = tempDirectory; certificateFile = certDirectory / "cert.pem"; - std::ofstream out(certificateFile, std::ofstream::out | - std::ofstream::binary | - std::ofstream::trunc); + std::ofstream out(certificateFile, + std::ofstream::out | std::ofstream::binary | + std::ofstream::trunc); out << certString; out.close(); BMCWEB_LOG_DEBUG("Creating certificate file{}", @@ -148,7 +148,7 @@ class CertificateFile * @param[in] type Issuer/Subject * @return None */ -static void updateCertIssuerOrSubject(nlohmann::json& out, +inline void updateCertIssuerOrSubject(nlohmann::json& out, std::string_view value) { // example: O=openbmc-project.xyz,CN=localhost @@ -213,11 +213,10 @@ static void updateCertIssuerOrSubject(nlohmann::json& out, * @param[in] countPtr Json pointer to the count in asyncResp * @return None */ -static void - getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& basePath, - const nlohmann::json::json_pointer& listPtr, - const nlohmann::json::json_pointer& countPtr) +inline void getCertificateList( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& basePath, const nlohmann::json::json_pointer& listPtr, + const nlohmann::json::json_pointer& countPtr) { constexpr std::array<std::string_view, 1> interfaces = { certs::certPropIntf}; @@ -226,55 +225,57 @@ static void [asyncResp, listPtr, countPtr]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& certPaths) { - if (ec) - { - BMCWEB_LOG_ERROR("Certificate collection query failed: {}", ec); - messages::internalError(asyncResp->res); - return; - } - - nlohmann::json& links = asyncResp->res.jsonValue[listPtr]; - links = nlohmann::json::array(); - for (const auto& certPath : certPaths) - { - sdbusplus::message::object_path objPath(certPath); - std::string certId = objPath.filename(); - if (certId.empty()) + if (ec) { - BMCWEB_LOG_ERROR("Invalid certificate objPath {}", certPath); - continue; + BMCWEB_LOG_ERROR("Certificate collection query failed: {}", ec); + messages::internalError(asyncResp->res); + return; } - boost::urls::url certURL; - if (objPath.parent_path() == certs::httpsObjectPath) + nlohmann::json& links = asyncResp->res.jsonValue[listPtr]; + links = nlohmann::json::array(); + for (const auto& certPath : certPaths) { - certURL = boost::urls::format( - "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, certId); + sdbusplus::message::object_path objPath(certPath); + std::string certId = objPath.filename(); + if (certId.empty()) + { + BMCWEB_LOG_ERROR("Invalid certificate objPath {}", + certPath); + continue; + } + + boost::urls::url certURL; + if (objPath.parent_path() == certs::httpsObjectPath) + { + certURL = boost::urls::format( + "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, certId); + } + else if (objPath.parent_path() == certs::ldapObjectPath) + { + certURL = boost::urls::format( + "/redfish/v1/AccountService/LDAP/Certificates/{}", + certId); + } + else if (objPath.parent_path() == certs::authorityObjectPath) + { + certURL = boost::urls::format( + "/redfish/v1/Managers/{}/Truststore/Certificates/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, certId); + } + else + { + continue; + } + + nlohmann::json::object_t link; + link["@odata.id"] = certURL; + links.emplace_back(std::move(link)); } - else if (objPath.parent_path() == certs::ldapObjectPath) - { - certURL = boost::urls::format( - "/redfish/v1/AccountService/LDAP/Certificates/{}", certId); - } - else if (objPath.parent_path() == certs::authorityObjectPath) - { - certURL = boost::urls::format( - "/redfish/v1/Managers/{}/Truststore/Certificates/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, certId); - } - else - { - continue; - } - - nlohmann::json::object_t link; - link["@odata.id"] = certURL; - links.emplace_back(std::move(link)); - } - asyncResp->res.jsonValue[countPtr] = links.size(); - }); + asyncResp->res.jsonValue[countPtr] = links.size(); + }); } /** @@ -288,7 +289,7 @@ static void * @param[in] name name of the certificate * @return None */ -static void getCertificateProperties( +inline void getCertificateProperties( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& objectPath, const std::string& service, const std::string& certId, const boost::urls::url& certURL, @@ -301,82 +302,84 @@ static void getCertificateProperties( [asyncResp, certURL, certId, name](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::resourceNotFound(asyncResp->res, "Certificate", certId); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::resourceNotFound(asyncResp->res, "Certificate", + certId); + return; + } - const std::string* certificateString = nullptr; - const std::vector<std::string>* keyUsage = nullptr; - const std::string* issuer = nullptr; - const std::string* subject = nullptr; - const uint64_t* validNotAfter = nullptr; - const uint64_t* validNotBefore = nullptr; + const std::string* certificateString = nullptr; + const std::vector<std::string>* keyUsage = nullptr; + const std::string* issuer = nullptr; + const std::string* subject = nullptr; + const uint64_t* validNotAfter = nullptr; + const uint64_t* validNotBefore = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "CertificateString", - certificateString, "KeyUsage", keyUsage, "Issuer", issuer, - "Subject", subject, "ValidNotAfter", validNotAfter, - "ValidNotBefore", validNotBefore); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, + "CertificateString", certificateString, "KeyUsage", keyUsage, + "Issuer", issuer, "Subject", subject, "ValidNotAfter", + validNotAfter, "ValidNotBefore", validNotBefore); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["@odata.id"] = certURL; - asyncResp->res.jsonValue["@odata.type"] = - "#Certificate.v1_0_0.Certificate"; - asyncResp->res.jsonValue["Id"] = certId; - asyncResp->res.jsonValue["Name"] = name; - asyncResp->res.jsonValue["Description"] = name; - asyncResp->res.jsonValue["CertificateString"] = ""; - asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array(); + asyncResp->res.jsonValue["@odata.id"] = certURL; + asyncResp->res.jsonValue["@odata.type"] = + "#Certificate.v1_0_0.Certificate"; + asyncResp->res.jsonValue["Id"] = certId; + asyncResp->res.jsonValue["Name"] = name; + asyncResp->res.jsonValue["Description"] = name; + asyncResp->res.jsonValue["CertificateString"] = ""; + asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array(); - if (certificateString != nullptr) - { - asyncResp->res.jsonValue["CertificateString"] = *certificateString; - } + if (certificateString != nullptr) + { + asyncResp->res.jsonValue["CertificateString"] = + *certificateString; + } - if (keyUsage != nullptr) - { - asyncResp->res.jsonValue["KeyUsage"] = *keyUsage; - } + if (keyUsage != nullptr) + { + asyncResp->res.jsonValue["KeyUsage"] = *keyUsage; + } - if (issuer != nullptr) - { - updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"], - *issuer); - } + if (issuer != nullptr) + { + updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"], + *issuer); + } - if (subject != nullptr) - { - updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"], - *subject); - } + if (subject != nullptr) + { + updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"], + *subject); + } - if (validNotAfter != nullptr) - { - asyncResp->res.jsonValue["ValidNotAfter"] = - redfish::time_utils::getDateTimeUint(*validNotAfter); - } + if (validNotAfter != nullptr) + { + asyncResp->res.jsonValue["ValidNotAfter"] = + redfish::time_utils::getDateTimeUint(*validNotAfter); + } - if (validNotBefore != nullptr) - { - asyncResp->res.jsonValue["ValidNotBefore"] = - redfish::time_utils::getDateTimeUint(*validNotBefore); - } + if (validNotBefore != nullptr) + { + asyncResp->res.jsonValue["ValidNotBefore"] = + redfish::time_utils::getDateTimeUint(*validNotBefore); + } - asyncResp->res.addHeader( - boost::beast::http::field::location, - std::string_view(certURL.data(), certURL.size())); - }); + asyncResp->res.addHeader( + boost::beast::http::field::location, + std::string_view(certURL.data(), certURL.size())); + }); } -static void +inline void deleteCertificate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& service, const sdbusplus::message::object_path& objectPath) @@ -384,14 +387,14 @@ static void crow::connections::systemBus->async_method_call( [asyncResp, id{objectPath.filename()}](const boost::system::error_code& ec) { - if (ec) - { - messages::resourceNotFound(asyncResp->res, "Certificate", id); - return; - } - BMCWEB_LOG_INFO("Certificate deleted"); - asyncResp->res.result(boost::beast::http::status::no_content); - }, + if (ec) + { + messages::resourceNotFound(asyncResp->res, "Certificate", id); + return; + } + BMCWEB_LOG_INFO("Certificate deleted"); + asyncResp->res.result(boost::beast::http::status::no_content); + }, service, objectPath, certs::objDeleteIntf, "Delete"); } @@ -462,6 +465,26 @@ inline void handleCertificateLocationsGet( "/Links/Certificates@odata.count"_json_pointer); } +inline void handleError(const std::string_view dbusErrorName, + const std::string& id, const std::string& certificate, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +{ + if (dbusErrorName == "org.freedesktop.DBus.Error.UnknownObject") + { + messages::resourceNotFound(asyncResp->res, "Certificate", id); + } + else if (dbusErrorName == + "xyz.openbmc_project.Certs.Error.InvalidCertificate") + { + messages::propertyValueIncorrect(asyncResp->res, "Certificate", + certificate); + } + else + { + messages::internalError(asyncResp->res); + } +} + inline void handleReplaceCertificateAction( App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -546,24 +569,28 @@ inline void handleReplaceCertificateAction( std::shared_ptr<CertificateFile> certFile = std::make_shared<CertificateFile>(certificate); crow::connections::systemBus->async_method_call( - [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id, - name](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - if (ec.value() == - boost::system::linux_error::bad_request_descriptor) + [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id, name, + certificate](const boost::system::error_code& ec, + sdbusplus::message_t& m) { + if (ec) { - messages::resourceNotFound(asyncResp->res, "Certificate", id); + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + const sd_bus_error* dbusError = m.get_error(); + if ((dbusError != nullptr) && (dbusError->name != nullptr)) + { + handleError(dbusError->name, id, certificate, asyncResp); + } + else + { + messages::internalError(asyncResp->res); + } return; } - messages::internalError(asyncResp->res); - return; - } - getCertificateProperties(asyncResp, objectPath, service, id, url, name); - BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", - certFile->getCertFilePath()); - }, + getCertificateProperties(asyncResp, objectPath, service, id, url, + name); + BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", + certFile->getCertFilePath()); + }, service, objectPath, certs::certReplaceIntf, "Replace", certFile->getCertFilePath()); } @@ -580,7 +607,7 @@ static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher; * @param[in] csrObjPath CSR D-Bus object path * @return None */ -static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +inline void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& certURI, const std::string& service, const std::string& certObjPath, const std::string& csrObjPath) @@ -588,24 +615,24 @@ static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, BMCWEB_LOG_DEBUG("getCSR CertObjectPath{} CSRObjectPath={} service={}", certObjPath, csrObjPath, service); crow::connections::systemBus->async_method_call( - [asyncResp, certURI](const boost::system::error_code& ec, - const std::string& csr) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - if (csr.empty()) - { - BMCWEB_LOG_ERROR("CSR read is empty"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["CSRString"] = csr; - asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] = - certURI; - }, + [asyncResp, + certURI](const boost::system::error_code& ec, const std::string& csr) { + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + if (csr.empty()) + { + BMCWEB_LOG_ERROR("CSR read is empty"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["CSRString"] = csr; + asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] = + certURI; + }, service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR"); } @@ -804,38 +831,38 @@ inline void csrMatcher = std::make_unique<sdbusplus::bus::match_t>( *crow::connections::systemBus, match, [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) { - timeout.cancel(); - if (m.is_method_error()) - { - BMCWEB_LOG_ERROR("Dbus method error!!!"); - messages::internalError(asyncResp->res); - return; - } + timeout.cancel(); + if (m.is_method_error()) + { + BMCWEB_LOG_ERROR("Dbus method error!!!"); + messages::internalError(asyncResp->res); + return; + } - dbus::utility::DBusInterfacesMap interfacesProperties; + dbus::utility::DBusInterfacesMap interfacesProperties; - sdbusplus::message::object_path csrObjectPath; - m.read(csrObjectPath, interfacesProperties); - BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str); - for (const auto& interface : interfacesProperties) - { - if (interface.first == "xyz.openbmc_project.Certs.CSR") + sdbusplus::message::object_path csrObjectPath; + m.read(csrObjectPath, interfacesProperties); + BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str); + for (const auto& interface : interfacesProperties) { - getCSR(asyncResp, certURI, service, objectPath, - csrObjectPath.str); - break; + if (interface.first == "xyz.openbmc_project.Certs.CSR") + { + getCSR(asyncResp, certURI, service, objectPath, + csrObjectPath.str); + break; + } } - } - }); + }); crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec, const std::string&) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message()); - messages::internalError(asyncResp->res); - return; - } - }, + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message()); + messages::internalError(asyncResp->res); + return; + } + }, service, objectPath, "xyz.openbmc_project.Certs.CSR.Create", "GenerateCSR", *optAlternativeNames, *optChallengePassword, city, commonName, *optContactPerson, country, *optEmail, *optGivenName, @@ -937,23 +964,24 @@ inline void handleHTTPSCertificateCollectionPost( crow::connections::systemBus->async_method_call( [asyncResp, certFile](const boost::system::error_code& ec, const std::string& objectPath) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - sdbusplus::message::object_path path(objectPath); - std::string certId = path.filename(); - const boost::urls::url certURL = boost::urls::format( - "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, certId); - getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName, - certId, certURL, "HTTPS Certificate"); - BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", - certFile->getCertFilePath()); - }, + sdbusplus::message::object_path path(objectPath); + std::string certId = path.filename(); + const boost::urls::url certURL = boost::urls::format( + "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, certId); + getCertificateProperties(asyncResp, objectPath, + certs::httpsServiceName, certId, certURL, + "HTTPS Certificate"); + BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", + certFile->getCertFilePath()); + }, certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf, "Install", certFile->getCertFilePath()); } @@ -1051,22 +1079,23 @@ inline void handleLDAPCertificateCollectionPost( crow::connections::systemBus->async_method_call( [asyncResp, certFile](const boost::system::error_code& ec, const std::string& objectPath) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - sdbusplus::message::object_path path(objectPath); - std::string certId = path.filename(); - const boost::urls::url certURL = boost::urls::format( - "/redfish/v1/AccountService/LDAP/Certificates/{}", certId); - getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName, - certId, certURL, "LDAP Certificate"); - BMCWEB_LOG_DEBUG("LDAP certificate install file={}", - certFile->getCertFilePath()); - }, + sdbusplus::message::object_path path(objectPath); + std::string certId = path.filename(); + const boost::urls::url certURL = boost::urls::format( + "/redfish/v1/AccountService/LDAP/Certificates/{}", certId); + getCertificateProperties(asyncResp, objectPath, + certs::ldapServiceName, certId, certURL, + "LDAP Certificate"); + BMCWEB_LOG_DEBUG("LDAP certificate install file={}", + certFile->getCertFilePath()); + }, certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf, "Install", certFile->getCertFilePath()); } @@ -1188,24 +1217,24 @@ inline void handleTrustStoreCertificateCollectionPost( crow::connections::systemBus->async_method_call( [asyncResp, certFile](const boost::system::error_code& ec, const std::string& objectPath) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error: {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - sdbusplus::message::object_path path(objectPath); - std::string certId = path.filename(); - const boost::urls::url certURL = boost::urls::format( - "/redfish/v1/Managers/{}/Truststore/Certificates/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, certId); - getCertificateProperties(asyncResp, objectPath, - certs::authorityServiceName, certId, certURL, - "TrustStore Certificate"); - BMCWEB_LOG_DEBUG("TrustStore certificate install file={}", - certFile->getCertFilePath()); - }, + sdbusplus::message::object_path path(objectPath); + std::string certId = path.filename(); + const boost::urls::url certURL = boost::urls::format( + "/redfish/v1/Managers/{}/Truststore/Certificates/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, certId); + getCertificateProperties(asyncResp, objectPath, + certs::authorityServiceName, certId, + certURL, "TrustStore Certificate"); + BMCWEB_LOG_DEBUG("TrustStore certificate install file={}", + certFile->getCertFilePath()); + }, certs::authorityServiceName, certs::authorityObjectPath, certs::certInstallIntf, "Install", certFile->getCertFilePath()); } diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp index 723cc1e411..a993b016bc 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -1,22 +1,25 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/action_info.hpp" +#include "generated/enums/chassis.hpp" +#include "generated/enums/resource.hpp" #include "led.hpp" #include "query.hpp" #include "redfish_util.hpp" @@ -59,32 +62,32 @@ inline void getStorageLink(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, (path / "storage").str, "xyz.openbmc_project.Association", "endpoints", [asyncResp](const boost::system::error_code& ec, const std::vector<std::string>& storageList) { - if (ec) - { - BMCWEB_LOG_DEBUG("getStorageLink got DBUS response error"); - return; - } - - nlohmann::json::array_t storages; - for (const std::string& storagePath : storageList) - { - std::string id = - sdbusplus::message::object_path(storagePath).filename(); - if (id.empty()) + if (ec) { - continue; + BMCWEB_LOG_DEBUG("getStorageLink got DBUS response error"); + return; } - nlohmann::json::object_t storage; - storage["@odata.id"] = - boost::urls::format("/redfish/v1/Systems/{}/Storage/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, id); - storages.emplace_back(std::move(storage)); - } - asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = - storages.size(); - asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages); - }); + nlohmann::json::array_t storages; + for (const std::string& storagePath : storageList) + { + std::string id = + sdbusplus::message::object_path(storagePath).filename(); + if (id.empty()) + { + continue; + } + + nlohmann::json::object_t storage; + storage["@odata.id"] = + boost::urls::format("/redfish/v1/Systems/{}/Storage/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, id); + storages.emplace_back(std::move(storage)); + } + asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = + storages.size(); + asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages); + }); } /** @@ -103,34 +106,39 @@ inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> asyncResp) "xyz.openbmc_project.State.Chassis", "CurrentPowerState", [asyncResp{std::move(asyncResp)}](const boost::system::error_code& ec, const std::string& chassisState) { - if (ec) - { - if (ec == boost::system::errc::host_unreachable) + if (ec) { - // Service not available, no error, just don't return - // chassis state info - BMCWEB_LOG_DEBUG("Service not available {}", ec); + if (ec == boost::system::errc::host_unreachable) + { + // Service not available, no error, just don't return + // chassis state info + BMCWEB_LOG_DEBUG("Service not available {}", ec); + return; + } + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - BMCWEB_LOG_DEBUG("Chassis state: {}", chassisState); - // Verify Chassis State - if (chassisState == "xyz.openbmc_project.State.Chassis.PowerState.On") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - } - else if (chassisState == - "xyz.openbmc_project.State.Chassis.PowerState.Off") - { - asyncResp->res.jsonValue["PowerState"] = "Off"; - asyncResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; - } - }); + BMCWEB_LOG_DEBUG("Chassis state: {}", chassisState); + // Verify Chassis State + if (chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.On") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + } + else if (chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.Off") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::Off; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::StandbyOffline; + } + }); } /** @@ -162,19 +170,20 @@ inline void handlePhysicalSecurityGetSubTree( "xyz.openbmc_project.Chassis.Intrusion", "Status", [asyncResp](const boost::system::error_code& ec1, const std::string& value) { - if (ec1) - { - // do not add err msg in redfish response, because this is - // not - // mandatory property - BMCWEB_LOG_ERROR("DBUS response error {}", ec1); - return; - } - asyncResp->res - .jsonValue["PhysicalSecurity"]["IntrusionSensorNumber"] = 1; - asyncResp->res - .jsonValue["PhysicalSecurity"]["IntrusionSensor"] = value; - }); + if (ec1) + { + // do not add err msg in redfish response, because this + // is not + // mandatory property + BMCWEB_LOG_ERROR("DBUS response error {}", ec1); + return; + } + asyncResp->res.jsonValue["PhysicalSecurity"] + ["IntrusionSensorNumber"] = 1; + asyncResp->res + .jsonValue["PhysicalSecurity"]["IntrusionSensor"] = + value; + }); return; } @@ -205,7 +214,7 @@ inline void handleChassisCollectionGet( inline void getChassisContainedBy( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& chassisId, const boost::system::error_code& ec, - const dbus::utility::MapperEndPoints& upstreamChassisPaths) + const dbus::utility::MapperGetSubTreePathsResponse& upstreamChassisPaths) { if (ec) { @@ -244,7 +253,7 @@ inline void getChassisContainedBy( inline void getChassisContains( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& chassisId, const boost::system::error_code& ec, - const dbus::utility::MapperEndPoints& downstreamChassisPaths) + const dbus::utility::MapperGetSubTreePathsResponse& downstreamChassisPaths) { if (ec) { @@ -276,27 +285,33 @@ inline void getChassisContains( continue; } nlohmann::json link; - link["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}", - downstreamChassis); + link["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}", downstreamChassis); jValue.push_back(std::move(link)); } asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = jValue.size(); } -inline void - getChassisConnectivity(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& chassisPath) +inline void getChassisConnectivity( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& chassisPath) { BMCWEB_LOG_DEBUG("Get chassis connectivity"); - dbus::utility::getAssociationEndPoints( + constexpr std::array<std::string_view, 2> interfaces{ + "xyz.openbmc_project.Inventory.Item.Board", + "xyz.openbmc_project.Inventory.Item.Chassis"}; + + dbus::utility::getAssociatedSubTreePaths( chassisPath + "/contained_by", + sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, + interfaces, std::bind_front(getChassisContainedBy, asyncResp, chassisId)); - dbus::utility::getAssociationEndPoints( + dbus::utility::getAssociatedSubTreePaths( chassisPath + "/containing", - std::bind_front(getChassisContains, asyncResp, chassisId)); + sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, + interfaces, std::bind_front(getChassisContains, asyncResp, chassisId)); } /** @@ -311,26 +326,26 @@ inline void requestRoutesChassisCollection(App& app) std::bind_front(handleChassisCollectionGet, std::ref(app))); } -inline void - getChassisLocationCode(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& connectionName, - const std::string& path) +inline void getChassisLocationCode( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& connectionName, const std::string& path) { sdbusplus::asio::getProperty<std::string>( *crow::connections::systemBus, connectionName, path, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error for Location"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error for Location"); + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - property; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + property; + }); } inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -342,14 +357,14 @@ inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "xyz.openbmc_project.Common.UUID", "UUID", [asyncResp](const boost::system::error_code& ec, const std::string& chassisUUID) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error for UUID"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["UUID"] = chassisUUID; - }); + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error for UUID"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["UUID"] = chassisUUID; + }); } inline void handleDecoratorAssetProperties( @@ -428,12 +443,12 @@ inline void handleDecoratorAssetProperties( // SensorCollection asyncResp->res.jsonValue["Sensors"]["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId); - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; nlohmann::json::array_t computerSystems; nlohmann::json::object_t system; - system["@odata.id"] = std::format("/redfish/v1/Systems/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + system["@odata.id"] = + std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); computerSystems.emplace_back(std::move(system)); asyncResp->res.jsonValue["Links"]["ComputerSystems"] = std::move(computerSystems); @@ -487,7 +502,8 @@ inline void handleChassisGetSubTree( asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}", chassisId); asyncResp->res.jsonValue["Name"] = "Chassis Collection"; - asyncResp->res.jsonValue["ChassisType"] = "RackMount"; + asyncResp->res.jsonValue["ChassisType"] = + chassis::ChassisType::RackMount; asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] = boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset", chassisId); @@ -499,16 +515,16 @@ inline void handleChassisGetSubTree( path + "/drive", [asyncResp, chassisId](const boost::system::error_code& ec3, const dbus::utility::MapperEndPoints& resp) { - if (ec3 || resp.empty()) - { - return; // no drives = no failures - } + if (ec3 || resp.empty()) + { + return; // no drives = no failures + } - nlohmann::json reference; - reference["@odata.id"] = - boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId); - asyncResp->res.jsonValue["Drives"] = std::move(reference); - }); + nlohmann::json reference; + reference["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}/Drives", chassisId); + asyncResp->res.jsonValue["Drives"] = std::move(reference); + }); const std::string& connectionName = connectionNames[0].first; @@ -533,15 +549,15 @@ inline void handleChassisGetSubTree( assetTagInterface, "AssetTag", [asyncResp, chassisId](const boost::system::error_code& ec2, const std::string& property) { - if (ec2) - { - BMCWEB_LOG_ERROR("DBus response error for AssetTag: {}", - ec2); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["AssetTag"] = property; - }); + if (ec2) + { + BMCWEB_LOG_ERROR( + "DBus response error for AssetTag: {}", ec2); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["AssetTag"] = property; + }); } else if (interface == replaceableInterface) { @@ -550,15 +566,16 @@ inline void handleChassisGetSubTree( replaceableInterface, "HotPluggable", [asyncResp, chassisId](const boost::system::error_code& ec2, const bool property) { - if (ec2) - { - BMCWEB_LOG_ERROR( - "DBus response error for HotPluggable: {}", ec2); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["HotPluggable"] = property; - }); + if (ec2) + { + BMCWEB_LOG_ERROR( + "DBus response error for HotPluggable: {}", + ec2); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["HotPluggable"] = property; + }); } else if (interface == revisionInterface) { @@ -567,15 +584,15 @@ inline void handleChassisGetSubTree( revisionInterface, "Version", [asyncResp, chassisId](const boost::system::error_code& ec2, const std::string& property) { - if (ec2) - { - BMCWEB_LOG_ERROR("DBus response error for Version: {}", - ec2); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["Version"] = property; - }); + if (ec2) + { + BMCWEB_LOG_ERROR( + "DBus response error for Version: {}", ec2); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Version"] = property; + }); } } @@ -595,9 +612,9 @@ inline void handleChassisGetSubTree( [asyncResp, chassisId, path](const boost::system::error_code&, const dbus::utility::DBusPropertiesMap& propertiesList) { - handleDecoratorAssetProperties(asyncResp, chassisId, path, - propertiesList); - }); + handleDecoratorAssetProperties(asyncResp, chassisId, path, + propertiesList); + }); for (const auto& interface : interfaces2) { @@ -691,81 +708,83 @@ inline void [asyncResp, chassisId, locationIndicatorActive, indicatorLed](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - 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; - - sdbusplus::message::object_path objPath(path); - if (objPath.filename() != chassisId) + if (ec) { - continue; + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; } - if (connectionNames.empty()) + // Iterate over all retrieved ObjectPaths. + for (const std::pair<std::string, + std::vector<std::pair< + std::string, std::vector<std::string>>>>& + object : subtree) { - BMCWEB_LOG_ERROR("Got 0 Connection names"); - continue; - } - - const std::vector<std::string>& interfaces3 = - connectionNames[0].second; + const std::string& path = object.first; + const std::vector< + std::pair<std::string, std::vector<std::string>>>& + connectionNames = object.second; - const std::array<const char*, 3> hasIndicatorLed = { - "xyz.openbmc_project.Inventory.Item.Chassis", - "xyz.openbmc_project.Inventory.Item.Panel", - "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; - bool indicatorChassis = false; - for (const char* interface : hasIndicatorLed) - { - if (std::ranges::find(interfaces3, interface) != - interfaces3.end()) + sdbusplus::message::object_path objPath(path); + if (objPath.filename() != chassisId) { - indicatorChassis = true; - break; + continue; } - } - if (locationIndicatorActive) - { - if (indicatorChassis) + + if (connectionNames.empty()) { - setSystemLocationIndicatorActive(asyncResp, - *locationIndicatorActive); + BMCWEB_LOG_ERROR("Got 0 Connection names"); + continue; } - else + + const std::vector<std::string>& interfaces3 = + connectionNames[0].second; + + const std::array<const char*, 3> hasIndicatorLed = { + "xyz.openbmc_project.Inventory.Item.Chassis", + "xyz.openbmc_project.Inventory.Item.Panel", + "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; + bool indicatorChassis = false; + for (const char* interface : hasIndicatorLed) { - messages::propertyUnknown(asyncResp->res, - "LocationIndicatorActive"); + if (std::ranges::find(interfaces3, interface) != + interfaces3.end()) + { + indicatorChassis = true; + break; + } } - } - if (indicatorLed) - { - if (indicatorChassis) + if (locationIndicatorActive) { - setIndicatorLedState(asyncResp, *indicatorLed); + if (indicatorChassis) + { + setSystemLocationIndicatorActive( + asyncResp, *locationIndicatorActive); + } + else + { + messages::propertyUnknown(asyncResp->res, + "LocationIndicatorActive"); + } } - else + if (indicatorLed) { - messages::propertyUnknown(asyncResp->res, "IndicatorLED"); + if (indicatorChassis) + { + setIndicatorLedState(asyncResp, *indicatorLed); + } + else + { + messages::propertyUnknown(asyncResp->res, + "IndicatorLED"); + } } + return; } - return; - } - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - }); + messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); + }); } /** @@ -797,32 +816,34 @@ inline void [asyncResp]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { - if (ec) - { - BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - - const char* processName = "xyz.openbmc_project.State.Chassis"; - const char* interfaceName = "xyz.openbmc_project.State.Chassis"; - const char* destProperty = "RequestedPowerTransition"; - const std::string propertyValue = - "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; - std::string objectPath = "/xyz/openbmc_project/state/chassis_system0"; + if (ec) + { + BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - /* Look for system reset chassis path */ - if ((std::ranges::find(chassisList, objectPath)) == chassisList.end()) - { - /* We prefer to reset the full chassis_system, but if it doesn't - * exist on some platforms, fall back to a host-only power reset - */ - objectPath = "/xyz/openbmc_project/state/chassis0"; - } + const char* processName = "xyz.openbmc_project.State.Chassis"; + const char* interfaceName = "xyz.openbmc_project.State.Chassis"; + const char* destProperty = "RequestedPowerTransition"; + const std::string propertyValue = + "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; + std::string objectPath = + "/xyz/openbmc_project/state/chassis_system0"; + + /* Look for system reset chassis path */ + if ((std::ranges::find(chassisList, objectPath)) == + chassisList.end()) + { + /* We prefer to reset the full chassis_system, but if it doesn't + * exist on some platforms, fall back to a host-only power reset + */ + objectPath = "/xyz/openbmc_project/state/chassis0"; + } - setDbusProperty(asyncResp, "ResetType", processName, objectPath, - interfaceName, destProperty, propertyValue); - }); + setDbusProperty(asyncResp, "ResetType", processName, objectPath, + interfaceName, destProperty, propertyValue); + }); } inline void handleChassisResetActionInfoPost( @@ -888,7 +909,7 @@ inline void handleChassisResetActionInfoGet( nlohmann::json::object_t parameter; parameter["Name"] = "ResetType"; parameter["Required"] = true; - parameter["DataType"] = "String"; + parameter["DataType"] = action_info::ParameterTypes::String; nlohmann::json::array_t allowed; allowed.emplace_back("PowerCycle"); parameter["AllowableValues"] = std::move(allowed); diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp index a3cda9ca38..d8c5ae358e 100644 --- a/redfish-core/lib/ethernet.hpp +++ b/redfish-core/lib/ethernet.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -19,6 +19,8 @@ #include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" +#include "generated/enums/ethernet_interface.hpp" +#include "generated/enums/resource.hpp" #include "human_sort.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -116,7 +118,7 @@ struct EthernetInterfaceData std::string defaultGateway; std::string ipv6DefaultGateway; std::string ipv6StaticDefaultGateway; - std::string macAddress; + std::optional<std::string> macAddress; std::optional<uint32_t> vlanId; std::vector<std::string> nameServers; std::vector<std::string> staticNameServers; @@ -178,9 +180,8 @@ inline std::string getDhcpEnabledEnumeration(bool isIPv4, bool isIPv6) return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.none"; } -inline std::string - translateAddressOriginDbusToRedfish(const std::string& inputOrigin, - bool isIPv4) +inline std::string translateAddressOriginDbusToRedfish( + const std::string& inputOrigin, bool isIPv4) { if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") { @@ -393,8 +394,8 @@ inline bool extractEthernetInterfaceData( sdbusplus::message::object_path path( "/xyz/openbmc_project/network"); - sdbusplus::message::object_path dhcp4Path = path / ethifaceId / - "dhcp4"; + sdbusplus::message::object_path dhcp4Path = + path / ethifaceId / "dhcp4"; if (sdbusplus::message::object_path(objpath.first) == dhcp4Path) { @@ -443,8 +444,8 @@ inline bool extractEthernetInterfaceData( } } - sdbusplus::message::object_path dhcp6Path = path / ethifaceId / - "dhcp6"; + sdbusplus::message::object_path dhcp6Path = + path / ethifaceId / "dhcp6"; if (sdbusplus::message::object_path(objpath.first) == dhcp6Path) { @@ -520,8 +521,8 @@ inline void extractIPV6Data(const std::string& ethifaceId, const dbus::utility::ManagedObjectType& dbusData, std::vector<IPv6AddressData>& ipv6Config) { - const std::string ipPathStart = "/xyz/openbmc_project/network/" + - ethifaceId; + const std::string ipPathStart = + "/xyz/openbmc_project/network/" + ethifaceId; // Since there might be several IPv6 configurations aligned with // single ethernet interface, loop over all of them @@ -534,10 +535,10 @@ inline void extractIPV6Data(const std::string& ethifaceId, { if (interface.first == "xyz.openbmc_project.Network.IP") { - auto type = std::ranges::find_if(interface.second, - [](const auto& property) { - return property.first == "Type"; - }); + auto type = std::ranges::find_if( + interface.second, [](const auto& property) { + return property.first == "Type"; + }); if (type == interface.second.end()) { continue; @@ -612,8 +613,8 @@ inline void extractIPData(const std::string& ethifaceId, const dbus::utility::ManagedObjectType& dbusData, std::vector<IPv4AddressData>& ipv4Config) { - const std::string ipPathStart = "/xyz/openbmc_project/network/" + - ethifaceId; + const std::string ipPathStart = + "/xyz/openbmc_project/network/" + ethifaceId; // Since there might be several IPv4 configurations aligned with // single ethernet interface, loop over all of them @@ -626,10 +627,10 @@ inline void extractIPData(const std::string& ethifaceId, { if (interface.first == "xyz.openbmc_project.Network.IP") { - auto type = std::ranges::find_if(interface.second, - [](const auto& property) { - return property.first == "Type"; - }); + auto type = std::ranges::find_if( + interface.second, [](const auto& property) { + return property.first == "Type"; + }); if (type == interface.second.end()) { continue; @@ -743,11 +744,11 @@ inline void deleteIPAddress(const std::string& ifaceId, { crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - messages::internalError(asyncResp->res); - } - }, + if (ec) + { + messages::internalError(asyncResp->res); + } + }, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network/" + ifaceId + ipHash, "xyz.openbmc_project.Object.Delete", "Delete"); @@ -768,14 +769,14 @@ inline void createIPv4(const std::string& ifaceId, uint8_t prefixLength, const std::string& gateway, const std::string& address, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - auto createIpHandler = [asyncResp, ifaceId, - gateway](const boost::system::error_code& ec) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - }; + auto createIpHandler = + [asyncResp, ifaceId, gateway](const boost::system::error_code& ec) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + }; crow::connections::systemBus->async_method_call( std::move(createIpHandler), "xyz.openbmc_project.Network", @@ -808,24 +809,24 @@ inline void deleteAndCreateIPAddress( crow::connections::systemBus->async_method_call( [asyncResp, version, ifaceId, address, prefixLength, gateway](const boost::system::error_code& ec) { - if (ec) - { - messages::internalError(asyncResp->res); - } - std::string protocol = "xyz.openbmc_project.Network.IP.Protocol."; - protocol += version == IpVersion::IpV4 ? "IPv4" : "IPv6"; - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec2) { - if (ec2) + if (ec) { messages::internalError(asyncResp->res); } + std::string protocol = "xyz.openbmc_project.Network.IP.Protocol."; + protocol += version == IpVersion::IpV4 ? "IPv4" : "IPv6"; + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec2) { + if (ec2) + { + messages::internalError(asyncResp->res); + } + }, + "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId, + "xyz.openbmc_project.Network.IP.Create", "IP", protocol, + address, prefixLength, gateway); }, - "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId, - "xyz.openbmc_project.Network.IP.Create", "IP", protocol, address, - prefixLength, gateway); - }, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network/" + ifaceId + id, "xyz.openbmc_project.Object.Delete", "Delete"); @@ -858,8 +859,7 @@ inline bool extractIPv6DefaultGatewayData( bool success = sdbusplus::unpackPropertiesNoThrow( redfish::dbus_utils::UnpackErrorPrinter(), interface.second, - "Gateway", staticGateway.gateway, "PrefixLength", - staticGateway.prefixLength, "ProtocolType", + "Gateway", staticGateway.gateway, "ProtocolType", staticGateway.protocol); if (!success) { @@ -887,21 +887,21 @@ inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength, sdbusplus::message::object_path path("/xyz/openbmc_project/network"); path /= ifaceId; - auto createIpHandler = [asyncResp, - address](const boost::system::error_code& ec) { - if (ec) - { - if (ec == boost::system::errc::io_error) + auto createIpHandler = + [asyncResp, address](const boost::system::error_code& ec) { + if (ec) { - messages::propertyValueFormatError(asyncResp->res, address, - "Address"); - } - else - { - messages::internalError(asyncResp->res); + if (ec == boost::system::errc::io_error) + { + messages::propertyValueFormatError(asyncResp->res, address, + "Address"); + } + else + { + messages::internalError(asyncResp->res); + } } - } - }; + }; // Passing null for gateway, as per redfish spec IPv6StaticAddresses // object does not have associated gateway property crow::connections::systemBus->async_method_call( @@ -921,18 +921,19 @@ inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength, * @return None */ inline void - deleteIPv6Gateway(std::string_view gatewayId, + deleteIPv6Gateway(std::string_view ifaceId, std::string_view gatewayId, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { sdbusplus::message::object_path path("/xyz/openbmc_project/network"); + path /= ifaceId; path /= gatewayId; crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - messages::internalError(asyncResp->res); - } - }, + if (ec) + { + messages::internalError(asyncResp->res); + } + }, "xyz.openbmc_project.Network", path, "xyz.openbmc_project.Object.Delete", "Delete"); } @@ -941,14 +942,13 @@ inline void * @brief Creates IPv6 static default gateway with given data * * @param[in] ifaceId Id of interface whose IP should be added - * @param[in] prefixLength Prefix length that needs to be added * @param[in] gateway Gateway address that needs to be added * @param[io] asyncResp Response object that will be returned to client * * @return None */ inline void createIPv6DefaultGateway( - std::string_view ifaceId, size_t prefixLength, std::string_view gateway, + std::string_view ifaceId, std::string_view gateway, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { sdbusplus::message::object_path path("/xyz/openbmc_project/network"); @@ -962,7 +962,7 @@ inline void createIPv6DefaultGateway( crow::connections::systemBus->async_method_call( std::move(createIpHandler), "xyz.openbmc_project.Network", path, "xyz.openbmc_project.Network.StaticGateway.Create", "StaticGateway", - gateway, prefixLength, "xyz.openbmc_project.Network.IP.Protocol.IPv6"); + gateway, "xyz.openbmc_project.Network.IP.Protocol.IPv6"); } /** @@ -972,28 +972,27 @@ inline void createIPv6DefaultGateway( * @param[in] ifaceId Id of interface upon which to create the IPv6 * entry * @param[in] gateway IPv6 gateway to assign to this interface - * @param[in] prefixLength IPv6 prefix syntax for the subnet mask * @param[io] asyncResp Response object that will be returned to client * * @return None */ inline void deleteAndCreateIPv6DefaultGateway( std::string_view ifaceId, std::string_view gatewayId, - std::string_view gateway, size_t prefixLength, + std::string_view gateway, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { sdbusplus::message::object_path path("/xyz/openbmc_project/network"); + path /= ifaceId; path /= gatewayId; crow::connections::systemBus->async_method_call( - [asyncResp, ifaceId, gateway, - prefixLength](const boost::system::error_code& ec) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - createIPv6DefaultGateway(ifaceId, prefixLength, gateway, asyncResp); - }, + [asyncResp, ifaceId, gateway](const boost::system::error_code& ec) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + createIPv6DefaultGateway(ifaceId, gateway, asyncResp); + }, "xyz.openbmc_project.Network", path, "xyz.openbmc_project.Object.Delete", "Delete"); } @@ -1032,8 +1031,8 @@ inline void handleIPv6DefaultGateway( } staticGatewayEntry++; } - std::string pathString = "IPv6StaticDefaultGateways/" + - std::to_string(entryIdx); + std::string pathString = + "IPv6StaticDefaultGateways/" + std::to_string(entryIdx); nlohmann::json::object_t* obj = std::get_if<nlohmann::json::object_t>(&thisJson); if (obj == nullptr) @@ -1043,7 +1042,7 @@ inline void handleIPv6DefaultGateway( messages::resourceCannotBeDeleted(asyncResp->res); return; } - deleteIPv6Gateway(staticGatewayEntry->id, asyncResp); + deleteIPv6Gateway(ifaceId, staticGatewayEntry->id, asyncResp); return; } if (obj->empty()) @@ -1057,15 +1056,13 @@ inline void handleIPv6DefaultGateway( } } std::optional<std::string> address; - std::optional<size_t> prefixLength; - if (!json_util::readJsonObject(*obj, asyncResp->res, "Address", address, - "PrefixLength", prefixLength)) + if (!json_util::readJsonObject(*obj, asyncResp->res, "Address", + address)) { return; } const std::string* addr = nullptr; - size_t prefix = 0; if (address) { addr = &(*address); @@ -1079,29 +1076,15 @@ inline void handleIPv6DefaultGateway( messages::propertyMissing(asyncResp->res, pathString + "/Address"); return; } - if (prefixLength) - { - prefix = *prefixLength; - } - else if (staticGatewayEntry != staticGatewayData.end()) - { - prefix = staticGatewayEntry->prefixLength; - } - else - { - messages::propertyMissing(asyncResp->res, - pathString + "/PrefixLength"); - return; - } if (staticGatewayEntry != staticGatewayData.end()) { deleteAndCreateIPv6DefaultGateway(ifaceId, staticGatewayEntry->id, - *addr, prefix, asyncResp); + *addr, asyncResp); staticGatewayEntry++; } else { - createIPv6DefaultGateway(ifaceId, prefix, *addr, asyncResp); + createIPv6DefaultGateway(ifaceId, *addr, asyncResp); } entryIdx++; } @@ -1126,44 +1109,46 @@ void getEthernetIfaceData(const std::string& ethifaceId, callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) mutable { - EthernetInterfaceData ethData{}; - std::vector<IPv4AddressData> ipv4Data; - std::vector<IPv6AddressData> ipv6Data; - std::vector<StaticGatewayData> ipv6GatewayData; + EthernetInterfaceData ethData{}; + std::vector<IPv4AddressData> ipv4Data; + std::vector<IPv6AddressData> ipv6Data; + std::vector<StaticGatewayData> ipv6GatewayData; - if (ec) - { - callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); - return; - } + if (ec) + { + callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); + return; + } - bool found = extractEthernetInterfaceData(ethifaceId, resp, ethData); - if (!found) - { - callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); - return; - } + bool found = + extractEthernetInterfaceData(ethifaceId, resp, ethData); + if (!found) + { + callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); + return; + } - extractIPData(ethifaceId, resp, ipv4Data); - // Fix global GW - for (IPv4AddressData& ipv4 : ipv4Data) - { - if (((ipv4.linktype == LinkType::Global) && - (ipv4.gateway == "0.0.0.0")) || - (ipv4.origin == "DHCP") || (ipv4.origin == "Static")) + extractIPData(ethifaceId, resp, ipv4Data); + // Fix global GW + for (IPv4AddressData& ipv4 : ipv4Data) { - ipv4.gateway = ethData.defaultGateway; + if (((ipv4.linktype == LinkType::Global) && + (ipv4.gateway == "0.0.0.0")) || + (ipv4.origin == "DHCP") || (ipv4.origin == "Static")) + { + ipv4.gateway = ethData.defaultGateway; + } } - } - extractIPV6Data(ethifaceId, resp, ipv6Data); - if (!extractIPv6DefaultGatewayData(ethifaceId, resp, ipv6GatewayData)) - { - callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); - } - // Finally make a callback with useful data - callback(true, ethData, ipv4Data, ipv6Data, ipv6GatewayData); - }); + extractIPV6Data(ethifaceId, resp, ipv6Data); + if (!extractIPv6DefaultGatewayData(ethifaceId, resp, + ipv6GatewayData)) + { + callback(false, ethData, ipv4Data, ipv6Data, ipv6GatewayData); + } + // Finally make a callback with useful data + callback(true, ethData, ipv4Data, ipv6Data, ipv6GatewayData); + }); } /** @@ -1181,44 +1166,44 @@ void getEthernetIfaceList(CallbackFunc&& callback) [callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) { - // Callback requires vector<string> to retrieve all available - // ethernet interfaces - std::vector<std::string> ifaceList; - ifaceList.reserve(resp.size()); - if (ec) - { - callback(false, ifaceList); - return; - } + // Callback requires vector<string> to retrieve all available + // ethernet interfaces + std::vector<std::string> ifaceList; + ifaceList.reserve(resp.size()); + if (ec) + { + callback(false, ifaceList); + return; + } - // Iterate over all retrieved ObjectPaths. - for (const auto& objpath : resp) - { - // And all interfaces available for certain ObjectPath. - for (const auto& interface : objpath.second) + // Iterate over all retrieved ObjectPaths. + for (const auto& objpath : resp) { - // If interface is - // xyz.openbmc_project.Network.EthernetInterface, this is - // what we're looking for. - if (interface.first == - "xyz.openbmc_project.Network.EthernetInterface") + // And all interfaces available for certain ObjectPath. + for (const auto& interface : objpath.second) { - std::string ifaceId = objpath.first.filename(); - if (ifaceId.empty()) + // If interface is + // xyz.openbmc_project.Network.EthernetInterface, this is + // what we're looking for. + if (interface.first == + "xyz.openbmc_project.Network.EthernetInterface") { - continue; + std::string ifaceId = objpath.first.filename(); + if (ifaceId.empty()) + { + continue; + } + // and put it into output vector. + ifaceList.emplace_back(ifaceId); } - // and put it into output vector. - ifaceList.emplace_back(ifaceId); } } - } - std::ranges::sort(ifaceList, AlphanumLess<std::string>()); + std::ranges::sort(ifaceList, AlphanumLess<std::string>()); - // Finally make a callback with useful data - callback(true, ifaceList); - }); + // Finally make a callback with useful data + callback(true, ifaceList); + }); } inline void @@ -1250,10 +1235,9 @@ inline void "MTU", mtuSize); } -inline void - handleDomainnamePatch(const std::string& ifaceId, - const std::string& domainname, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +inline void handleDomainnamePatch( + const std::string& ifaceId, const std::string& domainname, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { std::vector<std::string> vectorDomainname = {domainname}; setDbusProperty( @@ -1323,10 +1307,9 @@ inline void handleFqdnPatch(const std::string& ifaceId, const std::string& fqdn, handleDomainnamePatch(ifaceId, domainname, asyncResp); } -inline void - handleMACAddressPatch(const std::string& ifaceId, - const std::string& macAddress, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +inline void handleMACAddressPatch( + const std::string& ifaceId, const std::string& macAddress, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { setDbusProperty( asyncResp, "MACAddress", "xyz.openbmc_project.Network", @@ -1392,11 +1375,10 @@ inline void handleSLAACAutoConfigPatch( "IPv6AcceptRA", ipv6AutoConfigEnabled); } -inline void handleDHCPPatch(const std::string& ifaceId, - const EthernetInterfaceData& ethData, - const DHCPParameters& v4dhcpParms, - const DHCPParameters& v6dhcpParms, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +inline void handleDHCPPatch( + const std::string& ifaceId, const EthernetInterfaceData& ethData, + const DHCPParameters& v4dhcpParms, const DHCPParameters& v6dhcpParms, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { bool ipv4Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, true); bool ipv6Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, false); @@ -1532,8 +1514,8 @@ inline void handleIPv4StaticPatch( for (std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson : input) { - std::string pathString = "IPv4StaticAddresses/" + - std::to_string(entryIdx); + std::string pathString = + "IPv4StaticAddresses/" + std::to_string(entryIdx); nlohmann::json::object_t* obj = std::get_if<nlohmann::json::object_t>(&thisJson); if (obj == nullptr) @@ -1541,8 +1523,8 @@ inline void handleIPv4StaticPatch( if (nicIpEntry != ipv4Data.cend()) { deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp); - nicIpEntry = getNextStaticIpEntry(++nicIpEntry, - ipv4Data.cend()); + nicIpEntry = + getNextStaticIpEntry(++nicIpEntry, ipv4Data.cend()); if (!preserveGateway && (nicIpEntry == ipv4Data.cend())) { // All entries have been processed, and this last has @@ -1678,8 +1660,8 @@ inline void handleIPv4StaticPatch( deleteAndCreateIPAddress(IpVersion::IpV4, ifaceId, nicIpEntry->id, prefixLength, *address, *gateway, asyncResp); - nicIpEntry = getNextStaticIpEntry(++nicIpEntry, - ipv4Data.cend()); + nicIpEntry = + getNextStaticIpEntry(++nicIpEntry, ipv4Data.cend()); preserveGateway = true; } else @@ -1695,8 +1677,8 @@ inline void handleIPv4StaticPatch( // Received {}, do not modify this address if (nicIpEntry != ipv4Data.cend()) { - nicIpEntry = getNextStaticIpEntry(++nicIpEntry, - ipv4Data.cend()); + nicIpEntry = + getNextStaticIpEntry(++nicIpEntry, ipv4Data.cend()); preserveGateway = true; entryIdx++; } @@ -1737,8 +1719,8 @@ inline void handleIPv6StaticAddressesPatch( for (std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson : input) { - std::string pathString = "IPv6StaticAddresses/" + - std::to_string(entryIdx); + std::string pathString = + "IPv6StaticAddresses/" + std::to_string(entryIdx); nlohmann::json::object_t* obj = std::get_if<nlohmann::json::object_t>(&thisJson); if (obj != nullptr && !obj->empty()) @@ -1786,8 +1768,8 @@ inline void handleIPv6StaticAddressesPatch( deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId, nicIpEntry->id, *prefixLength, *address, "", asyncResp); - nicIpEntry = getNextStaticIpEntry(++nicIpEntry, - ipv6Data.cend()); + nicIpEntry = + getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend()); } else { @@ -1818,8 +1800,8 @@ inline void handleIPv6StaticAddressesPatch( } if (nicIpEntry != ipv6Data.cend()) { - nicIpEntry = getNextStaticIpEntry(++nicIpEntry, - ipv6Data.cend()); + nicIpEntry = + getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend()); } entryIdx++; } @@ -1832,13 +1814,12 @@ inline std::string extractParentInterfaceName(const std::string& ifaceId) return ifaceId.substr(0, pos); } -inline void - parseInterfaceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& ifaceId, - const EthernetInterfaceData& ethData, - const std::vector<IPv4AddressData>& ipv4Data, - const std::vector<IPv6AddressData>& ipv6Data, - const std::vector<StaticGatewayData>& ipv6GatewayData) +inline void parseInterfaceData( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& ifaceId, const EthernetInterfaceData& ethData, + const std::vector<IPv4AddressData>& ipv4Data, + const std::vector<IPv6AddressData>& ipv6Data, + const std::vector<StaticGatewayData>& ipv6GatewayData) { nlohmann::json& jsonResponse = asyncResp->res.jsonValue; jsonResponse["Id"] = ifaceId; @@ -1849,26 +1830,32 @@ inline void if (ethData.nicEnabled) { - jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown"; - jsonResponse["Status"]["State"] = "Enabled"; + jsonResponse["LinkStatus"] = + ethData.linkUp ? ethernet_interface::LinkStatus::LinkUp + : ethernet_interface::LinkStatus::LinkDown; + jsonResponse["Status"]["State"] = resource::State::Enabled; } else { - jsonResponse["LinkStatus"] = "NoLink"; - jsonResponse["Status"]["State"] = "Disabled"; + jsonResponse["LinkStatus"] = ethernet_interface::LinkStatus::NoLink; + jsonResponse["Status"]["State"] = resource::State::Disabled; } jsonResponse["SpeedMbps"] = ethData.speed; jsonResponse["MTUSize"] = ethData.mtuSize; - jsonResponse["MACAddress"] = ethData.macAddress; + if (ethData.macAddress) + { + jsonResponse["MACAddress"] = *ethData.macAddress; + } jsonResponse["DHCPv4"]["DHCPEnabled"] = translateDhcpEnabledToBool(ethData.dhcpEnabled, true); jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpv4Enabled; jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsv4Enabled; jsonResponse["DHCPv4"]["UseDomainName"] = ethData.domainv4Enabled; jsonResponse["DHCPv6"]["OperatingMode"] = - translateDhcpEnabledToBool(ethData.dhcpEnabled, false) ? "Enabled" - : "Disabled"; + translateDhcpEnabledToBool(ethData.dhcpEnabled, false) + ? "Enabled" + : "Disabled"; jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpv6Enabled; jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsv6Enabled; jsonResponse["DHCPv6"]["UseDomainName"] = ethData.domainv6Enabled; @@ -1892,7 +1879,8 @@ inline void if (ethData.vlanId) { - jsonResponse["EthernetInterfaceType"] = "Virtual"; + jsonResponse["EthernetInterfaceType"] = + ethernet_interface::EthernetDeviceType::Virtual; jsonResponse["VLAN"]["VLANEnable"] = true; jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId; jsonResponse["VLAN"]["Tagged"] = true; @@ -1908,7 +1896,8 @@ inline void } else { - jsonResponse["EthernetInterfaceType"] = "Physical"; + jsonResponse["EthernetInterfaceType"] = + ethernet_interface::EthernetDeviceType::Physical; } jsonResponse["NameServers"] = ethData.nameServers; @@ -1952,7 +1941,6 @@ inline void { nlohmann::json::object_t ipv6Gateway; ipv6Gateway["Address"] = ipv6GatewayConfig.gateway; - ipv6Gateway["PrefixLength"] = ipv6GatewayConfig.prefixLength; ipv6StaticGatewayArray.emplace_back(std::move(ipv6Gateway)); } jsonResponse["IPv6StaticDefaultGateways"] = @@ -2016,11 +2004,10 @@ inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, messages::internalError(asyncResp->res); } -inline void afterVlanCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& parentInterfaceUri, - const std::string& vlanInterface, - const boost::system::error_code& ec, - const sdbusplus::message_t& m +inline void afterVlanCreate( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& parentInterfaceUri, const std::string& vlanInterface, + const boost::system::error_code& ec, const sdbusplus::message_t& m ) { @@ -2069,55 +2056,59 @@ inline void requestEthernetInterfacesRoutes(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#EthernetInterfaceCollection.EthernetInterfaceCollection"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["Name"] = - "Ethernet Network Interface Collection"; - asyncResp->res.jsonValue["Description"] = - "Collection of EthernetInterfaces for this Manager"; - - // Get eth interface list, and call the below callback for JSON - // preparation - getEthernetIfaceList( - [asyncResp](const bool& success, - const std::vector<std::string>& ifaceList) { - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + asyncResp->res.jsonValue["@odata.type"] = + "#EthernetInterfaceCollection.EthernetInterfaceCollection"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/EthernetInterfaces", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["Name"] = + "Ethernet Network Interface Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of EthernetInterfaces for this Manager"; + + // Get eth interface list, and call the below callback for JSON + // preparation + getEthernetIfaceList( + [asyncResp](const bool& success, + const std::vector<std::string>& ifaceList) { + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; - ifaceArray = nlohmann::json::array(); - for (const std::string& ifaceItem : ifaceList) - { - nlohmann::json::object_t iface; - iface["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/EthernetInterfaces/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, ifaceItem); - ifaceArray.push_back(std::move(iface)); - } + nlohmann::json& ifaceArray = + asyncResp->res.jsonValue["Members"]; + ifaceArray = nlohmann::json::array(); + for (const std::string& ifaceItem : ifaceList) + { + nlohmann::json::object_t iface; + iface["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/EthernetInterfaces/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, ifaceItem); + ifaceArray.push_back(std::move(iface)); + } - asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/EthernetInterfaces", - BMCWEB_REDFISH_MANAGER_URI_NAME); - }); - }); + asyncResp->res.jsonValue["Members@odata.count"] = + ifaceArray.size(); + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format( + "/redfish/v1/Managers/{}/EthernetInterfaces", + BMCWEB_REDFISH_MANAGER_URI_NAME); + }); + }); BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/") .privileges(redfish::privileges::postEthernetInterfaceCollection) @@ -2125,90 +2116,93 @@ inline void requestEthernetInterfacesRoutes(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - bool vlanEnable = false; - uint32_t vlanId = 0; - std::vector<nlohmann::json::object_t> relatedInterfaces; + bool vlanEnable = false; + uint32_t vlanId = 0; + std::vector<nlohmann::json::object_t> relatedInterfaces; - if (!json_util::readJsonPatch(req, asyncResp->res, "VLAN/VLANEnable", - vlanEnable, "VLAN/VLANId", vlanId, - "Links/RelatedInterfaces", - relatedInterfaces)) - { - return; - } + if (!json_util::readJsonPatch( + req, asyncResp->res, "VLAN/VLANEnable", vlanEnable, + "VLAN/VLANId", vlanId, "Links/RelatedInterfaces", + relatedInterfaces)) + { + return; + } - if (relatedInterfaces.size() != 1) - { - messages::arraySizeTooLong(asyncResp->res, - "Links/RelatedInterfaces", - relatedInterfaces.size()); - return; - } + if (relatedInterfaces.size() != 1) + { + messages::arraySizeTooLong(asyncResp->res, + "Links/RelatedInterfaces", + relatedInterfaces.size()); + return; + } - std::string parentInterfaceUri; - if (!json_util::readJsonObject(relatedInterfaces[0], asyncResp->res, - "@odata.id", parentInterfaceUri)) - { - messages::propertyMissing(asyncResp->res, - "Links/RelatedInterfaces/0/@odata.id"); - return; - } - BMCWEB_LOG_INFO("Parent Interface URI: {}", parentInterfaceUri); + std::string parentInterfaceUri; + if (!json_util::readJsonObject(relatedInterfaces[0], + asyncResp->res, "@odata.id", + parentInterfaceUri)) + { + messages::propertyMissing( + asyncResp->res, "Links/RelatedInterfaces/0/@odata.id"); + return; + } + BMCWEB_LOG_INFO("Parent Interface URI: {}", parentInterfaceUri); - boost::system::result<boost::urls::url_view> parsedUri = - boost::urls::parse_relative_ref(parentInterfaceUri); - if (!parsedUri) - { - messages::propertyValueFormatError( - asyncResp->res, parentInterfaceUri, - "Links/RelatedInterfaces/0/@odata.id"); - return; - } + boost::system::result<boost::urls::url_view> parsedUri = + boost::urls::parse_relative_ref(parentInterfaceUri); + if (!parsedUri) + { + messages::propertyValueFormatError( + asyncResp->res, parentInterfaceUri, + "Links/RelatedInterfaces/0/@odata.id"); + return; + } - std::string parentInterface; - if (!crow::utility::readUrlSegments( - *parsedUri, "redfish", "v1", "Managers", "bmc", - "EthernetInterfaces", std::ref(parentInterface))) - { - messages::propertyValueNotInList( - asyncResp->res, parentInterfaceUri, - "Links/RelatedInterfaces/0/@odata.id"); - return; - } + std::string parentInterface; + if (!crow::utility::readUrlSegments( + *parsedUri, "redfish", "v1", "Managers", "bmc", + "EthernetInterfaces", std::ref(parentInterface))) + { + messages::propertyValueNotInList( + asyncResp->res, parentInterfaceUri, + "Links/RelatedInterfaces/0/@odata.id"); + return; + } - if (!vlanEnable) - { - // In OpenBMC implementation, VLANEnable cannot be false on - // create - messages::propertyValueIncorrect(asyncResp->res, "VLAN/VLANEnable", - "false"); - return; - } + if (!vlanEnable) + { + // In OpenBMC implementation, VLANEnable cannot be false on + // create + messages::propertyValueIncorrect( + asyncResp->res, "VLAN/VLANEnable", "false"); + return; + } - std::string vlanInterface = parentInterface + "_" + - std::to_string(vlanId); - crow::connections::systemBus->async_method_call( - [asyncResp, parentInterfaceUri, - vlanInterface](const boost::system::error_code& ec, - const sdbusplus::message_t& m) { - afterVlanCreate(asyncResp, parentInterfaceUri, vlanInterface, ec, - m); - }, - "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", - "xyz.openbmc_project.Network.VLAN.Create", "VLAN", parentInterface, - vlanId); - }); + std::string vlanInterface = + parentInterface + "_" + std::to_string(vlanId); + crow::connections::systemBus->async_method_call( + [asyncResp, parentInterfaceUri, + vlanInterface](const boost::system::error_code& ec, + const sdbusplus::message_t& m) { + afterVlanCreate(asyncResp, parentInterfaceUri, + vlanInterface, ec, m); + }, + "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network", + "xyz.openbmc_project.Network.VLAN.Create", "VLAN", + parentInterface, vlanId); + }); BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/") .privileges(redfish::privileges::getEthernetInterface) @@ -2216,43 +2210,46 @@ inline void requestEthernetInterfacesRoutes(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId, const std::string& ifaceId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - getEthernetIfaceData( - ifaceId, - [asyncResp, - ifaceId](const bool& success, const EthernetInterfaceData& ethData, - const std::vector<IPv4AddressData>& ipv4Data, - const std::vector<IPv6AddressData>& ipv6Data, - const std::vector<StaticGatewayData>& ipv6GatewayData) { - if (!success) - { - // TODO(Pawel)consider distinguish between non - // existing object, and other errors - messages::resourceNotFound(asyncResp->res, "EthernetInterface", - ifaceId); - return; - } + getEthernetIfaceData( + ifaceId, + [asyncResp, ifaceId]( + const bool& success, + const EthernetInterfaceData& ethData, + const std::vector<IPv4AddressData>& ipv4Data, + const std::vector<IPv6AddressData>& ipv6Data, + const std::vector<StaticGatewayData>& ipv6GatewayData) { + if (!success) + { + // TODO(Pawel)consider distinguish between non + // existing object, and other errors + messages::resourceNotFound( + asyncResp->res, "EthernetInterface", ifaceId); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#EthernetInterface.v1_9_0.EthernetInterface"; - asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; - asyncResp->res.jsonValue["Description"] = - "Management Network Interface"; + asyncResp->res.jsonValue["@odata.type"] = + "#EthernetInterface.v1_9_0.EthernetInterface"; + asyncResp->res.jsonValue["Name"] = + "Manager Ethernet Interface"; + asyncResp->res.jsonValue["Description"] = + "Management Network Interface"; - parseInterfaceData(asyncResp, ifaceId, ethData, ipv4Data, ipv6Data, - ipv6GatewayData); - }); - }); + parseInterfaceData(asyncResp, ifaceId, ethData, + ipv4Data, ipv6Data, ipv6GatewayData); + }); + }); BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/") .privileges(redfish::privileges::patchEthernetInterface) @@ -2260,37 +2257,38 @@ inline void requestEthernetInterfacesRoutes(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId, const std::string& ifaceId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - std::optional<std::string> hostname; - std::optional<std::string> fqdn; - std::optional<std::string> macAddress; - std::optional<std::string> ipv6DefaultGateway; - std::optional< - std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> - ipv4StaticAddresses; - std::optional< - std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> - ipv6StaticAddresses; - std::optional< - std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> - ipv6StaticDefaultGateways; - std::optional<std::vector<std::string>> staticNameServers; - std::optional<bool> ipv6AutoConfigEnabled; - std::optional<bool> interfaceEnabled; - std::optional<size_t> mtuSize; - DHCPParameters v4dhcpParms; - DHCPParameters v6dhcpParms; - // clang-format off + std::optional<std::string> hostname; + std::optional<std::string> fqdn; + std::optional<std::string> macAddress; + std::optional<std::string> ipv6DefaultGateway; + std::optional<std::vector< + std::variant<nlohmann::json::object_t, std::nullptr_t>>> + ipv4StaticAddresses; + std::optional<std::vector< + std::variant<nlohmann::json::object_t, std::nullptr_t>>> + ipv6StaticAddresses; + std::optional<std::vector< + std::variant<nlohmann::json::object_t, std::nullptr_t>>> + ipv6StaticDefaultGateways; + std::optional<std::vector<std::string>> staticNameServers; + std::optional<bool> ipv6AutoConfigEnabled; + std::optional<bool> interfaceEnabled; + std::optional<size_t> mtuSize; + DHCPParameters v4dhcpParms; + DHCPParameters v6dhcpParms; + // clang-format off if (!json_util::readJsonPatch(req, asyncResp->res, "DHCPv4/DHCPEnabled", v4dhcpParms.dhcpv4Enabled, "DHCPv4/UseDNSServers", v4dhcpParms.useDnsServers, @@ -2316,106 +2314,114 @@ inline void requestEthernetInterfacesRoutes(App& app) { return; } - // clang-format on - - // Get single eth interface data, and call the below callback - // for JSON preparation - getEthernetIfaceData( - ifaceId, - [asyncResp, ifaceId, hostname = std::move(hostname), - fqdn = std::move(fqdn), macAddress = std::move(macAddress), - ipv4StaticAddresses = std::move(ipv4StaticAddresses), - ipv6DefaultGateway = std::move(ipv6DefaultGateway), - ipv6StaticAddresses = std::move(ipv6StaticAddresses), - ipv6StaticDefaultGateway = std::move(ipv6StaticDefaultGateways), - staticNameServers = std::move(staticNameServers), mtuSize, - ipv6AutoConfigEnabled, v4dhcpParms = std::move(v4dhcpParms), - v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled]( - const bool success, const EthernetInterfaceData& ethData, - const std::vector<IPv4AddressData>& ipv4Data, - const std::vector<IPv6AddressData>& ipv6Data, - const std::vector<StaticGatewayData>& ipv6GatewayData) mutable { - if (!success) - { - // ... otherwise return error - // TODO(Pawel)consider distinguish between non - // existing object, and other errors - messages::resourceNotFound(asyncResp->res, "EthernetInterface", - ifaceId); - return; - } + // clang-format on + + // Get single eth interface data, and call the below callback + // for JSON preparation + getEthernetIfaceData( + ifaceId, + [asyncResp, ifaceId, hostname = std::move(hostname), + fqdn = std::move(fqdn), macAddress = std::move(macAddress), + ipv4StaticAddresses = std::move(ipv4StaticAddresses), + ipv6DefaultGateway = std::move(ipv6DefaultGateway), + ipv6StaticAddresses = std::move(ipv6StaticAddresses), + ipv6StaticDefaultGateway = + std::move(ipv6StaticDefaultGateways), + staticNameServers = std::move(staticNameServers), mtuSize, + ipv6AutoConfigEnabled, + v4dhcpParms = std::move(v4dhcpParms), + v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled]( + const bool success, + const EthernetInterfaceData& ethData, + const std::vector<IPv4AddressData>& ipv4Data, + const std::vector<IPv6AddressData>& ipv6Data, + const std::vector<StaticGatewayData>& + ipv6GatewayData) mutable { + if (!success) + { + // ... otherwise return error + // TODO(Pawel)consider distinguish between non + // existing object, and other errors + messages::resourceNotFound( + asyncResp->res, "EthernetInterface", ifaceId); + return; + } - handleDHCPPatch(ifaceId, ethData, v4dhcpParms, v6dhcpParms, - asyncResp); + handleDHCPPatch(ifaceId, ethData, v4dhcpParms, + v6dhcpParms, asyncResp); - if (hostname) - { - handleHostnamePatch(*hostname, asyncResp); - } + if (hostname) + { + handleHostnamePatch(*hostname, asyncResp); + } - if (ipv6AutoConfigEnabled) - { - handleSLAACAutoConfigPatch(ifaceId, *ipv6AutoConfigEnabled, - asyncResp); - } + if (ipv6AutoConfigEnabled) + { + handleSLAACAutoConfigPatch( + ifaceId, *ipv6AutoConfigEnabled, asyncResp); + } - if (fqdn) - { - handleFqdnPatch(ifaceId, *fqdn, asyncResp); - } + if (fqdn) + { + handleFqdnPatch(ifaceId, *fqdn, asyncResp); + } - if (macAddress) - { - handleMACAddressPatch(ifaceId, *macAddress, asyncResp); - } + if (macAddress) + { + handleMACAddressPatch(ifaceId, *macAddress, + asyncResp); + } - if (ipv4StaticAddresses) - { - handleIPv4StaticPatch(ifaceId, *ipv4StaticAddresses, ethData, - ipv4Data, asyncResp); - } + if (ipv4StaticAddresses) + { + handleIPv4StaticPatch(ifaceId, *ipv4StaticAddresses, + ethData, ipv4Data, asyncResp); + } - if (staticNameServers) - { - handleStaticNameServersPatch(ifaceId, *staticNameServers, - asyncResp); - } + if (staticNameServers) + { + handleStaticNameServersPatch( + ifaceId, *staticNameServers, asyncResp); + } - if (ipv6DefaultGateway) - { - messages::propertyNotWritable(asyncResp->res, - "IPv6DefaultGateway"); - } + if (ipv6DefaultGateway) + { + messages::propertyNotWritable(asyncResp->res, + "IPv6DefaultGateway"); + } - if (ipv6StaticAddresses) - { - handleIPv6StaticAddressesPatch(ifaceId, *ipv6StaticAddresses, - ipv6Data, asyncResp); - } + if (ipv6StaticAddresses) + { + handleIPv6StaticAddressesPatch(ifaceId, + *ipv6StaticAddresses, + ipv6Data, asyncResp); + } - if (ipv6StaticDefaultGateway) - { - handleIPv6DefaultGateway(ifaceId, *ipv6StaticDefaultGateway, - ipv6GatewayData, asyncResp); - } + if (ipv6StaticDefaultGateway) + { + handleIPv6DefaultGateway( + ifaceId, *ipv6StaticDefaultGateway, + ipv6GatewayData, asyncResp); + } - if (interfaceEnabled) - { - setDbusProperty(asyncResp, "InterfaceEnabled", + if (interfaceEnabled) + { + setDbusProperty( + asyncResp, "InterfaceEnabled", "xyz.openbmc_project.Network", sdbusplus::message::object_path( "/xyz/openbmc_project/network") / ifaceId, "xyz.openbmc_project.Network.EthernetInterface", "NICEnabled", *interfaceEnabled); - } + } - if (mtuSize) - { - handleMTUSizePatch(ifaceId, *mtuSize, asyncResp); - } - }); - }); + if (mtuSize) + { + handleMTUSizePatch(ifaceId, *mtuSize, asyncResp); + } + }); + }); BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/") .privileges(redfish::privileges::deleteEthernetInterface) @@ -2423,26 +2429,27 @@ inline void requestEthernetInterfacesRoutes(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId, const std::string& ifaceId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - crow::connections::systemBus->async_method_call( - [asyncResp, ifaceId](const boost::system::error_code& ec, - const sdbusplus::message_t& m) { - afterDelete(asyncResp, ifaceId, ec, m); - }, - "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "xyz.openbmc_project.Object.Delete", "Delete"); - }); + crow::connections::systemBus->async_method_call( + [asyncResp, ifaceId](const boost::system::error_code& ec, + const sdbusplus::message_t& m) { + afterDelete(asyncResp, ifaceId, ec, m); + }, + "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + ifaceId, + "xyz.openbmc_project.Object.Delete", "Delete"); + }); } } // namespace redfish diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index aebc2824b0..76f3c5a9ed 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -1,21 +1,22 @@ /* -// Copyright (c) 2020 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2020 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "event_service_manager.hpp" +#include "generated/enums/event_service.hpp" #include "http/utility.hpp" #include "logging.hpp" #include "query.hpp" @@ -33,6 +34,7 @@ #include <ranges> #include <span> #include <string> +#include <vector> namespace redfish { @@ -51,118 +53,123 @@ inline void requestRoutesEventService(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/EventService/") .privileges(redfish::privileges::getEventService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + .methods( + boost::beast::http::verb:: + get)([&app]( + const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService"; - asyncResp->res.jsonValue["@odata.type"] = - "#EventService.v1_5_0.EventService"; - asyncResp->res.jsonValue["Id"] = "EventService"; - asyncResp->res.jsonValue["Name"] = "Event Service"; - asyncResp->res.jsonValue["ServerSentEventUri"] = - "/redfish/v1/EventService/SSE"; - - asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] = - "/redfish/v1/EventService/Subscriptions"; - asyncResp->res - .jsonValue["Actions"]["#EventService.SubmitTestEvent"]["target"] = - "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"; - - const persistent_data::EventServiceConfig eventServiceConfig = - persistent_data::EventServiceStore::getInstance() - .getEventServiceConfig(); - - asyncResp->res.jsonValue["Status"]["State"] = - (eventServiceConfig.enabled ? "Enabled" : "Disabled"); - asyncResp->res.jsonValue["ServiceEnabled"] = eventServiceConfig.enabled; - asyncResp->res.jsonValue["DeliveryRetryAttempts"] = - eventServiceConfig.retryAttempts; - asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = - eventServiceConfig.retryTimeoutInterval; - asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes; - asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; - - nlohmann::json::object_t supportedSSEFilters; - supportedSSEFilters["EventFormatType"] = true; - supportedSSEFilters["MessageId"] = true; - supportedSSEFilters["MetricReportDefinition"] = true; - supportedSSEFilters["RegistryPrefix"] = true; - supportedSSEFilters["OriginResource"] = false; - supportedSSEFilters["ResourceType"] = false; - - asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = - std::move(supportedSSEFilters); - }); + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService"; + asyncResp->res.jsonValue["@odata.type"] = + "#EventService.v1_5_0.EventService"; + asyncResp->res.jsonValue["Id"] = "EventService"; + asyncResp->res.jsonValue["Name"] = "Event Service"; + asyncResp->res.jsonValue["ServerSentEventUri"] = + "/redfish/v1/EventService/SSE"; + + asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] = + "/redfish/v1/EventService/Subscriptions"; + asyncResp->res.jsonValue["Actions"]["#EventService.SubmitTestEvent"] + ["target"] = + "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"; + + const persistent_data::EventServiceConfig eventServiceConfig = + persistent_data::EventServiceStore::getInstance() + .getEventServiceConfig(); + + asyncResp->res.jsonValue["Status"]["State"] = + (eventServiceConfig.enabled ? "Enabled" : "Disabled"); + asyncResp->res.jsonValue["ServiceEnabled"] = + eventServiceConfig.enabled; + asyncResp->res.jsonValue["DeliveryRetryAttempts"] = + eventServiceConfig.retryAttempts; + asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = + eventServiceConfig.retryTimeoutInterval; + asyncResp->res.jsonValue["EventFormatTypes"] = + supportedEvtFormatTypes; + asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; + + nlohmann::json::object_t supportedSSEFilters; + supportedSSEFilters["EventFormatType"] = true; + supportedSSEFilters["MessageId"] = true; + supportedSSEFilters["MetricReportDefinition"] = true; + supportedSSEFilters["RegistryPrefix"] = true; + supportedSSEFilters["OriginResource"] = false; + supportedSSEFilters["ResourceType"] = false; + + asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = + std::move(supportedSSEFilters); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/") .privileges(redfish::privileges::patchEventService) .methods(boost::beast::http::verb::patch)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - std::optional<bool> serviceEnabled; - std::optional<uint32_t> retryAttemps; - std::optional<uint32_t> retryInterval; - - if (!json_util::readJsonPatch( - req, asyncResp->res, "ServiceEnabled", serviceEnabled, - "DeliveryRetryAttempts", retryAttemps, - "DeliveryRetryIntervalSeconds", retryInterval)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + std::optional<bool> serviceEnabled; + std::optional<uint32_t> retryAttemps; + std::optional<uint32_t> retryInterval; + + if (!json_util::readJsonPatch( + req, asyncResp->res, "ServiceEnabled", serviceEnabled, + "DeliveryRetryAttempts", retryAttemps, + "DeliveryRetryIntervalSeconds", retryInterval)) + { + return; + } - persistent_data::EventServiceConfig eventServiceConfig = - persistent_data::EventServiceStore::getInstance() - .getEventServiceConfig(); + persistent_data::EventServiceConfig eventServiceConfig = + persistent_data::EventServiceStore::getInstance() + .getEventServiceConfig(); - if (serviceEnabled) - { - eventServiceConfig.enabled = *serviceEnabled; - } + if (serviceEnabled) + { + eventServiceConfig.enabled = *serviceEnabled; + } - if (retryAttemps) - { - // Supported range [1-3] - if ((*retryAttemps < 1) || (*retryAttemps > 3)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryAttemps), - "DeliveryRetryAttempts", "[1-3]"); - } - else - { - eventServiceConfig.retryAttempts = *retryAttemps; - } - } + if (retryAttemps) + { + // Supported range [1-3] + if ((*retryAttemps < 1) || (*retryAttemps > 3)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryAttemps), + "DeliveryRetryAttempts", "[1-3]"); + } + else + { + eventServiceConfig.retryAttempts = *retryAttemps; + } + } - if (retryInterval) - { - // Supported range [5 - 180] - if ((*retryInterval < 5) || (*retryInterval > 180)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryInterval), - "DeliveryRetryIntervalSeconds", "[5-180]"); - } - else - { - eventServiceConfig.retryTimeoutInterval = *retryInterval; - } - } + if (retryInterval) + { + // Supported range [5 - 180] + if ((*retryInterval < 5) || (*retryInterval > 180)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryInterval), + "DeliveryRetryIntervalSeconds", "[5-180]"); + } + else + { + eventServiceConfig.retryTimeoutInterval = + *retryInterval; + } + } - EventServiceManager::getInstance().setEventServiceConfig( - eventServiceConfig); - }); + EventServiceManager::getInstance().setEventServiceConfig( + eventServiceConfig); + }); } inline void requestRoutesSubmitTestEvent(App& app) @@ -173,18 +180,18 @@ inline void requestRoutesSubmitTestEvent(App& app) .methods(boost::beast::http::verb::post)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (!EventServiceManager::getInstance().sendTestEventLog()) - { - messages::serviceDisabled(asyncResp->res, - "/redfish/v1/EventService/"); - return; - } - asyncResp->res.result(boost::beast::http::status::no_content); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (!EventServiceManager::getInstance().sendTestEventLog()) + { + messages::serviceDisabled(asyncResp->res, + "/redfish/v1/EventService/"); + return; + } + asyncResp->res.result(boost::beast::http::status::no_content); + }); } inline void doSubscriptionCollection( @@ -227,402 +234,424 @@ inline void requestRoutesEventDestinationCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#EventDestinationCollection.EventDestinationCollection"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/EventService/Subscriptions"; - asyncResp->res.jsonValue["Name"] = "Event Destination Collections"; - - nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; - - std::vector<std::string> subscripIds = - EventServiceManager::getInstance().getAllIDs(); - memberArray = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size(); - - for (const std::string& id : subscripIds) - { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/EventService/Subscriptions/{}" + id); - memberArray.emplace_back(std::move(member)); - } - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& resp) { - doSubscriptionCollection(ec, asyncResp, resp); - }, - "xyz.openbmc_project.Network.SNMP", - "/xyz/openbmc_project/network/snmp/manager", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#EventDestinationCollection.EventDestinationCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/EventService/Subscriptions"; + asyncResp->res.jsonValue["Name"] = + "Event Destination Collections"; + + nlohmann::json& memberArray = + asyncResp->res.jsonValue["Members"]; + + std::vector<std::string> subscripIds = + EventServiceManager::getInstance().getAllIDs(); + memberArray = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = + subscripIds.size(); + + for (const std::string& id : subscripIds) + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}" + id); + memberArray.emplace_back(std::move(member)); + } + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& resp) { + doSubscriptionCollection(ec, asyncResp, resp); + }, + "xyz.openbmc_project.Network.SNMP", + "/xyz/openbmc_project/network/snmp/manager", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") .privileges(redfish::privileges::postEventDestinationCollection) - .methods(boost::beast::http::verb::post)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= - maxNoOfSubscriptions) - { - messages::eventSubscriptionLimitExceeded(asyncResp->res); - return; - } - std::string destUrl; - std::string protocol; - std::optional<std::string> context; - std::optional<std::string> subscriptionType; - std::optional<std::string> eventFormatType2; - std::optional<std::string> retryPolicy; - std::optional<std::vector<std::string>> msgIds; - std::optional<std::vector<std::string>> regPrefixes; - std::optional<std::vector<std::string>> resTypes; - std::optional<std::vector<nlohmann::json::object_t>> headers; - std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray; - - if (!json_util::readJsonPatch( - req, asyncResp->res, "Destination", destUrl, "Context", context, - "Protocol", protocol, "SubscriptionType", subscriptionType, - "EventFormatType", eventFormatType2, "HttpHeaders", headers, - "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, - "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", - mrdJsonArray, "ResourceTypes", resTypes)) - { - return; - } - - // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers - static constexpr const uint16_t maxDestinationSize = 2000; - if (destUrl.size() > maxDestinationSize) - { - messages::stringValueTooLong(asyncResp->res, "Destination", - maxDestinationSize); - return; - } - - if (regPrefixes && msgIds) - { - if (!regPrefixes->empty() && !msgIds->empty()) + .methods( + boost::beast::http::verb:: + post)([&app]( + const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - messages::propertyValueConflict(asyncResp->res, "MessageIds", - "RegistryPrefixes"); return; } - } - - boost::system::result<boost::urls::url> url = - boost::urls::parse_absolute_uri(destUrl); - if (!url) - { - BMCWEB_LOG_WARNING("Failed to validate and split destination url"); - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; - } - url->normalize(); - crow::utility::setProtocolDefaults(*url, protocol); - crow::utility::setPortDefaults(*url); - - if (url->path().empty()) - { - url->set_path("/"); - } - - if (url->has_userinfo()) - { - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; - } - - if (protocol == "SNMPv2c") - { - if (context) + if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= + maxNoOfSubscriptions) { - messages::propertyValueConflict(asyncResp->res, "Context", - "Protocol"); + messages::eventSubscriptionLimitExceeded(asyncResp->res); return; } - if (eventFormatType2) + std::string destUrl; + std::string protocol; + std::optional<bool> verifyCertificate; + std::optional<std::string> context; + std::optional<std::string> subscriptionType; + std::optional<std::string> eventFormatType2; + std::optional<std::string> retryPolicy; + std::optional<std::vector<std::string>> msgIds; + std::optional<std::vector<std::string>> regPrefixes; + std::optional<std::vector<std::string>> originResources; + std::optional<std::vector<std::string>> resTypes; + std::optional<std::vector<nlohmann::json::object_t>> headers; + std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray; + + if (!json_util::readJsonPatch( + req, asyncResp->res, "Destination", destUrl, "Context", + context, "Protocol", protocol, "SubscriptionType", + subscriptionType, "EventFormatType", eventFormatType2, + "HttpHeaders", headers, "RegistryPrefixes", regPrefixes, + "OriginResources", originResources, "MessageIds", msgIds, + "DeliveryRetryPolicy", retryPolicy, + "MetricReportDefinitions", mrdJsonArray, "ResourceTypes", + resTypes, "VerifyCertificate", verifyCertificate)) { - messages::propertyValueConflict(asyncResp->res, - "EventFormatType", "Protocol"); return; } - if (retryPolicy) - { - messages::propertyValueConflict(asyncResp->res, "RetryPolicy", - "Protocol"); - return; - } - if (msgIds) + + // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + static constexpr const uint16_t maxDestinationSize = 2000; + if (destUrl.size() > maxDestinationSize) { - messages::propertyValueConflict(asyncResp->res, "MessageIds", - "Protocol"); + messages::stringValueTooLong(asyncResp->res, "Destination", + maxDestinationSize); return; } - if (regPrefixes) + + if (regPrefixes && msgIds) { - messages::propertyValueConflict(asyncResp->res, - "RegistryPrefixes", "Protocol"); - return; + if (!regPrefixes->empty() && !msgIds->empty()) + { + messages::propertyValueConflict( + asyncResp->res, "MessageIds", "RegistryPrefixes"); + return; + } } - if (resTypes) + + boost::system::result<boost::urls::url> url = + boost::urls::parse_absolute_uri(destUrl); + if (!url) { - messages::propertyValueConflict(asyncResp->res, "ResourceTypes", - "Protocol"); + BMCWEB_LOG_WARNING( + "Failed to validate and split destination url"); + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); return; } - if (headers) + url->normalize(); + crow::utility::setProtocolDefaults(*url, protocol); + crow::utility::setPortDefaults(*url); + + if (url->path().empty()) { - messages::propertyValueConflict(asyncResp->res, "HttpHeaders", - "Protocol"); - return; + url->set_path("/"); } - if (mrdJsonArray) + + if (url->has_userinfo()) { - messages::propertyValueConflict( - asyncResp->res, "MetricReportDefinitions", "Protocol"); + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); return; } - if (url->scheme() != "snmp") + + if (protocol == "SNMPv2c") { - messages::propertyValueConflict(asyncResp->res, "Destination", - "Protocol"); + if (context) + { + messages::propertyValueConflict(asyncResp->res, "Context", + "Protocol"); + return; + } + if (eventFormatType2) + { + messages::propertyValueConflict( + asyncResp->res, "EventFormatType", "Protocol"); + return; + } + if (retryPolicy) + { + messages::propertyValueConflict(asyncResp->res, + "RetryPolicy", "Protocol"); + return; + } + if (msgIds) + { + messages::propertyValueConflict(asyncResp->res, + "MessageIds", "Protocol"); + return; + } + if (regPrefixes) + { + messages::propertyValueConflict( + asyncResp->res, "RegistryPrefixes", "Protocol"); + return; + } + if (resTypes) + { + messages::propertyValueConflict( + asyncResp->res, "ResourceTypes", "Protocol"); + return; + } + if (headers) + { + messages::propertyValueConflict(asyncResp->res, + "HttpHeaders", "Protocol"); + return; + } + if (mrdJsonArray) + { + messages::propertyValueConflict( + asyncResp->res, "MetricReportDefinitions", "Protocol"); + return; + } + if (url->scheme() != "snmp") + { + messages::propertyValueConflict(asyncResp->res, + "Destination", "Protocol"); + return; + } + + addSnmpTrapClient(asyncResp, url->host_address(), + url->port_number()); return; } - addSnmpTrapClient(asyncResp, url->host_address(), - url->port_number()); - return; - } - - std::shared_ptr<Subscription> subValue = - std::make_shared<Subscription>(*url, app.ioContext()); + std::shared_ptr<Subscription> subValue = + std::make_shared<Subscription>(*url, app.ioContext()); - subValue->destinationUrl = std::move(*url); + subValue->destinationUrl = std::move(*url); - if (subscriptionType) - { - if (*subscriptionType != "RedfishEvent") + if (subscriptionType) { - messages::propertyValueNotInList( - asyncResp->res, *subscriptionType, "SubscriptionType"); - return; + if (*subscriptionType != "RedfishEvent") + { + messages::propertyValueNotInList( + asyncResp->res, *subscriptionType, "SubscriptionType"); + return; + } + subValue->subscriptionType = *subscriptionType; } - subValue->subscriptionType = *subscriptionType; - } - else - { - subValue->subscriptionType = "RedfishEvent"; // Default - } - - if (protocol != "Redfish") - { - messages::propertyValueNotInList(asyncResp->res, protocol, - "Protocol"); - return; - } - subValue->protocol = protocol; - - if (eventFormatType2) - { - if (std::ranges::find(supportedEvtFormatTypes, *eventFormatType2) == - supportedEvtFormatTypes.end()) + else { - messages::propertyValueNotInList( - asyncResp->res, *eventFormatType2, "EventFormatType"); - return; + subValue->subscriptionType = "RedfishEvent"; // Default } - subValue->eventFormatType = *eventFormatType2; - } - else - { - // If not specified, use default "Event" - subValue->eventFormatType = "Event"; - } - if (context) - { - // This value is selected arbitrarily. - constexpr const size_t maxContextSize = 256; - if (context->size() > maxContextSize) + if (protocol != "Redfish") { - messages::stringValueTooLong(asyncResp->res, "Context", - maxContextSize); + messages::propertyValueNotInList(asyncResp->res, protocol, + "Protocol"); return; } - subValue->customText = *context; - } + subValue->protocol = protocol; - if (headers) - { - size_t cumulativeLen = 0; + if (verifyCertificate) + { + subValue->verifyCertificate = *verifyCertificate; + } - for (const nlohmann::json::object_t& headerChunk : *headers) + if (eventFormatType2) { - for (const auto& item : headerChunk) + if (std::ranges::find(supportedEvtFormatTypes, + *eventFormatType2) == + supportedEvtFormatTypes.end()) { - const std::string* value = - item.second.get_ptr<const std::string*>(); - if (value == nullptr) - { - messages::propertyValueFormatError( - asyncResp->res, item.second, - "HttpHeaders/" + item.first); - return; - } - // Adding a new json value is the size of the key, + - // the size of the value + 2 * 2 quotes for each, + - // the colon and space between. example: - // "key": "value" - cumulativeLen += item.first.size() + value->size() + 6; - // This value is selected to mirror http_connection.hpp - constexpr const uint16_t maxHeaderSizeED = 8096; - if (cumulativeLen > maxHeaderSizeED) - { - messages::arraySizeTooLong( - asyncResp->res, "HttpHeaders", maxHeaderSizeED); - return; - } - subValue->httpHeaders.set(item.first, *value); + messages::propertyValueNotInList( + asyncResp->res, *eventFormatType2, "EventFormatType"); + return; } + subValue->eventFormatType = *eventFormatType2; + } + else + { + // If not specified, use default "Event" + subValue->eventFormatType = "Event"; } - } - if (regPrefixes) - { - for (const std::string& it : *regPrefixes) + if (context) { - if (std::ranges::find(supportedRegPrefixes, it) == - supportedRegPrefixes.end()) + // This value is selected arbitrarily. + constexpr const size_t maxContextSize = 256; + if (context->size() > maxContextSize) { - messages::propertyValueNotInList(asyncResp->res, it, - "RegistryPrefixes"); + messages::stringValueTooLong(asyncResp->res, "Context", + maxContextSize); return; } + subValue->customText = *context; } - subValue->registryPrefixes = *regPrefixes; - } - if (resTypes) - { - for (const std::string& it : *resTypes) + if (headers) { - if (std::ranges::find(supportedResourceTypes, it) == - supportedResourceTypes.end()) + size_t cumulativeLen = 0; + + for (const nlohmann::json::object_t& headerChunk : *headers) { - messages::propertyValueNotInList(asyncResp->res, it, - "ResourceTypes"); - return; + for (const auto& item : headerChunk) + { + const std::string* value = + item.second.get_ptr<const std::string*>(); + if (value == nullptr) + { + messages::propertyValueFormatError( + asyncResp->res, item.second, + "HttpHeaders/" + item.first); + return; + } + // Adding a new json value is the size of the key, + + // the size of the value + 2 * 2 quotes for each, + + // the colon and space between. example: + // "key": "value" + cumulativeLen += item.first.size() + value->size() + 6; + // This value is selected to mirror http_connection.hpp + constexpr const uint16_t maxHeaderSizeED = 8096; + if (cumulativeLen > maxHeaderSizeED) + { + messages::arraySizeTooLong( + asyncResp->res, "HttpHeaders", maxHeaderSizeED); + return; + } + subValue->httpHeaders.set(item.first, *value); + } } } - subValue->resourceTypes = *resTypes; - } - if (msgIds) - { - std::vector<std::string> registryPrefix; + if (regPrefixes) + { + for (const std::string& it : *regPrefixes) + { + if (std::ranges::find(supportedRegPrefixes, it) == + supportedRegPrefixes.end()) + { + messages::propertyValueNotInList(asyncResp->res, it, + "RegistryPrefixes"); + return; + } + } + subValue->registryPrefixes = *regPrefixes; + } - // If no registry prefixes are mentioned, consider all - // supported prefixes - if (subValue->registryPrefixes.empty()) + if (originResources) { - registryPrefix.assign(supportedRegPrefixes.begin(), - supportedRegPrefixes.end()); + subValue->originResources = *originResources; } - else + + if (resTypes) { - registryPrefix = subValue->registryPrefixes; + for (const std::string& it : *resTypes) + { + if (std::ranges::find(supportedResourceTypes, it) == + supportedResourceTypes.end()) + { + messages::propertyValueNotInList(asyncResp->res, it, + "ResourceTypes"); + return; + } + } + subValue->resourceTypes = *resTypes; } - for (const std::string& id : *msgIds) + if (msgIds) { - bool validId = false; + std::vector<std::string> registryPrefix; - // Check for Message ID in each of the selected Registry - for (const std::string& it : registryPrefix) + // If no registry prefixes are mentioned, consider all + // supported prefixes + if (subValue->registryPrefixes.empty()) + { + registryPrefix.assign(supportedRegPrefixes.begin(), + supportedRegPrefixes.end()); + } + else { - const std::span<const redfish::registries::MessageEntry> - registry = - redfish::registries::getRegistryFromPrefix(it); + registryPrefix = subValue->registryPrefixes; + } - if (std::ranges::any_of( - registry, - [&id](const redfish::registries::MessageEntry& - messageEntry) { - return id == messageEntry.first; - })) + for (const std::string& id : *msgIds) + { + bool validId = false; + + // Check for Message ID in each of the selected Registry + for (const std::string& it : registryPrefix) { - validId = true; - break; + const std::span<const redfish::registries::MessageEntry> + registry = + redfish::registries::getRegistryFromPrefix(it); + + if (std::ranges::any_of( + registry, + [&id](const redfish::registries::MessageEntry& + messageEntry) { + return id == messageEntry.first; + })) + { + validId = true; + break; + } + } + + if (!validId) + { + messages::propertyValueNotInList(asyncResp->res, id, + "MessageIds"); + return; } } - if (!validId) + subValue->registryMsgIds = *msgIds; + } + + if (retryPolicy) + { + if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == + supportedRetryPolicies.end()) { - messages::propertyValueNotInList(asyncResp->res, id, - "MessageIds"); + messages::propertyValueNotInList( + asyncResp->res, *retryPolicy, "DeliveryRetryPolicy"); return; } + subValue->retryPolicy = *retryPolicy; } - - subValue->registryMsgIds = *msgIds; - } - - if (retryPolicy) - { - if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == - supportedRetryPolicies.end()) + else { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; + // Default "TerminateAfterRetries" + subValue->retryPolicy = "TerminateAfterRetries"; } - subValue->retryPolicy = *retryPolicy; - } - else - { - // Default "TerminateAfterRetries" - subValue->retryPolicy = "TerminateAfterRetries"; - } - if (mrdJsonArray) - { - for (nlohmann::json::object_t& mrdObj : *mrdJsonArray) + if (mrdJsonArray) { - std::string mrdUri; + for (nlohmann::json::object_t& mrdObj : *mrdJsonArray) + { + std::string mrdUri; - if (!json_util::readJsonObject(mrdObj, asyncResp->res, - "@odata.id", mrdUri)) + if (!json_util::readJsonObject(mrdObj, asyncResp->res, + "@odata.id", mrdUri)) - { - return; + { + return; + } + subValue->metricReportDefinitions.emplace_back(mrdUri); } - subValue->metricReportDefinitions.emplace_back(mrdUri); } - } - std::string id = - EventServiceManager::getInstance().addSubscription(subValue); - if (id.empty()) - { - messages::internalError(asyncResp->res); - return; - } + std::string id = + EventServiceManager::getInstance().addPushSubscription( + subValue); + if (id.empty()) + { + messages::internalError(asyncResp->res); + return; + } - messages::created(asyncResp->res); - asyncResp->res.addHeader( - "Location", "/redfish/v1/EventService/Subscriptions/" + id); - }); + messages::created(asyncResp->res); + asyncResp->res.addHeader( + "Location", "/redfish/v1/EventService/Subscriptions/" + id); + }); } inline void requestRoutesEventDestination(App& app) @@ -633,55 +662,66 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (param.starts_with("snmp")) - { - getSnmpTrapClient(asyncResp, param); - return; - } + if (param.starts_with("snmp")) + { + getSnmpTrapClient(asyncResp, param); + return; + } - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(param); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - const std::string& id = param; - - asyncResp->res.jsonValue["@odata.type"] = - "#EventDestination.v1_8_0.EventDestination"; - asyncResp->res.jsonValue["Protocol"] = "Redfish"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/EventService/Subscriptions/{}", id); - asyncResp->res.jsonValue["Id"] = id; - asyncResp->res.jsonValue["Name"] = "Event Destination " + id; - asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl; - asyncResp->res.jsonValue["Context"] = subValue->customText; - asyncResp->res.jsonValue["SubscriptionType"] = - subValue->subscriptionType; - asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array(); - asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType; - asyncResp->res.jsonValue["RegistryPrefixes"] = - subValue->registryPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes; - - asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; - asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; - - nlohmann::json::array_t mrdJsonArray; - for (const auto& mdrUri : subValue->metricReportDefinitions) - { - nlohmann::json::object_t mdr; - mdr["@odata.id"] = mdrUri; - mrdJsonArray.emplace_back(std::move(mdr)); - } - asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray; - }); + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + const std::string& id = param; + + asyncResp->res.jsonValue["@odata.type"] = + "#EventDestination.v1_14_1.EventDestination"; + asyncResp->res.jsonValue["Protocol"] = + event_destination::EventDestinationProtocol::Redfish; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}", id); + asyncResp->res.jsonValue["Id"] = id; + asyncResp->res.jsonValue["Name"] = "Event Destination " + id; + asyncResp->res.jsonValue["Destination"] = + subValue->destinationUrl; + asyncResp->res.jsonValue["Context"] = subValue->customText; + asyncResp->res.jsonValue["SubscriptionType"] = + subValue->subscriptionType; + asyncResp->res.jsonValue["HttpHeaders"] = + nlohmann::json::array(); + asyncResp->res.jsonValue["EventFormatType"] = + subValue->eventFormatType; + asyncResp->res.jsonValue["RegistryPrefixes"] = + subValue->registryPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = + subValue->resourceTypes; + + asyncResp->res.jsonValue["MessageIds"] = + subValue->registryMsgIds; + asyncResp->res.jsonValue["DeliveryRetryPolicy"] = + subValue->retryPolicy; + asyncResp->res.jsonValue["VerifyCertificate"] = + subValue->verifyCertificate; + + nlohmann::json::array_t mrdJsonArray; + for (const auto& mdrUri : subValue->metricReportDefinitions) + { + nlohmann::json::object_t mdr; + mdr["@odata.id"] = mdrUri; + mrdJsonArray.emplace_back(std::move(mdr)); + } + asyncResp->res.jsonValue["MetricReportDefinitions"] = + mrdJsonArray; + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") // The below privilege is wrong, it should be ConfigureManager OR // ConfigureSelf @@ -692,70 +732,81 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(param); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } - std::optional<std::string> context; - std::optional<std::string> retryPolicy; - std::optional<std::vector<nlohmann::json::object_t>> headers; + std::optional<std::string> context; + std::optional<std::string> retryPolicy; + std::optional<bool> verifyCertificate; + std::optional<std::vector<nlohmann::json::object_t>> headers; - if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context, - "DeliveryRetryPolicy", retryPolicy, - "HttpHeaders", headers)) - { - return; - } + if (!json_util::readJsonPatch( + req, asyncResp->res, "Context", context, + "VerifyCertificate", verifyCertificate, + "DeliveryRetryPolicy", retryPolicy, "HttpHeaders", + headers)) + { + return; + } - if (context) - { - subValue->customText = *context; - } + if (context) + { + subValue->customText = *context; + } - if (headers) - { - boost::beast::http::fields fields; - for (const nlohmann::json::object_t& headerChunk : *headers) - { - for (const auto& it : headerChunk) + if (headers) { - const std::string* value = - it.second.get_ptr<const std::string*>(); - if (value == nullptr) + boost::beast::http::fields fields; + for (const nlohmann::json::object_t& headerChunk : *headers) { - messages::propertyValueFormatError( - asyncResp->res, it.second, - "HttpHeaders/" + it.first); + for (const auto& it : headerChunk) + { + const std::string* value = + it.second.get_ptr<const std::string*>(); + if (value == nullptr) + { + messages::propertyValueFormatError( + asyncResp->res, it.second, + "HttpHeaders/" + it.first); + return; + } + fields.set(it.first, *value); + } + } + subValue->httpHeaders = std::move(fields); + } + + if (retryPolicy) + { + if (std::ranges::find(supportedRetryPolicies, + *retryPolicy) == + supportedRetryPolicies.end()) + { + messages::propertyValueNotInList(asyncResp->res, + *retryPolicy, + "DeliveryRetryPolicy"); return; } - fields.set(it.first, *value); + subValue->retryPolicy = *retryPolicy; } - } - subValue->httpHeaders = std::move(fields); - } - if (retryPolicy) - { - if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == - supportedRetryPolicies.end()) - { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; - } - subValue->retryPolicy = *retryPolicy; - } + if (verifyCertificate) + { + subValue->verifyCertificate = *verifyCertificate; + } - EventServiceManager::getInstance().updateSubscriptionData(); - }); + EventServiceManager::getInstance().updateSubscriptionData(); + }); BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") // The below privilege is wrong, it should be ConfigureManager OR // ConfigureSelf @@ -766,25 +817,28 @@ inline void requestRoutesEventDestination(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (param.starts_with("snmp")) - { - deleteSnmpTrapClient(asyncResp, param); - EventServiceManager::getInstance().deleteSubscription(param); - return; - } + if (param.starts_with("snmp")) + { + deleteSnmpTrapClient(asyncResp, param); + EventServiceManager::getInstance().deleteSubscription( + param); + return; + } - if (!EventServiceManager::getInstance().isSubscriptionExist(param)) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - EventServiceManager::getInstance().deleteSubscription(param); - }); + if (!EventServiceManager::getInstance().isSubscriptionExist( + param)) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + EventServiceManager::getInstance().deleteSubscription(param); + }); } } // namespace redfish diff --git a/redfish-core/lib/eventservice_sse.hpp b/redfish-core/lib/eventservice_sse.hpp index 3cbca3bc8b..c0a9527e94 100644 --- a/redfish-core/lib/eventservice_sse.hpp +++ b/redfish-core/lib/eventservice_sse.hpp @@ -1,5 +1,6 @@ #pragma once +#include "filter_expr_executor.hpp" #include "privileges.hpp" #include "registries/privilege_registry.hpp" @@ -12,7 +13,8 @@ namespace redfish { -inline void createSubscription(crow::sse_socket::Connection& conn) +inline void createSubscription(crow::sse_socket::Connection& conn, + const crow::Request& req) { EventServiceManager& manager = EventServiceManager::getInstance(&conn.getIoContext()); @@ -23,6 +25,25 @@ inline void createSubscription(crow::sse_socket::Connection& conn) conn.close("Max SSE subscriptions reached"); return; } + + std::optional<filter_ast::LogicalAnd> filter; + + boost::urls::params_base::iterator filterIt = + req.url().params().find("$filter"); + + if (filterIt != req.url().params().end()) + { + std::string_view filterValue = (*filterIt).value; + filter = parseFilter(filterValue); + if (!filter) + { + conn.close(std::format("Bad $filter param: {}", filterValue)); + return; + } + } + + std::string lastEventId(req.getHeaderValue("Last-Event-Id")); + std::shared_ptr<redfish::Subscription> subValue = std::make_shared<redfish::Subscription>(conn); @@ -32,8 +53,9 @@ inline void createSubscription(crow::sse_socket::Connection& conn) subValue->protocol = "Redfish"; subValue->retryPolicy = "TerminateAfterRetries"; subValue->eventFormatType = "Event"; + subValue->filter = filter; - std::string id = manager.addSubscription(subValue, false); + std::string id = manager.addSSESubscription(subValue, lastEventId); if (id.empty()) { conn.close("Internal Error"); diff --git a/redfish-core/lib/fabric_adapters.hpp b/redfish-core/lib/fabric_adapters.hpp index 11dc7c6973..20e6726c07 100644 --- a/redfish-core/lib/fabric_adapters.hpp +++ b/redfish-core/lib/fabric_adapters.hpp @@ -2,6 +2,7 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/collection.hpp" @@ -32,25 +33,25 @@ inline void getFabricAdapterLocation( "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Location"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Location"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - property; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + property; + }); } -inline void - getFabricAdapterAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& serviceName, - const std::string& fabricAdapterPath) +inline void getFabricAdapterAsset( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& serviceName, const std::string& fabricAdapterPath) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, serviceName, fabricAdapterPath, @@ -58,112 +59,111 @@ inline void [fabricAdapterPath, asyncResp{asyncResp}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Properties"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Properties"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - const std::string* serialNumber = nullptr; - const std::string* model = nullptr; - const std::string* partNumber = nullptr; - const std::string* sparePartNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* model = nullptr; + const std::string* partNumber = nullptr; + const std::string* sparePartNumber = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "SerialNumber", - serialNumber, "Model", model, "PartNumber", partNumber, - "SparePartNumber", sparePartNumber); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, + "SerialNumber", serialNumber, "Model", model, "PartNumber", + partNumber, "SparePartNumber", sparePartNumber); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } + if (serialNumber != nullptr) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } - if (model != nullptr) - { - asyncResp->res.jsonValue["Model"] = *model; - } + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = *model; + } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } - if (sparePartNumber != nullptr && !sparePartNumber->empty()) - { - asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; - } - }); + if (sparePartNumber != nullptr && !sparePartNumber->empty()) + { + asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; + } + }); } -inline void - getFabricAdapterState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& serviceName, - const std::string& fabricAdapterPath) +inline void getFabricAdapterState( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& serviceName, const std::string& fabricAdapterPath) { sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, serviceName, fabricAdapterPath, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, const bool present) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for State"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for State"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!present) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + if (!present) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); } -inline void - getFabricAdapterHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& serviceName, - const std::string& fabricAdapterPath) +inline void getFabricAdapterHealth( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& serviceName, const std::string& fabricAdapterPath) { sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, serviceName, fabricAdapterPath, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp](const boost::system::error_code& ec, const bool functional) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Health"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Health"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!functional) - { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; - } - }); + if (!functional) + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; + } + }); } -inline void doAdapterGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& adapterId, - const std::string& fabricAdapterPath, - const std::string& serviceName) +inline void doAdapterGet( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& adapterId, + const std::string& fabricAdapterPath, const std::string& serviceName) { asyncResp->res.addHeader( boost::beast::http::field::link, @@ -175,8 +175,8 @@ inline void doAdapterGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId); - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath); getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath); @@ -256,11 +256,10 @@ inline void afterHandleFabricAdapterGet( serviceName); } -inline void - handleFabricAdapterGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& adapterId) +inline void handleFabricAdapterGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& adapterId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -381,11 +380,10 @@ inline void afterHandleFabricAdapterHead( "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); } -inline void - handleFabricAdapterHead(crow::App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& adapterId) +inline void handleFabricAdapterHead( + crow::App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& adapterId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { diff --git a/redfish-core/lib/fan.hpp b/redfish-core/lib/fan.hpp index 39b9e2abd7..28b995e794 100644 --- a/redfish-core/lib/fan.hpp +++ b/redfish-core/lib/fan.hpp @@ -3,6 +3,7 @@ #include "app.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/chassis_utils.hpp" @@ -64,19 +65,19 @@ inline void getFanPaths( [asyncResp, callback]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR( - "DBUS response error for getAssociatedSubTreePaths {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR( + "DBUS response error for getAssociatedSubTreePaths {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - callback(subtreePaths); - }); + callback(subtreePaths); + }); } inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -119,15 +120,16 @@ inline void asyncResp, chassisId, [asyncResp, chassisId](const std::optional<std::string>& validChassisPath) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); - }); + if (!validChassisPath) + { + messages::resourceNotFound(asyncResp->res, "Chassis", + chassisId); + return; + } + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby"); + }); } inline void @@ -165,7 +167,7 @@ inline bool checkFanId(const std::string& fanPath, const std::string& fanId) return !(fanName.empty() || fanName != fanId); } -static inline void handleFanPath( +inline void handleFanPath( const std::string& fanId, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const dbus::utility::MapperGetSubTreePathsResponse& fanPaths, @@ -183,15 +185,15 @@ static inline void handleFanPath( [fanPath, asyncResp, callback](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& object) { - if (ec || object.empty()) - { - BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}", - ec.value()); - messages::internalError(asyncResp->res); - return; - } - callback(fanPath, object.begin()->first); - }); + if (ec || object.empty()) + { + BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}", + ec.value()); + messages::internalError(asyncResp->res); + return; + } + callback(fanPath, object.begin()->first); + }); return; } @@ -209,8 +211,8 @@ inline void getValidFanPath( asyncResp, validChassisPath, [fanId, asyncResp, callback]( const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) { - handleFanPath(fanId, asyncResp, fanPaths, callback); - }); + handleFanPath(fanId, asyncResp, fanPaths, callback); + }); } inline void addFanCommonProperties(crow::Response& resp, @@ -224,8 +226,8 @@ inline void addFanCommonProperties(crow::Response& resp, resp.jsonValue["Id"] = fanId; resp.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId); - resp.jsonValue["Status"]["State"] = "Enabled"; - resp.jsonValue["Status"]["Health"] = "OK"; + resp.jsonValue["Status"]["State"] = resource::State::Enabled; + resp.jsonValue["Status"]["Health"] = resource::Health::OK; } inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -235,22 +237,23 @@ inline void getFanHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, *crow::connections::systemBus, service, fanPath, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp](const boost::system::error_code& ec, const bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Health {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Health {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; + } + }); } inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -260,22 +263,23 @@ inline void getFanState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, *crow::connections::systemBus, service, fanPath, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, const bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for State {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for State {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); } inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -287,52 +291,53 @@ inline void getFanAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, [fanPath, asyncResp{asyncResp}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& assetList) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) + { + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Properties{}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; + } + const std::string* manufacturer = nullptr; + const std::string* model = nullptr; + const std::string* partNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* sparePartNumber = nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", + manufacturer, "Model", model, "PartNumber", partNumber, + "SerialNumber", serialNumber, "SparePartNumber", + sparePartNumber); + if (!success) { - BMCWEB_LOG_ERROR("DBUS response error for Properties{}", - ec.value()); messages::internalError(asyncResp->res); + return; } - return; - } - const std::string* manufacturer = nullptr; - const std::string* model = nullptr; - const std::string* partNumber = nullptr; - const std::string* serialNumber = nullptr; - const std::string* sparePartNumber = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", - manufacturer, "Model", model, "PartNumber", partNumber, - "SerialNumber", serialNumber, "SparePartNumber", sparePartNumber); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; - } - if (model != nullptr) - { - asyncResp->res.jsonValue["Model"] = *model; - } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } - if (sparePartNumber != nullptr && !sparePartNumber->empty()) - { - asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; - } - }); + if (manufacturer != nullptr) + { + asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + } + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = *model; + } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } + if (serialNumber != nullptr) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } + if (sparePartNumber != nullptr && !sparePartNumber->empty()) + { + asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; + } + }); } inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -344,19 +349,20 @@ inline void getFanLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Location{}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Location{}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - property; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + property; + }); } inline void @@ -400,18 +406,20 @@ inline void handleFanHead(App& app, const crow::Request& req, asyncResp, chassisId, [asyncResp, chassisId, fanId](const std::optional<std::string>& validChassisPath) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - getValidFanPath(asyncResp, *validChassisPath, fanId, - [asyncResp](const std::string&, const std::string&) { - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); + if (!validChassisPath) + { + messages::resourceNotFound(asyncResp->res, "Chassis", + chassisId); + return; + } + getValidFanPath( + asyncResp, *validChassisPath, fanId, + [asyncResp](const std::string&, const std::string&) { + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby"); + }); }); - }); } inline void handleFanGet(App& app, const crow::Request& req, diff --git a/redfish-core/lib/hypervisor_system.hpp b/redfish-core/lib/hypervisor_system.hpp index 963fbfb84b..cc9d4c0313 100644 --- a/redfish-core/lib/hypervisor_system.hpp +++ b/redfish-core/lib/hypervisor_system.hpp @@ -5,6 +5,9 @@ #include "dbus_utility.hpp" #include "error_messages.hpp" #include "ethernet.hpp" +#include "generated/enums/action_info.hpp" +#include "generated/enums/computer_system.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/ip_utils.hpp" @@ -41,56 +44,69 @@ inline void "xyz.openbmc_project.State.Host", "CurrentHostState", [asyncResp](const boost::system::error_code& ec, const std::string& hostState) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - // This is an optional D-Bus object so just return if - // error occurs - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + // This is an optional D-Bus object so just return if + // error occurs + return; + } - BMCWEB_LOG_DEBUG("Hypervisor state: {}", hostState); - // Verify Host State - if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - } - else if (hostState == "xyz.openbmc_project.State.Host.HostState." - "Quiesced") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; - } - else if (hostState == "xyz.openbmc_project.State.Host.HostState." - "Standby") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; - } - else if (hostState == "xyz.openbmc_project.State.Host.HostState." - "TransitioningToRunning") - { - asyncResp->res.jsonValue["PowerState"] = "PoweringOn"; - asyncResp->res.jsonValue["Status"]["State"] = "Starting"; - } - else if (hostState == "xyz.openbmc_project.State.Host.HostState." - "TransitioningToOff") - { - asyncResp->res.jsonValue["PowerState"] = "PoweringOff"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - } - else if (hostState == "xyz.openbmc_project.State.Host.HostState.Off") - { - asyncResp->res.jsonValue["PowerState"] = "Off"; - asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; - } - else - { - messages::internalError(asyncResp->res); - return; - } - }); + BMCWEB_LOG_DEBUG("Hypervisor state: {}", hostState); + // Verify Host State + if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + } + else if (hostState == "xyz.openbmc_project.State.Host.HostState." + "Quiesced") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Quiesced; + } + else if (hostState == "xyz.openbmc_project.State.Host.HostState." + "Standby") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::StandbyOffline; + } + else if (hostState == "xyz.openbmc_project.State.Host.HostState." + "TransitioningToRunning") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOn; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Starting; + } + else if (hostState == "xyz.openbmc_project.State.Host.HostState." + "TransitioningToOff") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOff; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + } + else if (hostState == + "xyz.openbmc_project.State.Host.HostState.Off") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::Off; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Disabled; + } + else + { + messages::internalError(asyncResp->res); + return; + } + }); } /** @@ -115,37 +131,37 @@ inline void const boost::system::error_code& ec, const std::vector<std::pair<std::string, std::vector<std::string>>>& objInfo) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - // This is an optional D-Bus object so just return if - // error occurs - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + // This is an optional D-Bus object so just return if + // error occurs + return; + } - if (objInfo.empty()) - { - // As noted above, this is an optional interface so just return - // if there is no instance found - return; - } + if (objInfo.empty()) + { + // As noted above, this is an optional interface so just return + // if there is no instance found + return; + } - if (objInfo.size() > 1) - { - // More then one hypervisor object is not supported and is an - // error - messages::internalError(asyncResp->res); - return; - } + if (objInfo.size() > 1) + { + // More then one hypervisor object is not supported and is an + // error + messages::internalError(asyncResp->res); + return; + } - // Object present so system support limited ComputerSystem Action - nlohmann::json& reset = - asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]; - reset["target"] = - "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"; - reset["@Redfish.ActionInfo"] = - "/redfish/v1/Systems/hypervisor/ResetActionInfo"; - }); + // Object present so system support limited ComputerSystem Action + nlohmann::json& reset = + asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]; + reset["target"] = + "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"; + reset["@Redfish.ActionInfo"] = + "/redfish/v1/Systems/hypervisor/ResetActionInfo"; + }); } inline bool extractHypervisorInterfaceData( @@ -162,23 +178,8 @@ inline bool extractHypervisorInterfaceData( "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) { idFound = true; - if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") - { - for (const auto& propertyPair : ifacePair.second) - { - if (propertyPair.first == "MACAddress") - { - const std::string* mac = - std::get_if<std::string>(&propertyPair.second); - if (mac != nullptr) - { - ethData.macAddress = *mac; - } - } - } - } - else if (ifacePair.first == - "xyz.openbmc_project.Network.EthernetInterface") + if (ifacePair.first == + "xyz.openbmc_project.Network.EthernetInterface") { for (const auto& propertyPair : ifacePair.second) { @@ -318,22 +319,22 @@ void getHypervisorIfaceData(const std::string& ethIfaceId, callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) mutable { - EthernetInterfaceData ethData{}; - std::vector<IPv4AddressData> ipv4Data; - if (ec) - { - callback(false, ethData, ipv4Data); - return; - } + EthernetInterfaceData ethData{}; + std::vector<IPv4AddressData> ipv4Data; + if (ec) + { + callback(false, ethData, ipv4Data); + return; + } - bool found = extractHypervisorInterfaceData(ethIfaceId, resp, ethData, - ipv4Data); - if (!found) - { - BMCWEB_LOG_INFO("Hypervisor Interface not found"); - } - callback(found, ethData, ipv4Data); - }); + bool found = extractHypervisorInterfaceData(ethIfaceId, resp, + ethData, ipv4Data); + if (!found) + { + BMCWEB_LOG_INFO("Hypervisor Interface not found"); + } + callback(found, ethData, ipv4Data); + }); } /** @@ -352,11 +353,11 @@ inline void setHypervisorIPv4Address( BMCWEB_LOG_DEBUG("Setting the Hypervisor IPaddress : {} on Iface: {}", ipv4Address, ethIfaceId); - setDbusProperty(asyncResp, "IPv4StaticAddresses/1/Address", - "xyz.openbmc_project.Settings", - "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + - "/ipv4/addr0", - "xyz.openbmc_project.Network.IP", "Address", ipv4Address); + setDbusProperty( + asyncResp, "IPv4StaticAddresses/1/Address", + "xyz.openbmc_project.Settings", + "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", + "xyz.openbmc_project.Network.IP", "Address", ipv4Address); } /** @@ -375,11 +376,11 @@ inline void BMCWEB_LOG_DEBUG("Setting the Hypervisor subnet : {} on Iface: {}", subnet, ethIfaceId); - setDbusProperty(asyncResp, "IPv4StaticAddresses/1/SubnetMask", - "xyz.openbmc_project.Settings", - "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + - "/ipv4/addr0", - "xyz.openbmc_project.Network.IP", "PrefixLength", subnet); + setDbusProperty( + asyncResp, "IPv4StaticAddresses/1/SubnetMask", + "xyz.openbmc_project.Settings", + "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", + "xyz.openbmc_project.Network.IP", "PrefixLength", subnet); } /** @@ -456,8 +457,6 @@ inline void parseInterfaceData(nlohmann::json& jsonResponse, jsonResponse["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", ifaceId); jsonResponse["InterfaceEnabled"] = true; - jsonResponse["MACAddress"] = ethData.macAddress; - jsonResponse["HostName"] = ethData.hostName; jsonResponse["DHCPv4"]["DHCPEnabled"] = translateDhcpEnabledToBool(ethData.dhcpEnabled, true); @@ -512,11 +511,11 @@ inline void setDHCPEnabled(const std::string& ifaceId, bool ipv4DHCPEnabled, origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; } - setDbusProperty(asyncResp, "IPv4StaticAddresses/1/AddressOrigin", - "xyz.openbmc_project.Settings", - "/xyz/openbmc_project/network/hypervisor/" + ifaceId + - "/ipv4/addr0", - "xyz.openbmc_project.Network.IP", "Origin", origin); + setDbusProperty( + asyncResp, "IPv4StaticAddresses/1/AddressOrigin", + "xyz.openbmc_project.Settings", + "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", + "xyz.openbmc_project.Network.IP", "Origin", origin); } inline void handleHypervisorIPv4StaticPatch( @@ -625,39 +624,41 @@ inline void handleHypervisorEthernetInterfaceCollectionGet( [asyncResp]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { - if (ec) - { - messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#EthernetInterfaceCollection." - "EthernetInterfaceCollection"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; - asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " - "Interface Collection"; - asyncResp->res.jsonValue["Description"] = - "Collection of Virtual Management " - "Interfaces for the hypervisor"; - - nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; - ifaceArray = nlohmann::json::array(); - for (const std::string& iface : ifaceList) - { - sdbusplus::message::object_path path(iface); - std::string name = path.filename(); - if (name.empty()) + if (ec) { - continue; + messages::resourceNotFound(asyncResp->res, "System", + "hypervisor"); + return; } - nlohmann::json::object_t ethIface; - ethIface["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", name); - ifaceArray.emplace_back(std::move(ethIface)); - } - asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); - }); + asyncResp->res.jsonValue["@odata.type"] = + "#EthernetInterfaceCollection." + "EthernetInterfaceCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; + asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " + "Interface Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of Virtual Management " + "Interfaces for the hypervisor"; + + nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; + ifaceArray = nlohmann::json::array(); + for (const std::string& iface : ifaceList) + { + sdbusplus::message::object_path path(iface); + std::string name = path.filename(); + if (name.empty()) + { + continue; + } + nlohmann::json::object_t ethIface; + ethIface["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", + name); + ifaceArray.emplace_back(std::move(ethIface)); + } + asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); + }); } inline void handleHypervisorEthernetInterfaceGet( @@ -672,58 +673,43 @@ inline void handleHypervisorEthernetInterfaceGet( id, [asyncResp, ifaceId{std::string(id)}]( bool success, const EthernetInterfaceData& ethData, const std::vector<IPv4AddressData>& ipv4Data) { - if (!success) - { - messages::resourceNotFound(asyncResp->res, "EthernetInterface", - ifaceId); - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#EthernetInterface.v1_9_0.EthernetInterface"; - asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; - asyncResp->res.jsonValue["Description"] = - "Hypervisor's Virtual Management Ethernet Interface"; - parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, - ipv4Data); - }); + if (!success) + { + messages::resourceNotFound(asyncResp->res, "EthernetInterface", + ifaceId); + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#EthernetInterface.v1_9_0.EthernetInterface"; + asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; + asyncResp->res.jsonValue["Description"] = + "Hypervisor's Virtual Management Ethernet Interface"; + parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, + ipv4Data); + }); } inline void handleHypervisorSystemGet( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - sdbusplus::asio::getProperty<std::string>( - *crow::connections::systemBus, "xyz.openbmc_project.Settings", - "/xyz/openbmc_project/network/hypervisor", - "xyz.openbmc_project.Network.SystemConfiguration", "HostName", - [asyncResp](const boost::system::error_code& ec, - const std::string& /*hostName*/) { - if (ec) - { - messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); - return; - } - BMCWEB_LOG_DEBUG("Hypervisor is available"); - - asyncResp->res.jsonValue["@odata.type"] = - "#ComputerSystem.v1_6_0.ComputerSystem"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/Systems/hypervisor"; - asyncResp->res.jsonValue["Description"] = "Hypervisor"; - asyncResp->res.jsonValue["Name"] = "Hypervisor"; - asyncResp->res.jsonValue["Id"] = "hypervisor"; - asyncResp->res.jsonValue["SystemType"] = "OS"; - nlohmann::json::array_t managedBy; - nlohmann::json::object_t manager; - manager["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); - managedBy.emplace_back(std::move(manager)); - asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); - asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = - "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; - getHypervisorState(asyncResp); - getHypervisorActions(asyncResp); - // TODO: Add "SystemType" : "hypervisor" - }); + asyncResp->res.jsonValue["@odata.type"] = + "#ComputerSystem.v1_6_0.ComputerSystem"; + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/hypervisor"; + asyncResp->res.jsonValue["Description"] = "Hypervisor"; + asyncResp->res.jsonValue["Name"] = "Hypervisor"; + asyncResp->res.jsonValue["Id"] = "hypervisor"; + asyncResp->res.jsonValue["SystemType"] = computer_system::SystemType::OS; + nlohmann::json::array_t managedBy; + nlohmann::json::object_t manager; + manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME); + managedBy.emplace_back(std::move(manager)); + asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); + asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = + "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; + getHypervisorState(asyncResp); + getHypervisorActions(asyncResp); + // TODO: Add "SystemType" : "hypervisor" } inline void handleHypervisorEthernetInterfacePatch( @@ -742,10 +728,10 @@ inline void handleHypervisorEthernetInterfacePatch( std::optional<std::vector<nlohmann::json::object_t>> ipv4Addresses; std::optional<bool> ipv4DHCPEnabled; - if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostName, - "IPv4StaticAddresses", ipv4StaticAddresses, - "IPv4Addresses", ipv4Addresses, - "DHCPv4/DHCPEnabled", ipv4DHCPEnabled)) + if (!json_util::readJsonPatch( + req, asyncResp->res, "HostName", hostName, "IPv4StaticAddresses", + ipv4StaticAddresses, "IPv4Addresses", ipv4Addresses, + "DHCPv4/DHCPEnabled", ipv4DHCPEnabled)) { return; } @@ -762,67 +748,70 @@ inline void handleHypervisorEthernetInterfacePatch( ipv4StaticAddresses = std::move(ipv4StaticAddresses), ipv4DHCPEnabled](bool success, const EthernetInterfaceData& ethData, const std::vector<IPv4AddressData>&) mutable { - if (!success) - { - messages::resourceNotFound(asyncResp->res, "EthernetInterface", - ifaceId); - return; - } - - if (ipv4StaticAddresses) - { - std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& - ipv4Static = *ipv4StaticAddresses; - if (ipv4Static.begin() == ipv4Static.end()) + if (!success) { - messages::propertyValueTypeError(asyncResp->res, - std::vector<std::string>(), - "IPv4StaticAddresses"); + messages::resourceNotFound(asyncResp->res, "EthernetInterface", + ifaceId); return; } - // One and only one hypervisor instance supported - if (ipv4Static.size() != 1) + if (ipv4StaticAddresses) { - messages::propertyValueFormatError(asyncResp->res, "[]", - "IPv4StaticAddresses"); - return; + std::vector<std::variant<nlohmann::json::object_t, + std::nullptr_t>>& ipv4Static = + *ipv4StaticAddresses; + if (ipv4Static.begin() == ipv4Static.end()) + { + messages::propertyValueTypeError(asyncResp->res, + std::vector<std::string>(), + "IPv4StaticAddresses"); + return; + } + + // One and only one hypervisor instance supported + if (ipv4Static.size() != 1) + { + messages::propertyValueFormatError(asyncResp->res, "[]", + "IPv4StaticAddresses"); + return; + } + + std::variant<nlohmann::json::object_t, std::nullptr_t>& + ipv4Json = ipv4Static[0]; + // Check if the param is 'null'. If its null, it means + // that user wants to delete the IP address. Deleting + // the IP address is allowed only if its statically + // configured. Deleting the address originated from DHCP + // is not allowed. + if (std::holds_alternative<std::nullptr_t>(ipv4Json) && + translateDhcpEnabledToBool(ethData.dhcpEnabled, true)) + { + BMCWEB_LOG_INFO( + "Ignoring the delete on ipv4StaticAddresses " + "as the interface is DHCP enabled"); + } + else + { + handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, + asyncResp); + } } - std::variant<nlohmann::json::object_t, std::nullptr_t>& ipv4Json = - ipv4Static[0]; - // Check if the param is 'null'. If its null, it means - // that user wants to delete the IP address. Deleting - // the IP address is allowed only if its statically - // configured. Deleting the address originated from DHCP - // is not allowed. - if (std::holds_alternative<std::nullptr_t>(ipv4Json) && - translateDhcpEnabledToBool(ethData.dhcpEnabled, true)) + if (hostName) { - BMCWEB_LOG_INFO("Ignoring the delete on ipv4StaticAddresses " - "as the interface is DHCP enabled"); + handleHypervisorHostnamePatch(*hostName, asyncResp); } - else + + if (ipv4DHCPEnabled) { - handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, asyncResp); + setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); } - } - - if (hostName) - { - handleHypervisorHostnamePatch(*hostName, asyncResp); - } - if (ipv4DHCPEnabled) - { - setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); - } - - // Set this interface to disabled/inactive. This will be set - // to enabled/active by the pldm once the hypervisor - // consumes the updated settings from the user. - setIPv4InterfaceEnabled(ifaceId, false, asyncResp); - }); + // Set this interface to disabled/inactive. This will be set + // to enabled/active by the pldm once the hypervisor + // consumes the updated settings from the user. + setIPv4InterfaceEnabled(ifaceId, false, asyncResp); + }); asyncResp->res.result(boost::beast::http::status::accepted); } @@ -838,60 +827,56 @@ inline void handleHypervisorResetActionGet( const boost::system::error_code& ec, const std::vector<std::pair<std::string, std::vector<std::string>>>& objInfo) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - - // No hypervisor objects found by mapper - if (ec.value() == boost::system::errc::io_error) + if (ec) { - messages::resourceNotFound(asyncResp->res, "hypervisor", - "ResetActionInfo"); + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + + // No hypervisor objects found by mapper + if (ec.value() == boost::system::errc::io_error) + { + messages::resourceNotFound(asyncResp->res, "hypervisor", + "ResetActionInfo"); + return; + } + + messages::internalError(asyncResp->res); return; } - messages::internalError(asyncResp->res); - return; - } - - // One and only one hypervisor instance supported - if (objInfo.size() != 1) - { - messages::internalError(asyncResp->res); - return; - } + // One and only one hypervisor instance supported + if (objInfo.size() != 1) + { + messages::internalError(asyncResp->res); + return; + } - // The hypervisor object only support the ability to - // turn On The system object Action should be utilized - // for other operations - - asyncResp->res.jsonValue["@odata.type"] = - "#ActionInfo.v1_1_2.ActionInfo"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/Systems/hypervisor/ResetActionInfo"; - asyncResp->res.jsonValue["Name"] = "Reset Action Info"; - asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; - nlohmann::json::array_t parameters; - nlohmann::json::object_t parameter; - parameter["Name"] = "ResetType"; - parameter["Required"] = true; - parameter["DataType"] = "String"; - nlohmann::json::array_t allowed; - allowed.emplace_back("On"); - parameter["AllowableValues"] = std::move(allowed); - parameters.emplace_back(std::move(parameter)); - asyncResp->res.jsonValue["Parameters"] = std::move(parameters); - }); + // The hypervisor object only support the ability to + // turn On The system object Action should be utilized + // for other operations + + asyncResp->res.jsonValue["@odata.type"] = + "#ActionInfo.v1_1_2.ActionInfo"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/Systems/hypervisor/ResetActionInfo"; + asyncResp->res.jsonValue["Name"] = "Reset Action Info"; + asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; + nlohmann::json::array_t parameters; + nlohmann::json::object_t parameter; + parameter["Name"] = "ResetType"; + parameter["Required"] = true; + parameter["DataType"] = action_info::ParameterTypes::String; + nlohmann::json::array_t allowed; + allowed.emplace_back("On"); + parameter["AllowableValues"] = std::move(allowed); + parameters.emplace_back(std::move(parameter)); + asyncResp->res.jsonValue["Parameters"] = std::move(parameters); + }); } inline void handleHypervisorSystemResetPost( - App& app, const crow::Request& req, + const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } std::optional<std::string> resetType; if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) { @@ -916,12 +901,12 @@ inline void handleHypervisorSystemResetPost( std::string command = "xyz.openbmc_project.State.Host.Transition.On"; - setDbusPropertyAction(asyncResp, "xyz.openbmc_project.State.Hypervisor", - sdbusplus::message::object_path( - "/xyz/openbmc_project/state/hypervisor0"), - "xyz.openbmc_project.State.Host", - "RequestedHostTransition", "ResetType", - "ComputerSystem.Reset", command); + setDbusPropertyAction( + asyncResp, "xyz.openbmc_project.State.Hypervisor", + sdbusplus::message::object_path( + "/xyz/openbmc_project/state/hypervisor0"), + "xyz.openbmc_project.State.Host", "RequestedHostTransition", + "ResetType", "ComputerSystem.Reset", command); } inline void requestRoutesHypervisorSystems(App& app) @@ -947,11 +932,5 @@ inline void requestRoutesHypervisorSystems(App& app) .privileges(redfish::privileges::patchEthernetInterface) .methods(boost::beast::http::verb::patch)(std::bind_front( handleHypervisorEthernetInterfacePatch, std::ref(app))); - - BMCWEB_ROUTE(app, - "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") - .privileges(redfish::privileges::postComputerSystem) - .methods(boost::beast::http::verb::post)( - std::bind_front(handleHypervisorSystemResetPost, std::ref(app))); } } // namespace redfish diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp index 8cbe86ac94..1acde11dc8 100644 --- a/redfish-core/lib/led.hpp +++ b/redfish-core/lib/led.hpp @@ -1,23 +1,24 @@ /* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2019 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "async_resp.hpp" #include "dbus_utility.hpp" +#include "generated/enums/chassis.hpp" #include "redfish_util.hpp" #include <sdbusplus/asio/property.hpp> @@ -41,53 +42,56 @@ inline void "/xyz/openbmc_project/led/groups/enclosure_identify_blink", "xyz.openbmc_project.Led.Group", "Asserted", [asyncResp](const boost::system::error_code& ec, const bool blinking) { - // Some systems may not have enclosure_identify_blink object so - // proceed to get enclosure_identify state. - if (ec == boost::system::errc::invalid_argument) - { - BMCWEB_LOG_DEBUG( - "Get identity blinking LED failed, mismatch in property type"); - messages::internalError(asyncResp->res); - return; - } - - // Blinking ON, no need to check enclosure_identify assert. - if (!ec && blinking) - { - asyncResp->res.jsonValue["IndicatorLED"] = "Blinking"; - return; - } - - sdbusplus::asio::getProperty<bool>( - *crow::connections::systemBus, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups/enclosure_identify", - "xyz.openbmc_project.Led.Group", "Asserted", - [asyncResp](const boost::system::error_code& ec2, - const bool ledOn) { - if (ec2 == boost::system::errc::invalid_argument) + // Some systems may not have enclosure_identify_blink object so + // proceed to get enclosure_identify state. + if (ec == boost::system::errc::invalid_argument) { BMCWEB_LOG_DEBUG( - "Get enclosure identity led failed, mismatch in property type"); + "Get identity blinking LED failed, mismatch in property type"); messages::internalError(asyncResp->res); return; } - if (ec2) + // Blinking ON, no need to check enclosure_identify assert. + if (!ec && blinking) { + asyncResp->res.jsonValue["IndicatorLED"] = + chassis::IndicatorLED::Blinking; return; } - if (ledOn) - { - asyncResp->res.jsonValue["IndicatorLED"] = "Lit"; - } - else - { - asyncResp->res.jsonValue["IndicatorLED"] = "Off"; - } + sdbusplus::asio::getProperty<bool>( + *crow::connections::systemBus, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups/enclosure_identify", + "xyz.openbmc_project.Led.Group", "Asserted", + [asyncResp](const boost::system::error_code& ec2, + const bool ledOn) { + if (ec2 == boost::system::errc::invalid_argument) + { + BMCWEB_LOG_DEBUG( + "Get enclosure identity led failed, mismatch in property type"); + messages::internalError(asyncResp->res); + return; + } + + if (ec2) + { + return; + } + + if (ledOn) + { + asyncResp->res.jsonValue["IndicatorLED"] = + chassis::IndicatorLED::Lit; + } + else + { + asyncResp->res.jsonValue["IndicatorLED"] = + chassis::IndicatorLED::Off; + } + }); }); - }); } /** @@ -128,22 +132,23 @@ inline void "xyz.openbmc_project.Led.Group", "Asserted", ledBlinkng, [asyncResp, ledOn, ledBlinkng](const boost::system::error_code& ec) mutable { - if (ec) - { - // Some systems may not have enclosure_identify_blink object so - // Lets set enclosure_identify state to true if Blinking is - // true. - if (ledBlinkng) + if (ec) { - ledOn = true; + // Some systems may not have enclosure_identify_blink object so + // Lets set enclosure_identify state to true if Blinking is + // true. + if (ledBlinkng) + { + ledOn = true; + } } - } - setDbusProperty( - asyncResp, "IndicatorLED", "xyz.openbmc_project.LED.GroupManager", - sdbusplus::message::object_path( - "/xyz/openbmc_project/led/groups/enclosure_identify"), - "xyz.openbmc_project.Led.Group", "Asserted", ledBlinkng); - }); + setDbusProperty( + asyncResp, "IndicatorLED", + "xyz.openbmc_project.LED.GroupManager", + sdbusplus::message::object_path( + "/xyz/openbmc_project/led/groups/enclosure_identify"), + "xyz.openbmc_project.Led.Group", "Asserted", ledBlinkng); + }); } /** @@ -162,46 +167,46 @@ inline void getSystemLocationIndicatorActive( "/xyz/openbmc_project/led/groups/enclosure_identify_blink", "xyz.openbmc_project.Led.Group", "Asserted", [asyncResp](const boost::system::error_code& ec, const bool blinking) { - // Some systems may not have enclosure_identify_blink object so - // proceed to get enclosure_identify state. - if (ec == boost::system::errc::invalid_argument) - { - BMCWEB_LOG_DEBUG( - "Get identity blinking LED failed, mismatch in property type"); - messages::internalError(asyncResp->res); - return; - } - - // Blinking ON, no need to check enclosure_identify assert. - if (!ec && blinking) - { - asyncResp->res.jsonValue["LocationIndicatorActive"] = true; - return; - } - - sdbusplus::asio::getProperty<bool>( - *crow::connections::systemBus, - "xyz.openbmc_project.LED.GroupManager", - "/xyz/openbmc_project/led/groups/enclosure_identify", - "xyz.openbmc_project.Led.Group", "Asserted", - [asyncResp](const boost::system::error_code& ec2, - const bool ledOn) { - if (ec2 == boost::system::errc::invalid_argument) + // Some systems may not have enclosure_identify_blink object so + // proceed to get enclosure_identify state. + if (ec == boost::system::errc::invalid_argument) { BMCWEB_LOG_DEBUG( - "Get enclosure identity led failed, mismatch in property type"); + "Get identity blinking LED failed, mismatch in property type"); messages::internalError(asyncResp->res); return; } - if (ec2) + // Blinking ON, no need to check enclosure_identify assert. + if (!ec && blinking) { + asyncResp->res.jsonValue["LocationIndicatorActive"] = true; return; } - asyncResp->res.jsonValue["LocationIndicatorActive"] = ledOn; + sdbusplus::asio::getProperty<bool>( + *crow::connections::systemBus, + "xyz.openbmc_project.LED.GroupManager", + "/xyz/openbmc_project/led/groups/enclosure_identify", + "xyz.openbmc_project.Led.Group", "Asserted", + [asyncResp](const boost::system::error_code& ec2, + const bool ledOn) { + if (ec2 == boost::system::errc::invalid_argument) + { + BMCWEB_LOG_DEBUG( + "Get enclosure identity led failed, mismatch in property type"); + messages::internalError(asyncResp->res); + return; + } + + if (ec2) + { + return; + } + + asyncResp->res.jsonValue["LocationIndicatorActive"] = ledOn; + }); }); - }); } /** @@ -222,18 +227,18 @@ inline void setSystemLocationIndicatorActive( "/xyz/openbmc_project/led/groups/enclosure_identify_blink", "xyz.openbmc_project.Led.Group", "Asserted", ledState, [asyncResp, ledState](const boost::system::error_code& ec) { - if (ec) - { - // Some systems may not have enclosure_identify_blink object so - // lets set enclosure_identify state also if - // enclosure_identify_blink failed - setDbusProperty( - asyncResp, "LocationIndicatorActive", - "xyz.openbmc_project.LED.GroupManager", - sdbusplus::message::object_path( - "/xyz/openbmc_project/led/groups/enclosure_identify"), - "xyz.openbmc_project.Led.Group", "Asserted", ledState); - } - }); + if (ec) + { + // Some systems may not have enclosure_identify_blink object so + // lets set enclosure_identify state also if + // enclosure_identify_blink failed + setDbusProperty( + asyncResp, "LocationIndicatorActive", + "xyz.openbmc_project.LED.GroupManager", + sdbusplus::message::object_path( + "/xyz/openbmc_project/led/groups/enclosure_identify"), + "xyz.openbmc_project.Led.Group", "Asserted", ledState); + } + }); } } // namespace redfish diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index da2d1b55ad..639a31488c 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -19,6 +19,7 @@ #include "dbus_utility.hpp" #include "error_messages.hpp" #include "generated/enums/log_entry.hpp" +#include "generated/enums/log_service.hpp" #include "gzfile.hpp" #include "http_utility.hpp" #include "human_sort.hpp" @@ -29,12 +30,12 @@ #include "registries/privilege_registry.hpp" #include "task.hpp" #include "task_messages.hpp" +#include "utils/dbus_event_log_entry.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" #include "utils/time_utils.hpp" #include <systemd/sd-id128.h> -#include <systemd/sd-journal.h> #include <tinyxml2.h> #include <unistd.h> @@ -125,110 +126,7 @@ inline std::string getDumpPath(std::string_view dumpType) return dbusDumpPath; } -inline int getJournalMetadata(sd_journal* journal, std::string_view field, - std::string_view& contents) -{ - const char* data = nullptr; - size_t length = 0; - int ret = 0; - // Get the metadata from the requested field of the journal entry - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - const void** dataVoid = reinterpret_cast<const void**>(&data); - - ret = sd_journal_get_data(journal, field.data(), dataVoid, &length); - if (ret < 0) - { - return ret; - } - contents = std::string_view(data, length); - // Only use the content after the "=" character. - contents.remove_prefix(std::min(contents.find('=') + 1, contents.size())); - return ret; -} - -inline int getJournalMetadata(sd_journal* journal, std::string_view field, - const int& base, long int& contents) -{ - int ret = 0; - std::string_view metadata; - // Get the metadata from the requested field of the journal entry - ret = getJournalMetadata(journal, field, metadata); - if (ret < 0) - { - return ret; - } - contents = strtol(metadata.data(), nullptr, base); - return ret; -} - -inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp) -{ - int ret = 0; - uint64_t timestamp = 0; - ret = sd_journal_get_realtime_usec(journal, ×tamp); - if (ret < 0) - { - BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret)); - return false; - } - entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp); - return true; -} - -inline bool getUniqueEntryID(sd_journal* journal, std::string& entryID, - const bool firstEntry = true) -{ - int ret = 0; - static sd_id128_t prevBootID{}; - static uint64_t prevTs = 0; - static int index = 0; - if (firstEntry) - { - prevBootID = {}; - prevTs = 0; - } - - // Get the entry timestamp - uint64_t curTs = 0; - sd_id128_t curBootID{}; - ret = sd_journal_get_monotonic_usec(journal, &curTs, &curBootID); - if (ret < 0) - { - BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret)); - return false; - } - // If the timestamp isn't unique on the same boot, increment the index - bool sameBootIDs = sd_id128_equal(curBootID, prevBootID) != 0; - if (sameBootIDs && (curTs == prevTs)) - { - index++; - } - else - { - // Otherwise, reset it - index = 0; - } - - if (!sameBootIDs) - { - // Save the bootID - prevBootID = curBootID; - } - // Save the timestamp - prevTs = curTs; - - // make entryID as <bootID>_<timestamp>[_<index>] - std::array<char, SD_ID128_STRING_MAX> bootIDStr{}; - sd_id128_to_string(curBootID, bootIDStr.data()); - entryID = std::format("{}_{}", bootIDStr.data(), curTs); - if (index > 0) - { - entryID += "_" + std::to_string(index); - } - return true; -} - -static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, +inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, const bool firstEntry = true) { static time_t prevTs = 0; @@ -267,70 +165,7 @@ static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, return true; } -// Entry is formed like "BootID_timestamp" or "BootID_timestamp_index" inline bool - getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - std::string_view entryIDStrView, sd_id128_t& bootID, - uint64_t& timestamp, uint64_t& index) -{ - // Convert the unique ID back to a bootID + timestamp to find the entry - auto underscore1Pos = entryIDStrView.find('_'); - if (underscore1Pos == std::string_view::npos) - { - // EntryID has no bootID or timestamp - messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); - return false; - } - - // EntryID has bootID + timestamp - - // Convert entryIDViewString to BootID - // NOTE: bootID string which needs to be null-terminated for - // sd_id128_from_string() - std::string bootIDStr(entryIDStrView.substr(0, underscore1Pos)); - if (sd_id128_from_string(bootIDStr.c_str(), &bootID) < 0) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); - return false; - } - - // Get the timestamp from entryID - entryIDStrView.remove_prefix(underscore1Pos + 1); - - auto [timestampEnd, tstampEc] = std::from_chars( - entryIDStrView.begin(), entryIDStrView.end(), timestamp); - if (tstampEc != std::errc()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); - return false; - } - entryIDStrView = std::string_view( - timestampEnd, - static_cast<size_t>(std::distance(timestampEnd, entryIDStrView.end()))); - if (entryIDStrView.empty()) - { - index = 0U; - return true; - } - // Timestamp might include optional index, if two events happened at the - // same "time". - if (entryIDStrView[0] != '_') - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); - return false; - } - entryIDStrView.remove_prefix(1); - auto [ptr, indexEc] = std::from_chars(entryIDStrView.begin(), - entryIDStrView.end(), index); - if (indexEc != std::errc() || ptr != entryIDStrView.end()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); - return false; - } - return true; -} - -static bool getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles) { static const std::filesystem::path redfishLogDir = "/var/log"; @@ -525,102 +360,103 @@ inline void [asyncResp, entriesPath, dumpType](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& objects) { - if (ec) - { - BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); + messages::internalError(asyncResp->res); + return; + } - // Remove ending slash - std::string odataIdStr = entriesPath; - if (!odataIdStr.empty()) - { - odataIdStr.pop_back(); - } + // Remove ending slash + std::string odataIdStr = entriesPath; + if (!odataIdStr.empty()) + { + odataIdStr.pop_back(); + } - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr); - asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries"; - asyncResp->res.jsonValue["Description"] = "Collection of " + dumpType + - " Dump Entries"; + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr); + asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of " + dumpType + " Dump Entries"; - nlohmann::json::array_t entriesArray; - std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; + nlohmann::json::array_t entriesArray; + std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; - dbus::utility::ManagedObjectType resp(objects); - std::ranges::sort(resp, [](const auto& l, const auto& r) { - return AlphanumLess<std::string>()(l.first.filename(), - r.first.filename()); - }); + dbus::utility::ManagedObjectType resp(objects); + std::ranges::sort(resp, [](const auto& l, const auto& r) { + return AlphanumLess<std::string>()(l.first.filename(), + r.first.filename()); + }); - for (auto& object : resp) - { - if (object.first.str.find(dumpEntryPath) == std::string::npos) + for (auto& object : resp) { - continue; - } - uint64_t timestampUs = 0; - uint64_t size = 0; - std::string dumpStatus; - std::string originatorId; - log_entry::OriginatorTypes originatorType = - log_entry::OriginatorTypes::Internal; - nlohmann::json::object_t thisEntry; - - std::string entryID = object.first.filename(); - if (entryID.empty()) - { - continue; - } + if (object.first.str.find(dumpEntryPath) == std::string::npos) + { + continue; + } + uint64_t timestampUs = 0; + uint64_t size = 0; + std::string dumpStatus; + std::string originatorId; + log_entry::OriginatorTypes originatorType = + log_entry::OriginatorTypes::Internal; + nlohmann::json::object_t thisEntry; + + std::string entryID = object.first.filename(); + if (entryID.empty()) + { + continue; + } - parseDumpEntryFromDbusObject(object, dumpStatus, size, timestampUs, - originatorId, originatorType, - asyncResp); + parseDumpEntryFromDbusObject(object, dumpStatus, size, + timestampUs, originatorId, + originatorType, asyncResp); - if (dumpStatus != - "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && - !dumpStatus.empty()) - { - // Dump status is not Complete, no need to enumerate - continue; - } + if (dumpStatus != + "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && + !dumpStatus.empty()) + { + // Dump status is not Complete, no need to enumerate + continue; + } - thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; - thisEntry["@odata.id"] = entriesPath + entryID; - thisEntry["Id"] = entryID; - thisEntry["EntryType"] = "Event"; - thisEntry["Name"] = dumpType + " Dump Entry"; - thisEntry["Created"] = - redfish::time_utils::getDateTimeUintUs(timestampUs); + thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry"; + thisEntry["@odata.id"] = entriesPath + entryID; + thisEntry["Id"] = entryID; + thisEntry["EntryType"] = "Event"; + thisEntry["Name"] = dumpType + " Dump Entry"; + thisEntry["Created"] = + redfish::time_utils::getDateTimeUintUs(timestampUs); - if (!originatorId.empty()) - { - thisEntry["Originator"] = originatorId; - thisEntry["OriginatorType"] = originatorType; - } + if (!originatorId.empty()) + { + thisEntry["Originator"] = originatorId; + thisEntry["OriginatorType"] = originatorType; + } - if (dumpType == "BMC") - { - thisEntry["DiagnosticDataType"] = "Manager"; - thisEntry["AdditionalDataURI"] = entriesPath + entryID + - "/attachment"; - thisEntry["AdditionalDataSizeBytes"] = size; - } - else if (dumpType == "System") - { - thisEntry["DiagnosticDataType"] = "OEM"; - thisEntry["OEMDiagnosticDataType"] = "System"; - thisEntry["AdditionalDataURI"] = entriesPath + entryID + - "/attachment"; - thisEntry["AdditionalDataSizeBytes"] = size; + if (dumpType == "BMC") + { + thisEntry["DiagnosticDataType"] = "Manager"; + thisEntry["AdditionalDataURI"] = + entriesPath + entryID + "/attachment"; + thisEntry["AdditionalDataSizeBytes"] = size; + } + else if (dumpType == "System") + { + thisEntry["DiagnosticDataType"] = "OEM"; + thisEntry["OEMDiagnosticDataType"] = "System"; + thisEntry["AdditionalDataURI"] = + entriesPath + entryID + "/attachment"; + thisEntry["AdditionalDataSizeBytes"] = size; + } + entriesArray.emplace_back(std::move(thisEntry)); } - entriesArray.emplace_back(std::move(thisEntry)); - } - asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); - asyncResp->res.jsonValue["Members"] = std::move(entriesArray); - }); + asyncResp->res.jsonValue["Members@odata.count"] = + entriesArray.size(); + asyncResp->res.jsonValue["Members"] = std::move(entriesArray); + }); } inline void @@ -640,85 +476,86 @@ inline void [asyncResp, entryID, dumpType, entriesPath](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) { - if (ec) - { - BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - bool foundDumpEntry = false; - std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; - - for (const auto& objectPath : resp) - { - if (objectPath.first.str != dumpEntryPath + entryID) - { - continue; - } - - foundDumpEntry = true; - uint64_t timestampUs = 0; - uint64_t size = 0; - std::string dumpStatus; - std::string originatorId; - log_entry::OriginatorTypes originatorType = - log_entry::OriginatorTypes::Internal; - - parseDumpEntryFromDbusObject(objectPath, dumpStatus, size, - timestampUs, originatorId, - originatorType, asyncResp); - - if (dumpStatus != - "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && - !dumpStatus.empty()) + if (ec) { - // Dump status is not Complete - // return not found until status is changed to Completed - messages::resourceNotFound(asyncResp->res, dumpType + " dump", - entryID); + BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec); + messages::internalError(asyncResp->res); return; } - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntry.v1_11_0.LogEntry"; - asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID; - asyncResp->res.jsonValue["Id"] = entryID; - asyncResp->res.jsonValue["EntryType"] = "Event"; - asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry"; - asyncResp->res.jsonValue["Created"] = - redfish::time_utils::getDateTimeUintUs(timestampUs); - - if (!originatorId.empty()) - { - asyncResp->res.jsonValue["Originator"] = originatorId; - asyncResp->res.jsonValue["OriginatorType"] = originatorType; - } + bool foundDumpEntry = false; + std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/"; - if (dumpType == "BMC") + for (const auto& objectPath : resp) { - asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager"; - asyncResp->res.jsonValue["AdditionalDataURI"] = - entriesPath + entryID + "/attachment"; - asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; + if (objectPath.first.str != dumpEntryPath + entryID) + { + continue; + } + + foundDumpEntry = true; + uint64_t timestampUs = 0; + uint64_t size = 0; + std::string dumpStatus; + std::string originatorId; + log_entry::OriginatorTypes originatorType = + log_entry::OriginatorTypes::Internal; + + parseDumpEntryFromDbusObject(objectPath, dumpStatus, size, + timestampUs, originatorId, + originatorType, asyncResp); + + if (dumpStatus != + "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" && + !dumpStatus.empty()) + { + // Dump status is not Complete + // return not found until status is changed to Completed + messages::resourceNotFound(asyncResp->res, + dumpType + " dump", entryID); + return; + } + + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntry.v1_11_0.LogEntry"; + asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID; + asyncResp->res.jsonValue["Id"] = entryID; + asyncResp->res.jsonValue["EntryType"] = "Event"; + asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry"; + asyncResp->res.jsonValue["Created"] = + redfish::time_utils::getDateTimeUintUs(timestampUs); + + if (!originatorId.empty()) + { + asyncResp->res.jsonValue["Originator"] = originatorId; + asyncResp->res.jsonValue["OriginatorType"] = originatorType; + } + + if (dumpType == "BMC") + { + asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager"; + asyncResp->res.jsonValue["AdditionalDataURI"] = + entriesPath + entryID + "/attachment"; + asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; + } + else if (dumpType == "System") + { + asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM"; + asyncResp->res.jsonValue["OEMDiagnosticDataType"] = + "System"; + asyncResp->res.jsonValue["AdditionalDataURI"] = + entriesPath + entryID + "/attachment"; + asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; + } } - else if (dumpType == "System") + if (!foundDumpEntry) { - asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM"; - asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System"; - asyncResp->res.jsonValue["AdditionalDataURI"] = - entriesPath + entryID + "/attachment"; - asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size; + BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); + messages::resourceNotFound(asyncResp->res, dumpType + " dump", + entryID); + return; } - } - if (!foundDumpEntry) - { - BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID); - messages::resourceNotFound(asyncResp->res, dumpType + " dump", - entryID); - return; - } - }); + }); } inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -777,12 +614,11 @@ inline bool checkSizeLimit(int fd, crow::Response& res) } return true; } -inline void - downloadEntryCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& entryID, - const std::string& downloadEntryType, - const boost::system::error_code& ec, - const sdbusplus::message::unix_fd& unixfd) +inline void downloadEntryCallback( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& entryID, const std::string& downloadEntryType, + const boost::system::error_code& ec, + const sdbusplus::message::unix_fd& unixfd) { if (ec.value() == EBADR) { @@ -850,26 +686,25 @@ inline void return; } - std::string dumpEntryPath = std::format("{}/entry/{}", - getDumpPath(dumpType), entryID); + std::string dumpEntryPath = + std::format("{}/entry/{}", getDumpPath(dumpType), entryID); auto downloadDumpEntryHandler = [asyncResp, entryID, dumpType](const boost::system::error_code& ec, const sdbusplus::message::unix_fd& unixfd) { - downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); - }; + downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); + }; crow::connections::systemBus->async_method_call( std::move(downloadDumpEntryHandler), "xyz.openbmc_project.Dump.Manager", dumpEntryPath, "xyz.openbmc_project.Dump.Entry", "GetFileHandle"); } -inline void - downloadEventLogEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& entryID, - const std::string& dumpType) +inline void downloadEventLogEntry( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& entryID, + const std::string& dumpType) { if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { @@ -893,8 +728,8 @@ inline void [asyncResp, entryID, dumpType](const boost::system::error_code& ec, const sdbusplus::message::unix_fd& unixfd) { - downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); - }; + downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd); + }; crow::connections::systemBus->async_method_call( std::move(downloadEventLogEntryHandler), "xyz.openbmc_project.Logging", @@ -942,7 +777,7 @@ inline std::string getDumpEntryPath(const std::string& dumpPath) if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry") { return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + BMCWEB_REDFISH_MANAGER_URI_NAME); } if (dumpPath == "/xyz/openbmc_project/dump/system/entry") { @@ -974,114 +809,121 @@ inline void createDumpTaskCallback( dumpEntryPath{std::move(dumpEntryPath)}, dumpId](const boost::system::error_code& ec, const std::string& introspectXml) { - if (ec) - { - BMCWEB_LOG_ERROR("Introspect call failed with error: {}", - ec.message()); - messages::internalError(asyncResp->res); - return; - } - - // Check if the created dump object has implemented Progress - // interface to track dump completion. If yes, fetch the "Status" - // property of the interface, modify the task state accordingly. - // Else, return task completed. - tinyxml2::XMLDocument doc; - - doc.Parse(introspectXml.data(), introspectXml.size()); - tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) - { - BMCWEB_LOG_ERROR("XML document failed to parse"); - messages::internalError(asyncResp->res); - return; - } - tinyxml2::XMLElement* interfaceNode = - pRoot->FirstChildElement("interface"); - - bool isProgressIntfPresent = false; - while (interfaceNode != nullptr) - { - const char* thisInterfaceName = interfaceNode->Attribute("name"); - if (thisInterfaceName != nullptr) + if (ec) { - if (thisInterfaceName == - std::string_view("xyz.openbmc_project.Common.Progress")) - { - interfaceNode = - interfaceNode->NextSiblingElement("interface"); - continue; - } - isProgressIntfPresent = true; - break; + BMCWEB_LOG_ERROR("Introspect call failed with error: {}", + ec.message()); + messages::internalError(asyncResp->res); + return; } - interfaceNode = interfaceNode->NextSiblingElement("interface"); - } - std::shared_ptr<task::TaskData> task = task::TaskData::createTask( - [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent]( - const boost::system::error_code& ec2, sdbusplus::message_t& msg, - const std::shared_ptr<task::TaskData>& taskData) { - if (ec2) + // Check if the created dump object has implemented Progress + // interface to track dump completion. If yes, fetch the "Status" + // property of the interface, modify the task state accordingly. + // Else, return task completed. + tinyxml2::XMLDocument doc; + + doc.Parse(introspectXml.data(), introspectXml.size()); + tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); + if (pRoot == nullptr) { - BMCWEB_LOG_ERROR("{}: Error in creating dump", - createdObjPath.str); - taskData->messages.emplace_back(messages::internalError()); - taskData->state = "Cancelled"; - return task::completed; + BMCWEB_LOG_ERROR("XML document failed to parse"); + messages::internalError(asyncResp->res); + return; } + tinyxml2::XMLElement* interfaceNode = + pRoot->FirstChildElement("interface"); - if (isProgressIntfPresent) + bool isProgressIntfPresent = false; + while (interfaceNode != nullptr) { - dbus::utility::DBusPropertiesMap values; - std::string prop; - msg.read(prop, values); - - DumpCreationProgress dumpStatus = - getDumpCompletionStatus(values); - if (dumpStatus == DumpCreationProgress::DUMP_CREATE_FAILED) - { - BMCWEB_LOG_ERROR("{}: Error in creating dump", - createdObjPath.str); - taskData->state = "Cancelled"; - return task::completed; - } - - if (dumpStatus == DumpCreationProgress::DUMP_CREATE_INPROGRESS) + const char* thisInterfaceName = + interfaceNode->Attribute("name"); + if (thisInterfaceName != nullptr) { - BMCWEB_LOG_DEBUG("{}: Dump creation task is in progress", - createdObjPath.str); - return !task::completed; + if (thisInterfaceName == + std::string_view("xyz.openbmc_project.Common.Progress")) + { + interfaceNode = + interfaceNode->NextSiblingElement("interface"); + continue; + } + isProgressIntfPresent = true; + break; } + interfaceNode = interfaceNode->NextSiblingElement("interface"); } - nlohmann::json retMessage = messages::success(); - taskData->messages.emplace_back(retMessage); + std::shared_ptr<task::TaskData> task = task::TaskData::createTask( + [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent]( + const boost::system::error_code& ec2, + sdbusplus::message_t& msg, + const std::shared_ptr<task::TaskData>& taskData) { + if (ec2) + { + BMCWEB_LOG_ERROR("{}: Error in creating dump", + createdObjPath.str); + taskData->messages.emplace_back( + messages::internalError()); + taskData->state = "Cancelled"; + return task::completed; + } - boost::urls::url url = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId); + if (isProgressIntfPresent) + { + dbus::utility::DBusPropertiesMap values; + std::string prop; + msg.read(prop, values); + + DumpCreationProgress dumpStatus = + getDumpCompletionStatus(values); + if (dumpStatus == + DumpCreationProgress::DUMP_CREATE_FAILED) + { + BMCWEB_LOG_ERROR("{}: Error in creating dump", + createdObjPath.str); + taskData->state = "Cancelled"; + return task::completed; + } - std::string headerLoc = "Location: "; - headerLoc += url.buffer(); + if (dumpStatus == + DumpCreationProgress::DUMP_CREATE_INPROGRESS) + { + BMCWEB_LOG_DEBUG( + "{}: Dump creation task is in progress", + createdObjPath.str); + return !task::completed; + } + } + + nlohmann::json retMessage = messages::success(); + taskData->messages.emplace_back(retMessage); - taskData->payload->httpHeaders.emplace_back(std::move(headerLoc)); + boost::urls::url url = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId); - BMCWEB_LOG_DEBUG("{}: Dump creation task completed", - createdObjPath.str); - taskData->state = "Completed"; - return task::completed; + std::string headerLoc = "Location: "; + headerLoc += url.buffer(); + + taskData->payload->httpHeaders.emplace_back( + std::move(headerLoc)); + + BMCWEB_LOG_DEBUG("{}: Dump creation task completed", + createdObjPath.str); + taskData->state = "Completed"; + return task::completed; + }, + "type='signal',interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged',path='" + + createdObjPath.str + "'"); + + // The task timer is set to max time limit within which the + // requested dump will be collected. + task->startTimer(std::chrono::minutes(6)); + task->populateResp(asyncResp->res); + task->payload.emplace(payload); }, - "type='signal',interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged',path='" + - createdObjPath.str + "'"); - - // The task timer is set to max time limit within which the - // requested dump will be collected. - task->startTimer(std::chrono::minutes(6)); - task->populateResp(asyncResp->res); - task->payload.emplace(payload); - }, "xyz.openbmc_project.Dump.Manager", createdObjPath, "org.freedesktop.DBus.Introspectable", "Introspect"); } @@ -1172,52 +1014,52 @@ inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, dumpPath](const boost::system::error_code& ec, const sdbusplus::message_t& msg, const sdbusplus::message::object_path& objPath) mutable { - if (ec) - { - BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec); - const sd_bus_error* dbusError = msg.get_error(); - if (dbusError == nullptr) + if (ec) { - messages::internalError(asyncResp->res); - return; - } + BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec); + const sd_bus_error* dbusError = msg.get_error(); + if (dbusError == nullptr) + { + messages::internalError(asyncResp->res); + return; + } - BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}", - dbusError->name, dbusError->message); - if (std::string_view( - "xyz.openbmc_project.Common.Error.NotAllowed") == - dbusError->name) - { - messages::resourceInStandby(asyncResp->res); - return; - } - if (std::string_view( - "xyz.openbmc_project.Dump.Create.Error.Disabled") == - dbusError->name) - { - messages::serviceDisabled(asyncResp->res, dumpPath); - return; - } - if (std::string_view( - "xyz.openbmc_project.Common.Error.Unavailable") == - dbusError->name) - { - messages::resourceInUse(asyncResp->res); + BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}", + dbusError->name, dbusError->message); + if (std::string_view( + "xyz.openbmc_project.Common.Error.NotAllowed") == + dbusError->name) + { + messages::resourceInStandby(asyncResp->res); + return; + } + if (std::string_view( + "xyz.openbmc_project.Dump.Create.Error.Disabled") == + dbusError->name) + { + messages::serviceDisabled(asyncResp->res, dumpPath); + return; + } + if (std::string_view( + "xyz.openbmc_project.Common.Error.Unavailable") == + dbusError->name) + { + messages::resourceInUse(asyncResp->res); + return; + } + // Other Dbus errors such as: + // xyz.openbmc_project.Common.Error.InvalidArgument & + // org.freedesktop.DBus.Error.InvalidArgs are all related to + // the dbus call that is made here in the bmcweb + // implementation and has nothing to do with the client's + // input in the request. Hence, returning internal error + // back to the client. + messages::internalError(asyncResp->res); return; } - // Other Dbus errors such as: - // xyz.openbmc_project.Common.Error.InvalidArgument & - // org.freedesktop.DBus.Error.InvalidArgs are all related to - // the dbus call that is made here in the bmcweb - // implementation and has nothing to do with the client's - // input in the request. Hence, returning internal error - // back to the client. - messages::internalError(asyncResp->res); - return; - } - BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str); - createDumpTaskCallback(std::move(payload), asyncResp, objPath); - }, + BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str); + createDumpTaskCallback(std::move(payload), asyncResp, objPath); + }, "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec); } @@ -1227,21 +1069,20 @@ inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, { crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec); - messages::internalError(asyncResp->res); - return; - } - }, + if (ec) + { + BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec); + messages::internalError(asyncResp->res); + return; + } + }, "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType), "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); } -inline void - parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params, - std::string& filename, std::string& timestamp, - std::string& logfile) +inline void parseCrashdumpParameters( + const dbus::utility::DBusPropertiesMap& params, std::string& filename, + std::string& timestamp, std::string& logfile) { const std::string* filenamePtr = nullptr; const std::string* timestampPtr = nullptr; @@ -1279,189 +1120,192 @@ inline void requestRoutesSystemLogServiceCollection(App& app) */ BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/") .privileges(redfish::privileges::getLogServiceCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - // Collections don't include the static data added by SubRoute - // because it has a duplicate entry for members - asyncResp->res.jsonValue["@odata.type"] = - "#LogServiceCollection.LogServiceCollection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; - asyncResp->res.jsonValue["Description"] = - "Collection of LogServices for this Computer System"; - nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"]; - logServiceArray = nlohmann::json::array(); - nlohmann::json::object_t eventLog; - eventLog["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/EventLog", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - logServiceArray.emplace_back(std::move(eventLog)); - if constexpr (BMCWEB_REDFISH_DUMP_LOG) - { - nlohmann::json::object_t dumpLog; - dumpLog["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Dump", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - logServiceArray.emplace_back(std::move(dumpLog)); - } + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - if constexpr (BMCWEB_REDFISH_CPU_LOG) - { - nlohmann::json::object_t crashdump; - crashdump["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", + // Collections don't include the static data added by SubRoute + // because it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.type"] = + "#LogServiceCollection.LogServiceCollection"; + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices", BMCWEB_REDFISH_SYSTEM_URI_NAME); - logServiceArray.emplace_back(std::move(crashdump)); - } - - if constexpr (BMCWEB_REDFISH_HOST_LOGGER) - { - nlohmann::json::object_t hostlogger; - hostlogger["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", + asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of LogServices for this Computer System"; + nlohmann::json& logServiceArray = + asyncResp->res.jsonValue["Members"]; + logServiceArray = nlohmann::json::array(); + nlohmann::json::object_t eventLog; + eventLog["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/EventLog", BMCWEB_REDFISH_SYSTEM_URI_NAME); - logServiceArray.emplace_back(std::move(hostlogger)); - } - asyncResp->res.jsonValue["Members@odata.count"] = - logServiceArray.size(); - - constexpr std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.State.Boot.PostCode"}; - dbus::utility::getSubTreePaths( - "/", 0, interfaces, - [asyncResp](const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& - subtreePath) { - if (ec) + logServiceArray.emplace_back(std::move(eventLog)); + if constexpr (BMCWEB_REDFISH_DUMP_LOG) { - BMCWEB_LOG_ERROR("{}", ec); - return; + nlohmann::json::object_t dumpLog; + dumpLog["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/Dump", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + logServiceArray.emplace_back(std::move(dumpLog)); } - for (const auto& pathStr : subtreePath) + if constexpr (BMCWEB_REDFISH_CPU_LOG) { - if (pathStr.find("PostCode") != std::string::npos) - { - nlohmann::json& logServiceArrayLocal = - asyncResp->res.jsonValue["Members"]; - nlohmann::json::object_t member; - member["@odata.id"] = std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - - logServiceArrayLocal.emplace_back(std::move(member)); + nlohmann::json::object_t crashdump; + crashdump["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + logServiceArray.emplace_back(std::move(crashdump)); + } - asyncResp->res.jsonValue["Members@odata.count"] = - logServiceArrayLocal.size(); - return; - } + if constexpr (BMCWEB_REDFISH_HOST_LOGGER) + { + nlohmann::json::object_t hostlogger; + hostlogger["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + logServiceArray.emplace_back(std::move(hostlogger)); } + asyncResp->res.jsonValue["Members@odata.count"] = + logServiceArray.size(); + + constexpr std::array<std::string_view, 1> interfaces = { + "xyz.openbmc_project.State.Boot.PostCode"}; + dbus::utility::getSubTreePaths( + "/", 0, interfaces, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& + subtreePath) { + if (ec) + { + BMCWEB_LOG_ERROR("{}", ec); + return; + } + + for (const auto& pathStr : subtreePath) + { + if (pathStr.find("PostCode") != std::string::npos) + { + nlohmann::json& logServiceArrayLocal = + asyncResp->res.jsonValue["Members"]; + nlohmann::json::object_t member; + member["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + + logServiceArrayLocal.emplace_back( + std::move(member)); + + asyncResp->res.jsonValue["Members@odata.count"] = + logServiceArrayLocal.size(); + return; + } + } + }); }); - }); } inline void requestRoutesEventLogService(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/") .privileges(redfish::privileges::getLogService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/EventLog", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = - "#LogService.v1_2_0.LogService"; - asyncResp->res.jsonValue["Name"] = "Event Log Service"; - asyncResp->res.jsonValue["Description"] = "System Event Log Service"; - asyncResp->res.jsonValue["Id"] = "EventLog"; - asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; - - std::pair<std::string, std::string> redfishDateTimeOffset = - redfish::time_utils::getDateTimeOffsetNow(); - - asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; - asyncResp->res.jsonValue["DateTimeLocalOffset"] = - redfishDateTimeOffset.second; - - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/EventLog", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = + "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["Name"] = "Event Log Service"; + asyncResp->res.jsonValue["Description"] = + "System Event Log Service"; + asyncResp->res.jsonValue["Id"] = "EventLog"; + asyncResp->res.jsonValue["OverWritePolicy"] = + log_service::OverWritePolicy::WrapsWhenFull; + + std::pair<std::string, std::string> redfishDateTimeOffset = + redfish::time_utils::getDateTimeOffsetNow(); - = std::format( - "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog", + asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; + asyncResp->res.jsonValue["DateTimeLocalOffset"] = + redfishDateTimeOffset.second; + + asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/EventLog/Entries", BMCWEB_REDFISH_SYSTEM_URI_NAME); - }); + asyncResp->res + .jsonValue["Actions"]["#LogService.ClearLog"]["target"] + + = std::format( + "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + }); } -inline void requestRoutesJournalEventLogClear(App& app) +inline void handleSystemsLogServicesEventLogActionsClearPost( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { - BMCWEB_ROUTE( - app, - "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") - .privileges({{"ConfigureComponents"}}) - .methods(boost::beast::http::verb::post)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - // Clear the EventLog by deleting the log files - std::vector<std::filesystem::path> redfishLogFiles; - if (getRedfishLogFiles(redfishLogFiles)) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + // Clear the EventLog by deleting the log files + std::vector<std::filesystem::path> redfishLogFiles; + if (getRedfishLogFiles(redfishLogFiles)) + { + for (const std::filesystem::path& file : redfishLogFiles) { - for (const std::filesystem::path& file : redfishLogFiles) - { - std::error_code ec; - std::filesystem::remove(file, ec); - } + std::error_code ec; + std::filesystem::remove(file, ec); } + } - // Reload rsyslog so it knows to start new log files - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec) { + // Reload rsyslog so it knows to start new log files + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec) { if (ec) { BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec); @@ -1471,10 +1315,19 @@ inline void requestRoutesJournalEventLogClear(App& app) messages::success(asyncResp->res); }, - "org.freedesktop.systemd1", "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", - "replace"); - }); + "org.freedesktop.systemd1", "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", + "replace"); +} + +inline void requestRoutesJournalEventLogClear(App& app) +{ + BMCWEB_ROUTE( + app, + "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/") + .privileges({{"ConfigureComponents"}}) + .methods(boost::beast::http::verb::post)(std::bind_front( + handleSystemsLogServicesEventLogActionsClearPost, std::ref(app))); } enum class LogParseError @@ -1484,10 +1337,9 @@ enum class LogParseError messageIdNotInRegistry, }; -static LogParseError - fillEventLogEntryJson(const std::string& logEntryID, - const std::string& logEntry, - nlohmann::json::object_t& logEntryJson) +static LogParseError fillEventLogEntryJson( + const std::string& logEntryID, const std::string& logEntry, + nlohmann::json::object_t& logEntryJson) { // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" // First get the Timestamp @@ -1529,8 +1381,8 @@ static LogParseError logEntryFields.end()); messageArgs.resize(message->numberOfArgs); - std::string msg = redfish::registries::fillMessageArgs(messageArgs, - message->message); + std::string msg = + redfish::registries::fillMessageArgs(messageArgs, message->message); if (msg.empty()) { return LogParseError::parseFailed; @@ -1562,194 +1414,318 @@ static LogParseError return LogParseError::success; } -inline void requestRoutesJournalEventLogEntryCollection(App& app) +inline void fillEventLogLogEntryFromPropertyMap( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const dbus::utility::DBusPropertiesMap& resp, + nlohmann::json& objectToFillOut) { - BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") - .privileges(redfish::privileges::getLogEntryCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - query_param::QueryCapabilities capabilities = { - .canDelegateTop = true, - .canDelegateSkip = true, - }; - query_param::Query delegatedQuery; - if (!redfish::setUpRedfishRouteWithDelegation( - app, req, asyncResp, delegatedQuery, capabilities)) + std::optional<DbusEventLogEntry> optEntry = + fillDbusEventLogEntryFromPropertyMap(resp); + + if (!optEntry.has_value()) + { + messages::internalError(asyncResp->res); + return; + } + DbusEventLogEntry entry = optEntry.value(); + + objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + objectToFillOut["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); + objectToFillOut["Name"] = "System Event Log Entry"; + objectToFillOut["Id"] = std::to_string(entry.Id); + objectToFillOut["Message"] = entry.Message; + objectToFillOut["Resolved"] = entry.Resolved; + std::optional<bool> notifyAction = + getProviderNotifyAction(entry.ServiceProviderNotify); + if (notifyAction) + { + objectToFillOut["ServiceProviderNotified"] = *notifyAction; + } + if ((entry.Resolution != nullptr) && !entry.Resolution->empty()) + { + objectToFillOut["Resolution"] = *entry.Resolution; + } + objectToFillOut["EntryType"] = "Event"; + objectToFillOut["Severity"] = + translateSeverityDbusToRedfish(entry.Severity); + objectToFillOut["Created"] = + redfish::time_utils::getDateTimeUintMs(entry.Timestamp); + objectToFillOut["Modified"] = + redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp); + if (entry.Path != nullptr) + { + objectToFillOut["AdditionalDataURI"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment", + BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id)); + } +} + +inline void afterLogEntriesGetManagedObjects( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& resp) +{ + if (ec) + { + // TODO Handle for specific error code + BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}", + ec); + messages::internalError(asyncResp->res); + return; + } + nlohmann::json::array_t entriesArray; + for (const auto& objectPath : resp) + { + dbus::utility::DBusPropertiesMap propsFlattened; + auto isEntry = + std::ranges::find_if(objectPath.second, [](const auto& object) { + return object.first == "xyz.openbmc_project.Logging.Entry"; + }); + if (isEntry == objectPath.second.end()) { - return; + continue; } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + for (const auto& interfaceMap : objectPath.second) { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; + for (const auto& propertyMap : interfaceMap.second) + { + propsFlattened.emplace_back(propertyMap.first, + propertyMap.second); + } } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + fillEventLogLogEntryFromPropertyMap(asyncResp, propsFlattened, + entriesArray.emplace_back()); + } + + std::ranges::sort(entriesArray, [](const nlohmann::json& left, + const nlohmann::json& right) { + return (left["Id"] <= right["Id"]); + }); + asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size(); + asyncResp->res.jsonValue["Members"] = std::move(entriesArray); +} + +inline void handleSystemsLogServiceEventLogLogEntryCollection( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + query_param::QueryCapabilities capabilities = { + .canDelegateTop = true, + .canDelegateSkip = true, + }; + query_param::Query delegatedQuery; + if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, + delegatedQuery, capabilities)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); + size_t skip = delegatedQuery.skip.value_or(0); + + // Collections don't include the static data added by SubRoute + // because it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of System Event Log Entries"; + + nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; + logEntryArray = nlohmann::json::array(); + // Go through the log files and create a unique ID for each + // entry + std::vector<std::filesystem::path> redfishLogFiles; + getRedfishLogFiles(redfishLogFiles); + uint64_t entryCount = 0; + std::string logEntry; + + // Oldest logs are in the last file, so start there and loop + // backwards + for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) + { + std::ifstream logStream(*it); + if (!logStream.is_open()) { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; + continue; } - size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); - size_t skip = delegatedQuery.skip.value_or(0); - - // Collections don't include the static data added by SubRoute - // because it has a duplicate entry for members - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of System Event Log Entries"; - - nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; - logEntryArray = nlohmann::json::array(); - // Go through the log files and create a unique ID for each - // entry - std::vector<std::filesystem::path> redfishLogFiles; - getRedfishLogFiles(redfishLogFiles); - uint64_t entryCount = 0; - std::string logEntry; - - // Oldest logs are in the last file, so start there and loop - // backwards - for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); - it++) + // Reset the unique ID on the first entry + bool firstEntry = true; + while (std::getline(logStream, logEntry)) { - std::ifstream logStream(*it); - if (!logStream.is_open()) + std::string idStr; + if (!getUniqueEntryID(logEntry, idStr, firstEntry)) { continue; } + firstEntry = false; - // Reset the unique ID on the first entry - bool firstEntry = true; - while (std::getline(logStream, logEntry)) + nlohmann::json::object_t bmcLogEntry; + LogParseError status = + fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); + if (status == LogParseError::messageIdNotInRegistry) { - std::string idStr; - if (!getUniqueEntryID(logEntry, idStr, firstEntry)) - { - continue; - } - firstEntry = false; - - nlohmann::json::object_t bmcLogEntry; - LogParseError status = fillEventLogEntryJson(idStr, logEntry, - bmcLogEntry); - if (status == LogParseError::messageIdNotInRegistry) - { - continue; - } - if (status != LogParseError::success) - { - messages::internalError(asyncResp->res); - return; - } - - entryCount++; - // Handle paging using skip (number of entries to skip from the - // start) and top (number of entries to display) - if (entryCount <= skip || entryCount > skip + top) - { - continue; - } + continue; + } + if (status != LogParseError::success) + { + messages::internalError(asyncResp->res); + return; + } - logEntryArray.emplace_back(std::move(bmcLogEntry)); + entryCount++; + // Handle paging using skip (number of entries to skip from the + // start) and top (number of entries to display) + if (entryCount <= skip || entryCount > skip + top) + { + continue; } + + logEntryArray.emplace_back(std::move(bmcLogEntry)); } - asyncResp->res.jsonValue["Members@odata.count"] = entryCount; - if (skip + top < entryCount) - { - asyncResp->res - .jsonValue["Members@odata.nextLink"] = boost::urls::format( + } + asyncResp->res.jsonValue["Members@odata.count"] = entryCount; + if (skip + top < entryCount) + { + asyncResp->res.jsonValue["Members@odata.nextLink"] = + boost::urls::format( "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}", BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top)); - } - }); + } } -inline void requestRoutesJournalEventLogEntry(App& app) +inline void requestRoutesJournalEventLogEntryCollection(App& app) { - BMCWEB_ROUTE( - app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/") + .privileges(redfish::privileges::getLogEntryCollection) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app))); +} - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } +inline void handleSystemsLogServiceEventLogEntriesGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& param) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - const std::string& targetID = param; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + const std::string& targetID = param; - // Go through the log files and check the unique ID for each - // entry to find the target entry - std::vector<std::filesystem::path> redfishLogFiles; - getRedfishLogFiles(redfishLogFiles); - std::string logEntry; + // Go through the log files and check the unique ID for each + // entry to find the target entry + std::vector<std::filesystem::path> redfishLogFiles; + getRedfishLogFiles(redfishLogFiles); + std::string logEntry; - // Oldest logs are in the last file, so start there and loop - // backwards - for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); - it++) + // Oldest logs are in the last file, so start there and loop + // backwards + for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++) + { + std::ifstream logStream(*it); + if (!logStream.is_open()) { - std::ifstream logStream(*it); - if (!logStream.is_open()) + continue; + } + + // Reset the unique ID on the first entry + bool firstEntry = true; + while (std::getline(logStream, logEntry)) + { + std::string idStr; + if (!getUniqueEntryID(logEntry, idStr, firstEntry)) { continue; } + firstEntry = false; - // Reset the unique ID on the first entry - bool firstEntry = true; - while (std::getline(logStream, logEntry)) + if (idStr == targetID) { - std::string idStr; - if (!getUniqueEntryID(logEntry, idStr, firstEntry)) - { - continue; - } - firstEntry = false; - - if (idStr == targetID) + nlohmann::json::object_t bmcLogEntry; + LogParseError status = + fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); + if (status != LogParseError::success) { - nlohmann::json::object_t bmcLogEntry; - LogParseError status = - fillEventLogEntryJson(idStr, logEntry, bmcLogEntry); - if (status != LogParseError::success) - { - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue.update(bmcLogEntry); + messages::internalError(asyncResp->res); return; } + asyncResp->res.jsonValue.update(bmcLogEntry); + return; } } - // Requested ID was not found - messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); - }); + } + // Requested ID was not found + messages::resourceNotFound(asyncResp->res, "LogEntry", targetID); +} + +inline void requestRoutesJournalEventLogEntry(App& app) +{ + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServiceEventLogEntriesGet, std::ref(app))); +} + +inline void dBusEventLogEntryCollection( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +{ + // Collections don't include the static data added by SubRoute + // because it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of System Event Log Entries"; + + // DBus implementation of EventLog/Entries + // Make call to Logging Service to find all log entry objects + sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); + dbus::utility::getManagedObjects( + "xyz.openbmc_project.Logging", path, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& resp) { + afterLogEntriesGetManagedObjects(asyncResp, ec, resp); + }); } inline void requestRoutesDBusEventLogEntryCollection(App& app) @@ -1760,234 +1736,39 @@ inline void requestRoutesDBusEventLogEntryCollection(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - // Collections don't include the static data added by SubRoute - // because it has a duplicate entry for members - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of System Event Log Entries"; - - // DBus implementation of EventLog/Entries - // Make call to Logging Service to find all log entry objects - sdbusplus::message::object_path path("/xyz/openbmc_project/logging"); - dbus::utility::getManagedObjects( - "xyz.openbmc_project.Logging", path, - [asyncResp](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& resp) { - if (ec) - { - // TODO Handle for specific error code - BMCWEB_LOG_ERROR( - "getLogEntriesIfaceData resp_handler got error {}", ec); - messages::internalError(asyncResp->res); - return; - } - nlohmann::json::array_t entriesArray; - for (const auto& objectPath : resp) - { - const uint32_t* id = nullptr; - const uint64_t* timestamp = nullptr; - const uint64_t* updateTimestamp = nullptr; - const std::string* severity = nullptr; - const std::string* message = nullptr; - const std::string* filePath = nullptr; - const std::string* resolution = nullptr; - bool resolved = false; - const std::string* notify = nullptr; - - for (const auto& interfaceMap : objectPath.second) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - if (interfaceMap.first == - "xyz.openbmc_project.Logging.Entry") - { - for (const auto& propertyMap : interfaceMap.second) - { - if (propertyMap.first == "Id") - { - id = std::get_if<uint32_t>(&propertyMap.second); - } - else if (propertyMap.first == "Timestamp") - { - timestamp = - std::get_if<uint64_t>(&propertyMap.second); - } - else if (propertyMap.first == "UpdateTimestamp") - { - updateTimestamp = - std::get_if<uint64_t>(&propertyMap.second); - } - else if (propertyMap.first == "Severity") - { - severity = std::get_if<std::string>( - &propertyMap.second); - } - else if (propertyMap.first == "Resolution") - { - resolution = std::get_if<std::string>( - &propertyMap.second); - } - else if (propertyMap.first == "Message") - { - message = std::get_if<std::string>( - &propertyMap.second); - } - else if (propertyMap.first == "Resolved") - { - const bool* resolveptr = - std::get_if<bool>(&propertyMap.second); - if (resolveptr == nullptr) - { - messages::internalError(asyncResp->res); - return; - } - resolved = *resolveptr; - } - else if (propertyMap.first == - "ServiceProviderNotify") - { - notify = std::get_if<std::string>( - &propertyMap.second); - if (notify == nullptr) - { - messages::internalError(asyncResp->res); - return; - } - } - } - if (id == nullptr || message == nullptr || - severity == nullptr) - { - messages::internalError(asyncResp->res); - return; - } - } - else if (interfaceMap.first == - "xyz.openbmc_project.Common.FilePath") - { - for (const auto& propertyMap : interfaceMap.second) - { - if (propertyMap.first == "Path") - { - filePath = std::get_if<std::string>( - &propertyMap.second); - } - } - } - } - // Object path without the - // xyz.openbmc_project.Logging.Entry interface, ignore - // and continue. - if (id == nullptr || message == nullptr || - severity == nullptr || timestamp == nullptr || - updateTimestamp == nullptr) - { - continue; - } - nlohmann::json& thisEntry = entriesArray.emplace_back(); - thisEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; - thisEntry["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(*id)); - thisEntry["Name"] = "System Event Log Entry"; - thisEntry["Id"] = std::to_string(*id); - thisEntry["Message"] = *message; - thisEntry["Resolved"] = resolved; - if ((resolution != nullptr) && (!(*resolution).empty())) - { - thisEntry["Resolution"] = *resolution; + return; } - std::optional<bool> notifyAction = - getProviderNotifyAction(*notify); - if (notifyAction) + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - thisEntry["ServiceProviderNotified"] = *notifyAction; + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; } - thisEntry["EntryType"] = "Event"; - thisEntry["Severity"] = - translateSeverityDbusToRedfish(*severity); - thisEntry["Created"] = - redfish::time_utils::getDateTimeUintMs(*timestamp); - thisEntry["Modified"] = - redfish::time_utils::getDateTimeUintMs(*updateTimestamp); - if (filePath != nullptr) + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { - thisEntry["AdditionalDataURI"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - std::to_string(*id) + "/attachment"; + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; } - } - std::ranges::sort(entriesArray, [](const nlohmann::json& left, - const nlohmann::json& right) { - return (left["Id"] <= right["Id"]); + dBusEventLogEntryCollection(asyncResp); }); - asyncResp->res.jsonValue["Members@odata.count"] = - entriesArray.size(); - asyncResp->res.jsonValue["Members"] = std::move(entriesArray); - }); - }); } -inline void requestRoutesDBusEventLogEntry(App& app) +inline void dBusEventLogEntryGet( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) { - BMCWEB_ROUTE( - app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - std::string entryID = param; - dbus::utility::escapePathForDbus(entryID); + dbus::utility::escapePathForDbus(entryID); - // DBus implementation of EventLog/Entries - // Make call to Logging Service to find all log entry objects - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging/entry/" + entryID, "", - [asyncResp, entryID](const boost::system::error_code& ec, - const dbus::utility::DBusPropertiesMap& resp) { + // DBus implementation of EventLog/Entries + // Make call to Logging Service to find all log entry objects + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging/entry/" + entryID, "", + [asyncResp, entryID](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& resp) { if (ec.value() == EBADR) { messages::resourceNotFound(asyncResp->res, "EventLogEntry", @@ -2001,73 +1782,96 @@ inline void requestRoutesDBusEventLogEntry(App& app) messages::internalError(asyncResp->res); return; } - const uint32_t* id = nullptr; - const uint64_t* timestamp = nullptr; - const uint64_t* updateTimestamp = nullptr; - const std::string* severity = nullptr; - const std::string* message = nullptr; - const std::string* filePath = nullptr; - const std::string* resolution = nullptr; - bool resolved = false; - const std::string* notify = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), resp, "Id", id, "Timestamp", - timestamp, "UpdateTimestamp", updateTimestamp, "Severity", - severity, "Message", message, "Resolved", resolved, - "Resolution", resolution, "Path", filePath, - "ServiceProviderNotify", notify); - - if (!success) - { - messages::internalError(asyncResp->res); - return; - } - if (id == nullptr || message == nullptr || severity == nullptr || - timestamp == nullptr || updateTimestamp == nullptr || - notify == nullptr) + fillEventLogLogEntryFromPropertyMap(asyncResp, resp, + asyncResp->res.jsonValue); + }); +} + +inline void + dBusEventLogEntryPatch(const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& entryId) +{ + std::optional<bool> resolved; + + if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved)) + { + return; + } + BMCWEB_LOG_DEBUG("Set Resolved"); + + setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging/entry/" + entryId, + "xyz.openbmc_project.Logging.Entry", "Resolved", + resolved.value_or(false)); +} + +inline void dBusEventLogEntryDelete( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID) +{ + BMCWEB_LOG_DEBUG("Do delete single event entries."); + + dbus::utility::escapePathForDbus(entryID); + + // Process response from Logging service. + auto respHandler = [asyncResp, + entryID](const boost::system::error_code& ec) { + BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); + if (ec) + { + if (ec.value() == EBADR) { - messages::internalError(asyncResp->res); + messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); return; } + // TODO Handle for specific error code + BMCWEB_LOG_ERROR( + "EventLogEntry (DBus) doDelete respHandler got error {}", ec); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntry.v1_9_0.LogEntry"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(*id)); - asyncResp->res.jsonValue["Name"] = "System Event Log Entry"; - asyncResp->res.jsonValue["Id"] = std::to_string(*id); - asyncResp->res.jsonValue["Message"] = *message; - asyncResp->res.jsonValue["Resolved"] = resolved; - std::optional<bool> notifyAction = getProviderNotifyAction(*notify); - if (notifyAction) - { - asyncResp->res.jsonValue["ServiceProviderNotified"] = - *notifyAction; - } - if ((resolution != nullptr) && (!(*resolution).empty())) - { - asyncResp->res.jsonValue["Resolution"] = *resolution; - } - asyncResp->res.jsonValue["EntryType"] = "Event"; - asyncResp->res.jsonValue["Severity"] = - translateSeverityDbusToRedfish(*severity); - asyncResp->res.jsonValue["Created"] = - redfish::time_utils::getDateTimeUintMs(*timestamp); - asyncResp->res.jsonValue["Modified"] = - redfish::time_utils::getDateTimeUintMs(*updateTimestamp); - if (filePath != nullptr) - { - asyncResp->res.jsonValue["AdditionalDataURI"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - std::to_string(*id) + "/attachment"; - } - }); - }); + asyncResp->res.result(boost::beast::http::status::ok); + }; + + // Make call to Logging service to request Delete Log + crow::connections::systemBus->async_method_call( + respHandler, "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging/entry/" + entryID, + "xyz.openbmc_project.Object.Delete", "Delete"); +} + +inline void requestRoutesDBusEventLogEntry(App& app) +{ + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)( + [&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& entryId) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + dBusEventLogEntryGet(asyncResp, entryId); + }); BMCWEB_ROUTE( app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") @@ -2076,37 +1880,26 @@ inline void requestRoutesDBusEventLogEntry(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& entryId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - std::optional<bool> resolved; - - if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", - resolved)) - { - return; - } - BMCWEB_LOG_DEBUG("Set Resolved"); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging/entry/" + entryId, - "xyz.openbmc_project.Logging.Entry", "Resolved", - *resolved); - }); + dBusEventLogEntryPatch(req, asyncResp, entryId); + }); BMCWEB_ROUTE( app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/") @@ -2116,59 +1909,25 @@ inline void requestRoutesDBusEventLogEntry(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - BMCWEB_LOG_DEBUG("Do delete single event entries."); - - std::string entryID = param; - - dbus::utility::escapePathForDbus(entryID); - - // Process response from Logging service. - auto respHandler = [asyncResp, - entryID](const boost::system::error_code& ec) { - BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done"); - if (ec) - { - if (ec.value() == EBADR) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - messages::resourceNotFound(asyncResp->res, "LogEntry", - entryID); return; } - // TODO Handle for specific error code - BMCWEB_LOG_ERROR( - "EventLogEntry (DBus) doDelete respHandler got error {}", - ec); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - asyncResp->res.result(boost::beast::http::status::ok); - }; - - // Make call to Logging service to request Delete Log - crow::connections::systemBus->async_method_call( - respHandler, "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging/entry/" + entryID, - "xyz.openbmc_project.Object.Delete", "Delete"); - }); + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + dBusEventLogEntryDelete(asyncResp, param); + }); } constexpr const char* hostLoggerFolderPath = "/var/log/console"; @@ -2231,226 +1990,6 @@ inline bool getHostLoggerEntries( return true; } -inline void fillHostLoggerEntryJson(std::string_view logEntryID, - std::string_view msg, - nlohmann::json::object_t& logEntryJson) -{ - // Fill in the log entry with the gathered data. - logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; - logEntryJson["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); - logEntryJson["Name"] = "Host Logger Entry"; - logEntryJson["Id"] = logEntryID; - logEntryJson["Message"] = msg; - logEntryJson["EntryType"] = "Oem"; - logEntryJson["Severity"] = "OK"; - logEntryJson["OemRecordFormat"] = "Host Logger Entry"; -} - -inline void requestRoutesSystemHostLogger(App& app) -{ - BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/") - .privileges(redfish::privileges::getLogService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = - "#LogService.v1_2_0.LogService"; - asyncResp->res.jsonValue["Name"] = "Host Logger Service"; - asyncResp->res.jsonValue["Description"] = "Host Logger Service"; - asyncResp->res.jsonValue["Id"] = "HostLogger"; - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - }); -} - -inline void requestRoutesSystemHostLoggerCollection(App& app) -{ - BMCWEB_ROUTE(app, - "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - query_param::QueryCapabilities capabilities = { - .canDelegateTop = true, - .canDelegateSkip = true, - }; - query_param::Query delegatedQuery; - if (!redfish::setUpRedfishRouteWithDelegation( - app, req, asyncResp, delegatedQuery, capabilities)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["Name"] = "HostLogger Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of HostLogger Entries"; - nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; - logEntryArray = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = 0; - - std::vector<std::filesystem::path> hostLoggerFiles; - if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) - { - BMCWEB_LOG_DEBUG("Failed to get host log file path"); - return; - } - // If we weren't provided top and skip limits, use the defaults. - size_t skip = delegatedQuery.skip.value_or(0); - size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); - size_t logCount = 0; - // This vector only store the entries we want to expose that - // control by skip and top. - std::vector<std::string> logEntries; - if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries, - logCount)) - { - messages::internalError(asyncResp->res); - return; - } - // If vector is empty, that means skip value larger than total - // log count - if (logEntries.empty()) - { - asyncResp->res.jsonValue["Members@odata.count"] = logCount; - return; - } - if (!logEntries.empty()) - { - for (size_t i = 0; i < logEntries.size(); i++) - { - nlohmann::json::object_t hostLogEntry; - fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i], - hostLogEntry); - logEntryArray.emplace_back(std::move(hostLogEntry)); - } - - asyncResp->res.jsonValue["Members@odata.count"] = logCount; - if (skip + top < logCount) - { - asyncResp->res.jsonValue["Members@odata.nextLink"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries?$skip=", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - std::to_string(skip + top); - } - } - }); -} - -inline void requestRoutesSystemHostLoggerLogEntry(App& app) -{ - BMCWEB_ROUTE( - app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - std::string_view targetID = param; - - uint64_t idInt = 0; - - auto [ptr, ec] = std::from_chars(targetID.begin(), targetID.end(), - idInt); - if (ec != std::errc{} || ptr != targetID.end()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", param); - return; - } - - std::vector<std::filesystem::path> hostLoggerFiles; - if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) - { - BMCWEB_LOG_DEBUG("Failed to get host log file path"); - return; - } - - size_t logCount = 0; - size_t top = 1; - std::vector<std::string> logEntries; - // We can get specific entry by skip and top. For example, if we - // want to get nth entry, we can set skip = n-1 and top = 1 to - // get that entry - if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries, - logCount)) - { - messages::internalError(asyncResp->res); - return; - } - - if (!logEntries.empty()) - { - nlohmann::json::object_t hostLogEntry; - fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry); - asyncResp->res.jsonValue.update(hostLogEntry); - return; - } - - // Requested ID was not found - messages::resourceNotFound(asyncResp->res, "LogEntry", param); - }); -} - inline void handleBMCLogServicesCollectionGet( crow::App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -2499,42 +2038,43 @@ inline void handleBMCLogServicesCollectionGet( [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { - if (ec) - { - BMCWEB_LOG_ERROR( - "handleBMCLogServicesCollectionGet respHandler got error {}", - ec); - // Assume that getting an error simply means there are no dump - // LogServices. Return without adding any error response. - return; - } - - nlohmann::json& logServiceArrayLocal = - asyncResp->res.jsonValue["Members"]; - - for (const std::string& path : subTreePaths) - { - if (path == "/xyz/openbmc_project/dump/bmc") + if (ec) { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Dump", - BMCWEB_REDFISH_MANAGER_URI_NAME); - logServiceArrayLocal.emplace_back(std::move(member)); + BMCWEB_LOG_ERROR( + "handleBMCLogServicesCollectionGet respHandler got error {}", + ec); + // Assume that getting an error simply means there are no + // dump LogServices. Return without adding any error + // response. + return; } - else if (path == "/xyz/openbmc_project/dump/faultlog") + + nlohmann::json& logServiceArrayLocal = + asyncResp->res.jsonValue["Members"]; + + for (const std::string& path : subTreePaths) { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/FaultLog", - BMCWEB_REDFISH_MANAGER_URI_NAME); - logServiceArrayLocal.emplace_back(std::move(member)); + if (path == "/xyz/openbmc_project/dump/bmc") + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Dump", + BMCWEB_REDFISH_MANAGER_URI_NAME); + logServiceArrayLocal.emplace_back(std::move(member)); + } + else if (path == "/xyz/openbmc_project/dump/faultlog") + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/FaultLog", + BMCWEB_REDFISH_MANAGER_URI_NAME); + logServiceArrayLocal.emplace_back(std::move(member)); + } } - } - asyncResp->res.jsonValue["Members@odata.count"] = - logServiceArrayLocal.size(); - }); + asyncResp->res.jsonValue["Members@odata.count"] = + logServiceArrayLocal.size(); + }); } } @@ -2546,318 +2086,34 @@ inline void requestRoutesBMCLogServiceCollection(App& app) std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app))); } -inline void requestRoutesBMCJournalLogService(App& app) -{ - BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/") - .privileges(redfish::privileges::getLogService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } - - asyncResp->res.jsonValue["@odata.type"] = - "#LogService.v1_2_0.LogService"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; - asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; - asyncResp->res.jsonValue["Id"] = "Journal"; - asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; - - std::pair<std::string, std::string> redfishDateTimeOffset = - redfish::time_utils::getDateTimeOffsetNow(); - asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; - asyncResp->res.jsonValue["DateTimeLocalOffset"] = - redfishDateTimeOffset.second; - - asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Journal/Entries", - BMCWEB_REDFISH_MANAGER_URI_NAME); - }); -} - -static int - fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID, - sd_journal* journal, - nlohmann::json::object_t& bmcJournalLogEntryJson) -{ - // Get the Log Entry contents - int ret = 0; - - std::string message; - std::string_view syslogID; - ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID); - if (ret < 0) - { - BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", - strerror(-ret)); - } - if (!syslogID.empty()) - { - message += std::string(syslogID) + ": "; - } - - std::string_view msg; - ret = getJournalMetadata(journal, "MESSAGE", msg); - if (ret < 0) - { - BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", strerror(-ret)); - return 1; - } - message += std::string(msg); - - // Get the severity from the PRIORITY field - long int severity = 8; // Default to an invalid priority - ret = getJournalMetadata(journal, "PRIORITY", 10, severity); - if (ret < 0) - { - BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", strerror(-ret)); - } - - // Get the Created time from the timestamp - std::string entryTimeStr; - if (!getEntryTimestamp(journal, entryTimeStr)) - { - return 1; - } - - // Fill in the log entry with the gathered data - bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; - bmcJournalLogEntryJson["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, bmcJournalLogEntryID); - bmcJournalLogEntryJson["Name"] = "BMC Journal Entry"; - bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID; - bmcJournalLogEntryJson["Message"] = std::move(message); - bmcJournalLogEntryJson["EntryType"] = "Oem"; - log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK; - if (severity <= 2) - { - severityEnum = log_entry::EventSeverity::Critical; - } - else if (severity <= 4) - { - severityEnum = log_entry::EventSeverity::Warning; - } - - bmcJournalLogEntryJson["Severity"] = severityEnum; - bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry"; - bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr); - return 0; -} - -inline void requestRoutesBMCJournalLogEntryCollection(App& app) -{ - BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/") - .privileges(redfish::privileges::getLogEntryCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& managerId) { - query_param::QueryCapabilities capabilities = { - .canDelegateTop = true, - .canDelegateSkip = true, - }; - query_param::Query delegatedQuery; - if (!redfish::setUpRedfishRouteWithDelegation( - app, req, asyncResp, delegatedQuery, capabilities)) - { - return; - } - - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } - - size_t skip = delegatedQuery.skip.value_or(0); - size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); - - // Collections don't include the static data added by SubRoute - // because it has a duplicate entry for members - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Journal/Entries", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of BMC Journal Entries"; - nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; - logEntryArray = nlohmann::json::array(); - - // Go through the journal and use the timestamp to create a - // unique ID for each entry - sd_journal* journalTmp = nullptr; - int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); - if (ret < 0) - { - BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); - messages::internalError(asyncResp->res); - return; - } - std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( - journalTmp, sd_journal_close); - journalTmp = nullptr; - uint64_t entryCount = 0; - // Reset the unique ID on the first entry - bool firstEntry = true; - SD_JOURNAL_FOREACH(journal.get()) - { - entryCount++; - // Handle paging using skip (number of entries to skip from - // the start) and top (number of entries to display) - if (entryCount <= skip || entryCount > skip + top) - { - continue; - } - - std::string idStr; - if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) - { - continue; - } - firstEntry = false; - - nlohmann::json::object_t bmcJournalLogEntry; - if (fillBMCJournalLogEntryJson(idStr, journal.get(), - bmcJournalLogEntry) != 0) - { - messages::internalError(asyncResp->res); - return; - } - logEntryArray.emplace_back(std::move(bmcJournalLogEntry)); - } - asyncResp->res.jsonValue["Members@odata.count"] = entryCount; - if (skip + top < entryCount) - { - asyncResp->res - .jsonValue["Members@odata.nextLink"] = boost::urls::format( - "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}", - BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top)); - } - }); -} - -inline void requestRoutesBMCJournalLogEntry(App& app) -{ - BMCWEB_ROUTE( - app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& managerId, const std::string& entryID) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } - - // Convert the unique ID back to a timestamp to find the entry - sd_id128_t bootID{}; - uint64_t ts = 0; - uint64_t index = 0; - if (!getTimestampFromID(asyncResp, entryID, bootID, ts, index)) - { - return; - } - - sd_journal* journalTmp = nullptr; - int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); - if (ret < 0) - { - BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); - messages::internalError(asyncResp->res); - return; - } - std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( - journalTmp, sd_journal_close); - journalTmp = nullptr; - // Go to the timestamp in the log and move to the entry at the - // index tracking the unique ID - std::string idStr; - bool firstEntry = true; - ret = sd_journal_seek_monotonic_usec(journal.get(), bootID, ts); - if (ret < 0) - { - BMCWEB_LOG_ERROR("failed to seek to an entry in journal{}", - strerror(-ret)); - messages::internalError(asyncResp->res); - return; - } - for (uint64_t i = 0; i <= index; i++) - { - sd_journal_next(journal.get()); - if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) - { - messages::internalError(asyncResp->res); - return; - } - firstEntry = false; - } - // Confirm that the entry ID matches what was requested - if (idStr != entryID) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryID); - return; - } - - nlohmann::json::object_t bmcJournalLogEntry; - if (fillBMCJournalLogEntryJson(entryID, journal.get(), - bmcJournalLogEntry) != 0) - { - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue.update(bmcJournalLogEntry); - }); -} - inline void getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& dumpType) { std::string dumpPath; - std::string overWritePolicy; + log_service::OverWritePolicy overWritePolicy = + log_service::OverWritePolicy::Invalid; bool collectDiagnosticDataSupported = false; if (dumpType == "BMC") { dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump", BMCWEB_REDFISH_MANAGER_URI_NAME); - overWritePolicy = "WrapsWhenFull"; + overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; collectDiagnosticDataSupported = true; } else if (dumpType == "FaultLog") { dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog", BMCWEB_REDFISH_MANAGER_URI_NAME); - overWritePolicy = "Unknown"; + overWritePolicy = log_service::OverWritePolicy::Unknown; collectDiagnosticDataSupported = false; } else if (dumpType == "System") { dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump", BMCWEB_REDFISH_SYSTEM_URI_NAME); - overWritePolicy = "WrapsWhenFull"; + overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull; collectDiagnosticDataSupported = true; } else @@ -2873,7 +2129,7 @@ inline void asyncResp->res.jsonValue["Name"] = "Dump LogService"; asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService"; asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename(); - asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy); + asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy; std::pair<std::string, std::string> redfishDateTimeOffset = redfish::time_utils::getDateTimeOffsetNow(); @@ -2896,25 +2152,26 @@ inline void [asyncResp, dumpType, dumpPath]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) { - if (ec) - { - BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", ec); - // Assume that getting an error simply means there are no dump - // LogServices. Return without adding any error response. - return; - } - std::string dbusDumpPath = getDumpPath(dumpType); - for (const std::string& path : subTreePaths) - { - if (path == dbusDumpPath) + if (ec) + { + BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}", + ec); + // Assume that getting an error simply means there are no dump + // LogServices. Return without adding any error response. + return; + } + std::string dbusDumpPath = getDumpPath(dumpType); + for (const std::string& path : subTreePaths) { - asyncResp->res - .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = - dumpPath + "/Actions/LogService.ClearLog"; - break; + if (path == dbusDumpPath) + { + asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] + ["target"] = + dumpPath + "/Actions/LogService.ClearLog"; + break; + } } - } - }); + }); } inline void handleLogServicesDumpServiceGet( @@ -3360,59 +2617,62 @@ inline void requestRoutesCrashdumpService(App& app) // This is incorrect, should be: //.privileges(redfish::privileges::getLogService) .privileges({{"ConfigureManager"}}) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - // Copy over the static data to include the entries added by - // SubRoute - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = - "#LogService.v1_2_0.LogService"; - asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; - asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; - asyncResp->res.jsonValue["Id"] = "Crashdump"; - asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; - asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; - - std::pair<std::string, std::string> redfishDateTimeOffset = - redfish::time_utils::getDateTimeOffsetNow(); - asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; - asyncResp->res.jsonValue["DateTimeLocalOffset"] = - redfishDateTimeOffset.second; - - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] - ["target"] = std::format( - "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"] - ["target"] = std::format( - "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - }); + // Copy over the static data to include the entries added by + // SubRoute + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = + "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; + asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; + asyncResp->res.jsonValue["Id"] = "Crashdump"; + asyncResp->res.jsonValue["OverWritePolicy"] = + log_service::OverWritePolicy::WrapsWhenFull; + asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; + + std::pair<std::string, std::string> redfishDateTimeOffset = + redfish::time_utils::getDateTimeOffsetNow(); + asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; + asyncResp->res.jsonValue["DateTimeLocalOffset"] = + redfishDateTimeOffset.second; + + asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] + ["target"] = std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res + .jsonValue["Actions"]["#LogService.CollectDiagnosticData"] + ["target"] = std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + }); } void inline requestRoutesCrashdumpClear(App& app) @@ -3427,38 +2687,39 @@ void inline requestRoutesCrashdumpClear(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec, - const std::string&) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - messages::success(asyncResp->res); - }, - crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec, + const std::string&) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + messages::success(asyncResp->res); + }, + crashdumpObject, crashdumpPath, deleteAllInterface, + "DeleteAll"); + }); } -static void +inline void logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& logID, nlohmann::json& logEntryJson) { @@ -3466,63 +2727,65 @@ static void [asyncResp, logID, &logEntryJson](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& params) { - if (ec) - { - BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); - if (ec.value() == - boost::system::linux_error::bad_request_descriptor) + if (ec) + { + BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); + if (ec.value() == + boost::system::linux_error::bad_request_descriptor) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", + logID); + } + else + { + messages::internalError(asyncResp->res); + } + return; + } + + std::string timestamp{}; + std::string filename{}; + std::string logfile{}; + parseCrashdumpParameters(params, filename, timestamp, logfile); + + if (filename.empty() || timestamp.empty()) { messages::resourceNotFound(asyncResp->res, "LogEntry", logID); + return; + } + + std::string crashdumpURI = + std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", + BMCWEB_REDFISH_SYSTEM_URI_NAME) + + logID + "/" + filename; + nlohmann::json::object_t logEntry; + logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + logEntry["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); + logEntry["Name"] = "CPU Crashdump"; + logEntry["Id"] = logID; + logEntry["EntryType"] = log_entry::LogEntryType::Oem; + logEntry["AdditionalDataURI"] = std::move(crashdumpURI); + logEntry["DiagnosticDataType"] = "OEM"; + logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; + logEntry["Created"] = std::move(timestamp); + + // If logEntryJson references an array of LogEntry resources + // ('Members' list), then push this as a new entry, otherwise set it + // directly + if (logEntryJson.is_array()) + { + logEntryJson.push_back(logEntry); + asyncResp->res.jsonValue["Members@odata.count"] = + logEntryJson.size(); } else { - messages::internalError(asyncResp->res); + logEntryJson.update(logEntry); } - return; - } - - std::string timestamp{}; - std::string filename{}; - std::string logfile{}; - parseCrashdumpParameters(params, filename, timestamp, logfile); - - if (filename.empty() || timestamp.empty()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", logID); - return; - } - - std::string crashdumpURI = - std::format("/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - logID + "/" + filename; - nlohmann::json::object_t logEntry; - logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; - logEntry["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, logID); - logEntry["Name"] = "CPU Crashdump"; - logEntry["Id"] = logID; - logEntry["EntryType"] = "Oem"; - logEntry["AdditionalDataURI"] = std::move(crashdumpURI); - logEntry["DiagnosticDataType"] = "OEM"; - logEntry["OEMDiagnosticDataType"] = "PECICrashdump"; - logEntry["Created"] = std::move(timestamp); - - // If logEntryJson references an array of LogEntry resources - // ('Members' list), then push this as a new entry, otherwise set it - // directly - if (logEntryJson.is_array()) - { - logEntryJson.push_back(logEntry); - asyncResp->res.jsonValue["Members@odata.count"] = - logEntryJson.size(); - } - else - { - logEntryJson.update(logEntry); - } - }; + }; sdbusplus::asio::getAllProperties( *crow::connections::systemBus, crashdumpObject, crashdumpPath + std::string("/") + logID, crashdumpInterface, @@ -3541,71 +2804,74 @@ inline void requestRoutesCrashdumpEntryCollection(App& app) // This is incorrect, should be. //.privileges(redfish::privileges::postLogEntryCollection) .privileges({{"ConfigureComponents"}}) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - constexpr std::array<std::string_view, 1> interfaces = { - crashdumpInterface}; - dbus::utility::getSubTreePaths( - "/", 0, interfaces, - [asyncResp](const boost::system::error_code& ec, - const std::vector<std::string>& resp) { - if (ec) + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - if (ec.value() != - boost::system::errc::no_such_file_or_directory) - { - BMCWEB_LOG_DEBUG("failed to get entries ec: {}", - ec.message()); - messages::internalError(asyncResp->res); - return; - } + return; } - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = std::format( - "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of Crashdump Entries"; - asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = 0; - - for (const std::string& path : resp) + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - const sdbusplus::message::object_path objPath(path); - // Get the log ID - std::string logID = objPath.filename(); - if (logID.empty()) - { - continue; - } - // Add the log entry to the array - logCrashdumpEntry(asyncResp, logID, - asyncResp->res.jsonValue["Members"]); + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + constexpr std::array<std::string_view, 1> interfaces = { + crashdumpInterface}; + dbus::utility::getSubTreePaths( + "/", 0, interfaces, + [asyncResp](const boost::system::error_code& ec, + const std::vector<std::string>& resp) { + if (ec) + { + if (ec.value() != + boost::system::errc::no_such_file_or_directory) + { + BMCWEB_LOG_DEBUG("failed to get entries ec: {}", + ec.message()); + messages::internalError(asyncResp->res); + return; + } + } + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Name"] = + "Open BMC Crashdump Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of Crashdump Entries"; + asyncResp->res.jsonValue["Members"] = + nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + + for (const std::string& path : resp) + { + const sdbusplus::message::object_path objPath(path); + // Get the log ID + std::string logID = objPath.filename(); + if (logID.empty()) + { + continue; + } + // Add the log entry to the array + logCrashdumpEntry(asyncResp, logID, + asyncResp->res.jsonValue["Members"]); + } + }); }); - }); } inline void requestRoutesCrashdumpEntry(App& app) @@ -3622,26 +2888,26 @@ inline void requestRoutesCrashdumpEntry(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& param) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - const std::string& logID = param; - logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + const std::string& logID = param; + logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); + }); } inline void requestRoutesCrashdumpFile(App& app) @@ -3657,73 +2923,80 @@ inline void requestRoutesCrashdumpFile(App& app) const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& logID, const std::string& fileName) { - // Do not call getRedfishRoute here since the crashdump file is not a - // Redfish resource. + // Do not call getRedfishRoute here since the crashdump file is + // not a Redfish resource. - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - auto getStoredLogCallback = - [asyncResp, logID, fileName, url(boost::urls::url(req.url()))]( - const boost::system::error_code& ec, - const std::vector< - std::pair<std::string, dbus::utility::DbusVariantType>>& - resp) { - if (ec) - { - BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message()); - messages::internalError(asyncResp->res); - return; - } + auto getStoredLogCallback = + [asyncResp, logID, fileName, + url(boost::urls::url(req.url()))]( + const boost::system::error_code& ec, + const std::vector<std::pair< + std::string, dbus::utility::DbusVariantType>>& + resp) { + if (ec) + { + BMCWEB_LOG_DEBUG("failed to get log ec: {}", + ec.message()); + messages::internalError(asyncResp->res); + return; + } - std::string dbusFilename{}; - std::string dbusTimestamp{}; - std::string dbusFilepath{}; + std::string dbusFilename{}; + std::string dbusTimestamp{}; + std::string dbusFilepath{}; - parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp, - dbusFilepath); + parseCrashdumpParameters(resp, dbusFilename, + dbusTimestamp, dbusFilepath); - if (dbusFilename.empty() || dbusTimestamp.empty() || - dbusFilepath.empty()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", logID); - return; - } + if (dbusFilename.empty() || dbusTimestamp.empty() || + dbusFilepath.empty()) + { + messages::resourceNotFound(asyncResp->res, + "LogEntry", logID); + return; + } - // Verify the file name parameter is correct - if (fileName != dbusFilename) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", logID); - return; - } + // Verify the file name parameter is correct + if (fileName != dbusFilename) + { + messages::resourceNotFound(asyncResp->res, + "LogEntry", logID); + return; + } - if (!asyncResp->res.openFile(dbusFilepath)) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", logID); - return; - } + if (asyncResp->res.openFile(dbusFilepath) != + crow::OpenCode::Success) + { + messages::resourceNotFound(asyncResp->res, + "LogEntry", logID); + return; + } - // Configure this to be a file download when accessed - // from a browser - asyncResp->res.addHeader( - boost::beast::http::field::content_disposition, "attachment"); - }; - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, crashdumpObject, - crashdumpPath + std::string("/") + logID, crashdumpInterface, - std::move(getStoredLogCallback)); - }); + // Configure this to be a file download when accessed + // from a browser + asyncResp->res.addHeader( + boost::beast::http::field::content_disposition, + "attachment"); + }; + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, crashdumpObject, + crashdumpPath + std::string("/") + logID, + crashdumpInterface, std::move(getStoredLogCallback)); + }); } enum class OEMDiagnosticType @@ -3761,122 +3034,158 @@ inline void requestRoutesCrashdumpCollect(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - std::string diagnosticDataType; - std::string oemDiagnosticDataType; - if (!redfish::json_util::readJsonAction( - req, asyncResp->res, "DiagnosticDataType", diagnosticDataType, - "OEMDiagnosticDataType", oemDiagnosticDataType)) - { - return; - } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - if (diagnosticDataType != "OEM") - { - BMCWEB_LOG_ERROR( - "Only OEM DiagnosticDataType supported for Crashdump"); - messages::actionParameterValueFormatError( - asyncResp->res, diagnosticDataType, "DiagnosticDataType", - "CollectDiagnosticData"); - return; - } + std::string diagnosticDataType; + std::string oemDiagnosticDataType; + if (!redfish::json_util::readJsonAction( + req, asyncResp->res, "DiagnosticDataType", + diagnosticDataType, "OEMDiagnosticDataType", + oemDiagnosticDataType)) + { + return; + } - OEMDiagnosticType oemDiagType = - getOEMDiagnosticType(oemDiagnosticDataType); + if (diagnosticDataType != "OEM") + { + BMCWEB_LOG_ERROR( + "Only OEM DiagnosticDataType supported for Crashdump"); + messages::actionParameterValueFormatError( + asyncResp->res, diagnosticDataType, + "DiagnosticDataType", "CollectDiagnosticData"); + return; + } - std::string iface; - std::string method; - std::string taskMatchStr; - if (oemDiagType == OEMDiagnosticType::onDemand) - { - iface = crashdumpOnDemandInterface; - method = "GenerateOnDemandLog"; - taskMatchStr = "type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'," - "arg0namespace='com.intel.crashdump'"; - } - else if (oemDiagType == OEMDiagnosticType::telemetry) - { - iface = crashdumpTelemetryInterface; - method = "GenerateTelemetryLog"; - taskMatchStr = "type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'," - "arg0namespace='com.intel.crashdump'"; - } - else - { - BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", - oemDiagnosticDataType); - messages::actionParameterValueFormatError( - asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType", - "CollectDiagnosticData"); - return; - } + OEMDiagnosticType oemDiagType = + getOEMDiagnosticType(oemDiagnosticDataType); - auto collectCrashdumpCallback = - [asyncResp, payload(task::Payload(req)), - taskMatchStr](const boost::system::error_code& ec, - const std::string&) mutable { - if (ec) - { - if (ec.value() == boost::system::errc::operation_not_supported) + std::string iface; + std::string method; + std::string taskMatchStr; + if (oemDiagType == OEMDiagnosticType::onDemand) { - messages::resourceInStandby(asyncResp->res); + iface = crashdumpOnDemandInterface; + method = "GenerateOnDemandLog"; + taskMatchStr = + "type='signal'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'," + "arg0namespace='com.intel.crashdump'"; } - else if (ec.value() == - boost::system::errc::device_or_resource_busy) + else if (oemDiagType == OEMDiagnosticType::telemetry) { - messages::serviceTemporarilyUnavailable(asyncResp->res, - "60"); + iface = crashdumpTelemetryInterface; + method = "GenerateTelemetryLog"; + taskMatchStr = + "type='signal'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'," + "arg0namespace='com.intel.crashdump'"; } else { - messages::internalError(asyncResp->res); - } - return; - } - std::shared_ptr<task::TaskData> task = task::TaskData::createTask( - [](const boost::system::error_code& ec2, sdbusplus::message_t&, - const std::shared_ptr<task::TaskData>& taskData) { - if (!ec2) - { - taskData->messages.emplace_back(messages::taskCompletedOK( - std::to_string(taskData->index))); - taskData->state = "Completed"; + BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}", + oemDiagnosticDataType); + messages::actionParameterValueFormatError( + asyncResp->res, oemDiagnosticDataType, + "OEMDiagnosticDataType", "CollectDiagnosticData"); + return; } - return task::completed; - }, - taskMatchStr); - task->startTimer(std::chrono::minutes(5)); - task->populateResp(asyncResp->res); - task->payload.emplace(std::move(payload)); - }; + auto collectCrashdumpCallback = + [asyncResp, payload(task::Payload(req)), + taskMatchStr](const boost::system::error_code& ec, + const std::string&) mutable { + if (ec) + { + if (ec.value() == + boost::system::errc::operation_not_supported) + { + messages::resourceInStandby(asyncResp->res); + } + else if (ec.value() == boost::system::errc:: + device_or_resource_busy) + { + messages::serviceTemporarilyUnavailable( + asyncResp->res, "60"); + } + else + { + messages::internalError(asyncResp->res); + } + return; + } + std::shared_ptr<task::TaskData> task = + task::TaskData::createTask( + [](const boost::system::error_code& ec2, + sdbusplus::message_t&, + const std::shared_ptr<task::TaskData>& + taskData) { + if (!ec2) + { + taskData->messages.emplace_back( + messages::taskCompletedOK( + std::to_string( + taskData->index))); + taskData->state = "Completed"; + } + return task::completed; + }, + taskMatchStr); + + task->startTimer(std::chrono::minutes(5)); + task->populateResp(asyncResp->res); + task->payload.emplace(std::move(payload)); + }; + + crow::connections::systemBus->async_method_call( + std::move(collectCrashdumpCallback), crashdumpObject, + crashdumpPath, iface, method); + }); +} - crow::connections::systemBus->async_method_call( - std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath, - iface, method); - }); +inline void dBusLogServiceActionsClear( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +{ + BMCWEB_LOG_DEBUG("Do delete all entries."); + + // Process response from Logging service. + auto respHandler = [asyncResp](const boost::system::error_code& ec) { + BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); + if (ec) + { + // TODO Handle for specific error code + BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + asyncResp->res.result(boost::beast::http::status::no_content); + }; + + // Make call to Logging service to request Clear Log + crow::connections::systemBus->async_method_call( + respHandler, "xyz.openbmc_project.Logging", + "/xyz/openbmc_project/logging", + "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); } /** @@ -3898,638 +3207,25 @@ inline void requestRoutesDBusLogServiceActionsClear(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - BMCWEB_LOG_DEBUG("Do delete all entries."); - - // Process response from Logging service. - auto respHandler = [asyncResp](const boost::system::error_code& ec) { - BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done"); - if (ec) - { - // TODO Handle for specific error code - BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - - asyncResp->res.result(boost::beast::http::status::no_content); - }; - - // Make call to Logging service to request Clear Log - crow::connections::systemBus->async_method_call( - respHandler, "xyz.openbmc_project.Logging", - "/xyz/openbmc_project/logging", - "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); - }); -} - -/**************************************************** - * Redfish PostCode interfaces - * using DBUS interface: getPostCodesTS - ******************************************************/ -inline void requestRoutesPostCodesLogService(App& app) -{ - BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/") - .privileges(redfish::privileges::getLogService) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = - "#LogService.v1_2_0.LogService"; - asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; - asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; - asyncResp->res.jsonValue["Id"] = "PostCodes"; - asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - - std::pair<std::string, std::string> redfishDateTimeOffset = - redfish::time_utils::getDateTimeOffsetNow(); - asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; - asyncResp->res.jsonValue["DateTimeLocalOffset"] = - redfishDateTimeOffset.second; - - asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] - ["target"] = std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - }); -} - -inline void requestRoutesPostCodesClear(App& app) -{ - BMCWEB_ROUTE( - app, - "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/") - // The following privilege is incorrect; It should be ConfigureManager - //.privileges(redfish::privileges::postLogService) - .privileges({{"ConfigureComponents"}}) - .methods(boost::beast::http::verb::post)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); - - // Make call to post-code service to request clear all - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - // TODO Handle for specific error code - BMCWEB_LOG_ERROR("doClearPostCodes resp_handler got error {}", - ec); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - messages::internalError(asyncResp->res); - return; - } - messages::success(asyncResp->res); - }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", - "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); - }); -} - -/** - * @brief Parse post code ID and get the current value and index value - * eg: postCodeID=B1-2, currentValue=1, index=2 - * - * @param[in] postCodeID Post Code ID - * @param[out] currentValue Current value - * @param[out] index Index value - * - * @return bool true if the parsing is successful, false the parsing fails - */ -inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue, - uint16_t& index) -{ - std::vector<std::string> split; - bmcweb::split(split, postCodeID, '-'); - if (split.size() != 2) - { - return false; - } - std::string_view postCodeNumber = split[0]; - if (postCodeNumber.size() < 2) - { - return false; - } - if (postCodeNumber[0] != 'B') - { - return false; - } - postCodeNumber.remove_prefix(1); - auto [ptrIndex, ecIndex] = std::from_chars(postCodeNumber.begin(), - postCodeNumber.end(), index); - if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc()) - { - return false; - } - - std::string_view postCodeIndex = split[1]; - - auto [ptrValue, ecValue] = std::from_chars( - postCodeIndex.begin(), postCodeIndex.end(), currentValue); - - return ptrValue == postCodeIndex.end() && ecValue == std::errc(); -} - -static bool fillPostCodeEntry( - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const boost::container::flat_map< - uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode, - const uint16_t bootIndex, const uint64_t codeIndex = 0, - const uint64_t skip = 0, const uint64_t top = 0) -{ - // Get the Message from the MessageRegistry - const registries::Message* message = - registries::getMessage("OpenBMC.0.2.BIOSPOSTCode"); - if (message == nullptr) - { - BMCWEB_LOG_ERROR("Couldn't find known message?"); - return false; - } - uint64_t currentCodeIndex = 0; - uint64_t firstCodeTimeUs = 0; - for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& - code : postcode) - { - currentCodeIndex++; - std::string postcodeEntryID = - "B" + std::to_string(bootIndex) + "-" + - std::to_string(currentCodeIndex); // 1 based index in EntryID string - - uint64_t usecSinceEpoch = code.first; - uint64_t usTimeOffset = 0; - - if (1 == currentCodeIndex) - { // already incremented - firstCodeTimeUs = code.first; - } - else - { - usTimeOffset = code.first - firstCodeTimeUs; - } - - // skip if no specific codeIndex is specified and currentCodeIndex does - // not fall between top and skip - if ((codeIndex == 0) && - (currentCodeIndex <= skip || currentCodeIndex > top)) - { - continue; - } - - // skip if a specific codeIndex is specified and does not match the - // currentIndex - if ((codeIndex > 0) && (currentCodeIndex != codeIndex)) - { - // This is done for simplicity. 1st entry is needed to calculate - // time offset. To improve efficiency, one can get to the entry - // directly (possibly with flatmap's nth method) - continue; - } - - // currentCodeIndex is within top and skip or equal to specified code - // index - - // Get the Created time from the timestamp - std::string entryTimeStr; - entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch); - - // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex) - std::ostringstream hexCode; - hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex - << std::get<0>(code.second); - std::ostringstream timeOffsetStr; - // Set Fixed -Point Notation - timeOffsetStr << std::fixed; - // Set precision to 4 digits - timeOffsetStr << std::setprecision(4); - // Add double to stream - timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; - - std::string bootIndexStr = std::to_string(bootIndex); - std::string timeOffsetString = timeOffsetStr.str(); - std::string hexCodeStr = hexCode.str(); - - std::array<std::string_view, 3> messageArgs = { - bootIndexStr, timeOffsetString, hexCodeStr}; - - std::string msg = - redfish::registries::fillMessageArgs(messageArgs, message->message); - if (msg.empty()) - { - messages::internalError(asyncResp->res); - return false; - } - - // Get Severity template from message registry - std::string severity; - if (message != nullptr) - { - severity = message->messageSeverity; - } - - // Format entry - nlohmann::json::object_t bmcLogEntry; - bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; - bmcLogEntry["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID); - bmcLogEntry["Name"] = "POST Code Log Entry"; - bmcLogEntry["Id"] = postcodeEntryID; - bmcLogEntry["Message"] = std::move(msg); - bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode"; - bmcLogEntry["MessageArgs"] = messageArgs; - bmcLogEntry["EntryType"] = "Event"; - bmcLogEntry["Severity"] = std::move(severity); - bmcLogEntry["Created"] = entryTimeStr; - if (!std::get<std::vector<uint8_t>>(code.second).empty()) - { - bmcLogEntry["AdditionalDataURI"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - postcodeEntryID + "/attachment"; - } - - // codeIndex is only specified when querying single entry, return only - // that entry in this case - if (codeIndex != 0) - { - asyncResp->res.jsonValue.update(bmcLogEntry); - return true; - } - - nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; - logEntryArray.emplace_back(std::move(bmcLogEntry)); - } - - // Return value is always false when querying multiple entries - return false; -} - -static void - getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& entryId) -{ - uint16_t bootIndex = 0; - uint64_t codeIndex = 0; - if (!parsePostCode(entryId, codeIndex, bootIndex)) - { - // Requested ID was not found - messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); - return; - } - - if (bootIndex == 0 || codeIndex == 0) - { - // 0 is an invalid index - messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); - return; - } - - crow::connections::systemBus->async_method_call( - [asyncResp, entryId, bootIndex, - codeIndex](const boost::system::error_code& ec, - const boost::container::flat_map< - uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& - postcode) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); - messages::internalError(asyncResp->res); - return; - } - - if (postcode.empty()) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); - return; - } - - if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex)) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); - return; - } - }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", - "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", - bootIndex); -} - -static void - getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const uint16_t bootIndex, const uint16_t bootCount, - const uint64_t entryCount, size_t skip, size_t top) -{ - crow::connections::systemBus->async_method_call( - [asyncResp, bootIndex, bootCount, entryCount, skip, - top](const boost::system::error_code& ec, - const boost::container::flat_map< - uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& - postcode) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); - messages::internalError(asyncResp->res); - return; - } - - uint64_t endCount = entryCount; - if (!postcode.empty()) - { - endCount = entryCount + postcode.size(); - if (skip < endCount && (top + skip) > entryCount) - { - uint64_t thisBootSkip = std::max(static_cast<uint64_t>(skip), - entryCount) - - entryCount; - uint64_t thisBootTop = - std::min(static_cast<uint64_t>(top + skip), endCount) - - entryCount; - - fillPostCodeEntry(asyncResp, postcode, bootIndex, 0, - thisBootSkip, thisBootTop); - } - asyncResp->res.jsonValue["Members@odata.count"] = endCount; - } - - // continue to previous bootIndex - if (bootIndex < bootCount) - { - getPostCodeForBoot(asyncResp, static_cast<uint16_t>(bootIndex + 1), - bootCount, endCount, skip, top); - } - else if (skip + top < endCount) - { - asyncResp->res.jsonValue["Members@odata.nextLink"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - std::to_string(skip + top); - } - }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", - "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", - bootIndex); -} - -static void - getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - size_t skip, size_t top) -{ - uint64_t entryCount = 0; - sdbusplus::asio::getProperty<uint16_t>( - *crow::connections::systemBus, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", - "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount", - [asyncResp, entryCount, skip, top](const boost::system::error_code& ec, - const uint16_t bootCount) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top); - }); -} - -inline void requestRoutesPostCodesEntryCollection(App& app) -{ - BMCWEB_ROUTE(app, - "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/") - .privileges(redfish::privileges::getLogEntryCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - query_param::QueryCapabilities capabilities = { - .canDelegateTop = true, - .canDelegateSkip = true, - }; - query_param::Query delegatedQuery; - if (!redfish::setUpRedfishRouteWithDelegation( - app, req, asyncResp, delegatedQuery, capabilities)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; - asyncResp->res.jsonValue["Description"] = - "Collection of POST Code Log Entries"; - asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = 0; - size_t skip = delegatedQuery.skip.value_or(0); - size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); - getCurrentBootNumber(asyncResp, skip, top); - }); -} - -inline void requestRoutesPostCodesEntryAdditionalData(App& app) -{ - BMCWEB_ROUTE( - app, - "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& postCodeID) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (!http_helpers::isContentTypeAllowed( - req.getHeaderValue("Accept"), - http_helpers::ContentType::OctetStream, true)) - { - asyncResp->res.result(boost::beast::http::status::bad_request); - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - uint64_t currentValue = 0; - uint16_t index = 0; - if (!parsePostCode(postCodeID, currentValue, index)) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID); - return; - } - - crow::connections::systemBus->async_method_call( - [asyncResp, postCodeID, currentValue]( - const boost::system::error_code& ec, - const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>& - postcodes) { - if (ec.value() == EBADR) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", - postCodeID); - return; - } - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - size_t value = static_cast<size_t>(currentValue) - 1; - if (value == std::string::npos || postcodes.size() < currentValue) - { - BMCWEB_LOG_WARNING("Wrong currentValue value"); - messages::resourceNotFound(asyncResp->res, "LogEntry", - postCodeID); - return; - } - - const auto& [tID, c] = postcodes[value]; - if (c.empty()) - { - BMCWEB_LOG_WARNING("No found post code data"); - messages::resourceNotFound(asyncResp->res, "LogEntry", - postCodeID); - return; - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - const char* d = reinterpret_cast<const char*>(c.data()); - std::string_view strData(d, c.size()); - - asyncResp->res.addHeader(boost::beast::http::field::content_type, - "application/octet-stream"); - asyncResp->res.addHeader( - boost::beast::http::field::content_transfer_encoding, "Base64"); - asyncResp->res.write(crow::utility::base64encode(strData)); - }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", - "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index); - }); -} - -inline void requestRoutesPostCodesEntry(App& app) -{ - BMCWEB_ROUTE( - app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/") - .privileges(redfish::privileges::getLogEntry) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& targetID) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - getPostCodeForEntry(asyncResp, targetID); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + dBusLogServiceActionsClear(asyncResp); + }); } } // namespace redfish diff --git a/redfish-core/lib/manager_diagnostic_data.hpp b/redfish-core/lib/manager_diagnostic_data.hpp index 1856e1f9e6..1ca80fdd54 100644 --- a/redfish-core/lib/manager_diagnostic_data.hpp +++ b/redfish-core/lib/manager_diagnostic_data.hpp @@ -106,8 +106,8 @@ inline void } static constexpr double roundFactor = 10000; // 4 decimal places - asyncResp->res.jsonValue[jPtr] = std::round(userCPU * roundFactor) / - roundFactor; + asyncResp->res.jsonValue[jPtr] = + std::round(userCPU * roundFactor) / roundFactor; } inline void managerGetProcessorStatistics( diff --git a/redfish-core/lib/manager_logservices_journal.hpp b/redfish-core/lib/manager_logservices_journal.hpp new file mode 100644 index 0000000000..17fb95bfc5 --- /dev/null +++ b/redfish-core/lib/manager_logservices_journal.hpp @@ -0,0 +1,655 @@ +#pragma once + +#include "app.hpp" +#include "error_messages.hpp" +#include "generated/enums/log_entry.hpp" +#include "query.hpp" +#include "registries/base_message_registry.hpp" +#include "registries/privilege_registry.hpp" +#include "utils/time_utils.hpp" + +#include <systemd/sd-journal.h> + +#include <boost/beast/http/verb.hpp> + +#include <array> +#include <memory> +#include <string> +#include <string_view> + +namespace redfish +{ +// Entry is formed like "BootID_timestamp" or "BootID_timestamp_index" +inline bool + getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + std::string_view entryIDStrView, sd_id128_t& bootID, + uint64_t& timestamp, uint64_t& index) +{ + // Convert the unique ID back to a bootID + timestamp to find the entry + auto underscore1Pos = entryIDStrView.find('_'); + if (underscore1Pos == std::string_view::npos) + { + // EntryID has no bootID or timestamp + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + + // EntryID has bootID + timestamp + + // Convert entryIDViewString to BootID + // NOTE: bootID string which needs to be null-terminated for + // sd_id128_from_string() + std::string bootIDStr(entryIDStrView.substr(0, underscore1Pos)); + if (sd_id128_from_string(bootIDStr.c_str(), &bootID) < 0) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + + // Get the timestamp from entryID + entryIDStrView.remove_prefix(underscore1Pos + 1); + + auto [timestampEnd, tstampEc] = std::from_chars( + entryIDStrView.begin(), entryIDStrView.end(), timestamp); + if (tstampEc != std::errc()) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + entryIDStrView = std::string_view( + timestampEnd, + static_cast<size_t>(std::distance(timestampEnd, entryIDStrView.end()))); + if (entryIDStrView.empty()) + { + index = 0U; + return true; + } + // Timestamp might include optional index, if two events happened at the + // same "time". + if (entryIDStrView[0] != '_') + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + entryIDStrView.remove_prefix(1); + auto [ptr, indexEc] = + std::from_chars(entryIDStrView.begin(), entryIDStrView.end(), index); + if (indexEc != std::errc() || ptr != entryIDStrView.end()) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + if (index <= 1) + { + // Indexes go directly from no postfix (handled above) to _2 + // so if we ever see _0 or _1, it's incorrect + messages::resourceNotFound(asyncResp->res, "LogEntry", entryIDStrView); + return false; + } + + // URI indexes are one based, journald is zero based + index -= 1; + return true; +} + +inline std::string getUniqueEntryID(uint64_t index, uint64_t curTs, + sd_id128_t& curBootID) +{ + // make entryID as <bootID>_<timestamp>[_<index>] + std::array<char, SD_ID128_STRING_MAX> bootIDStr{}; + sd_id128_to_string(curBootID, bootIDStr.data()); + std::string postfix; + if (index > 0) + { + postfix = std::format("_{}", index + 1); + } + return std::format("{}_{}{}", bootIDStr.data(), curTs, postfix); +} + +inline int getJournalMetadata(sd_journal* journal, std::string_view field, + std::string_view& contents) +{ + const char* data = nullptr; + size_t length = 0; + int ret = 0; + // Get the metadata from the requested field of the journal entry + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const void** dataVoid = reinterpret_cast<const void**>(&data); + + ret = sd_journal_get_data(journal, field.data(), dataVoid, &length); + if (ret < 0) + { + return ret; + } + contents = std::string_view(data, length); + // Only use the content after the "=" character. + contents.remove_prefix(std::min(contents.find('=') + 1, contents.size())); + return ret; +} + +inline int getJournalMetadataInt(sd_journal* journal, std::string_view field, + const int& base, long int& contents) +{ + int ret = 0; + std::string_view metadata; + // Get the metadata from the requested field of the journal entry + ret = getJournalMetadata(journal, field, metadata); + if (ret < 0) + { + return ret; + } + contents = strtol(metadata.data(), nullptr, base); + return ret; +} + +inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp) +{ + int ret = 0; + uint64_t timestamp = 0; + ret = sd_journal_get_realtime_usec(journal, ×tamp); + if (ret < 0) + { + BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", strerror(-ret)); + return false; + } + entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp); + return true; +} + +inline bool fillBMCJournalLogEntryJson( + const std::string& bmcJournalLogEntryID, sd_journal* journal, + nlohmann::json::object_t& bmcJournalLogEntryJson) +{ + // Get the Log Entry contents + std::string message; + std::string_view syslogID; + int ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID); + if (ret < 0) + { + BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", + strerror(-ret)); + } + if (!syslogID.empty()) + { + message += std::string(syslogID) + ": "; + } + + std::string_view msg; + ret = getJournalMetadata(journal, "MESSAGE", msg); + if (ret < 0) + { + BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", strerror(-ret)); + return false; + } + message += std::string(msg); + + // Get the severity from the PRIORITY field + long int severity = 8; // Default to an invalid priority + ret = getJournalMetadataInt(journal, "PRIORITY", 10, severity); + if (ret < 0) + { + BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", strerror(-ret)); + } + + // Get the Created time from the timestamp + std::string entryTimeStr; + if (!getEntryTimestamp(journal, entryTimeStr)) + { + return false; + } + + // Fill in the log entry with the gathered data + bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + bmcJournalLogEntryJson["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, bmcJournalLogEntryID); + bmcJournalLogEntryJson["Name"] = "BMC Journal Entry"; + bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID; + bmcJournalLogEntryJson["Message"] = std::move(message); + bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem; + log_entry::EventSeverity severityEnum = log_entry::EventSeverity::OK; + if (severity <= 2) + { + severityEnum = log_entry::EventSeverity::Critical; + } + else if (severity <= 4) + { + severityEnum = log_entry::EventSeverity::Warning; + } + + bmcJournalLogEntryJson["Severity"] = severityEnum; + bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry"; + bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr); + return true; +} + +inline void handleManagersLogServiceJournalGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& managerId) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", managerId); + return; + } + + asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; + asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; + asyncResp->res.jsonValue["Id"] = "Journal"; + asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; + + std::pair<std::string, std::string> redfishDateTimeOffset = + redfish::time_utils::getDateTimeOffsetNow(); + asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; + asyncResp->res.jsonValue["DateTimeLocalOffset"] = + redfishDateTimeOffset.second; + + asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Journal/Entries", + BMCWEB_REDFISH_MANAGER_URI_NAME); +} + +struct JournalReadState +{ + std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal; + uint64_t index = 0; + sd_id128_t prevBootID{}; + uint64_t prevTs = 0; +}; + +inline void readJournalEntries( + uint64_t topEntryCount, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + JournalReadState&& readState) +{ + nlohmann::json& logEntry = asyncResp->res.jsonValue["Members"]; + nlohmann::json::array_t* logEntryArray = + logEntry.get_ptr<nlohmann::json::array_t*>(); + if (logEntryArray == nullptr) + { + messages::internalError(asyncResp->res); + return; + } + + // The Journal APIs unfortunately do blocking calls to the filesystem, and + // can be somewhat expensive. Short of creating our own io_uring based + // implementation of sd-journal, which would be difficult, the best thing we + // can do is to only parse a certain number of entries at a time. The + // current chunk size is selected arbitrarily to ensure that we're not + // trying to process thousands of entries at the same time. + // The implementation will process the number of entries, then return + // control to the io_context to let other operations continue. + size_t segmentCountRemaining = 10; + + // Reset the unique ID on the first entry + for (uint64_t entryCount = logEntryArray->size(); + entryCount < topEntryCount; entryCount++) + { + if (segmentCountRemaining == 0) + { + boost::asio::post(crow::connections::systemBus->get_io_context(), + [asyncResp, topEntryCount, + readState = std::move(readState)]() mutable { + readJournalEntries(topEntryCount, asyncResp, + std::move(readState)); + }); + return; + } + + // Get the entry timestamp + sd_id128_t curBootID{}; + uint64_t curTs = 0; + int ret = sd_journal_get_monotonic_usec(readState.journal.get(), &curTs, + &curBootID); + if (ret < 0) + { + BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", + strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + + // If the timestamp isn't unique on the same boot, increment the index + bool sameBootIDs = sd_id128_equal(curBootID, readState.prevBootID) != 0; + if (sameBootIDs && (curTs == readState.prevTs)) + { + readState.index++; + } + else + { + // Otherwise, reset it + readState.index = 0; + } + + // Save the bootID + readState.prevBootID = curBootID; + + // Save the timestamp + readState.prevTs = curTs; + + std::string idStr = getUniqueEntryID(readState.index, curTs, curBootID); + + nlohmann::json::object_t bmcJournalLogEntry; + if (!fillBMCJournalLogEntryJson(idStr, readState.journal.get(), + bmcJournalLogEntry)) + { + messages::internalError(asyncResp->res); + return; + } + logEntryArray->emplace_back(std::move(bmcJournalLogEntry)); + + ret = sd_journal_next(readState.journal.get()); + if (ret < 0) + { + messages::internalError(asyncResp->res); + return; + } + if (ret == 0) + { + break; + } + segmentCountRemaining--; + } +} + +inline void handleManagersJournalLogEntryCollectionGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& managerId) +{ + query_param::QueryCapabilities capabilities = { + .canDelegateTop = true, + .canDelegateSkip = true, + }; + query_param::Query delegatedQuery; + if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, + delegatedQuery, capabilities)) + { + return; + } + + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", managerId); + return; + } + + size_t skip = delegatedQuery.skip.value_or(0); + size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); + + // Collections don't include the static data added by SubRoute + // because it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Journal/Entries", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of BMC Journal Entries"; + asyncResp->res.jsonValue["Members"] = nlohmann::json::array_t(); + + // Go through the journal and use the timestamp to create a + // unique ID for each entry + sd_journal* journalTmp = nullptr; + int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); + if (ret < 0) + { + BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + + std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( + journalTmp, sd_journal_close); + journalTmp = nullptr; + + // Seek to the end + if (sd_journal_seek_tail(journal.get()) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + // Get the last entry + if (sd_journal_previous(journal.get()) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + // Get the last sequence number + uint64_t endSeqNum = 0; +#if LIBSYSTEMD_VERSION >= 254 + { + if (sd_journal_get_seqnum(journal.get(), &endSeqNum, nullptr) < 0) + { + messages::internalError(asyncResp->res); + return; + } + } +#endif + + // Seek to the beginning + if (sd_journal_seek_head(journal.get()) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + // Get the first entry + if (sd_journal_next(journal.get()) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + // Get the first sequence number + uint64_t startSeqNum = 0; +#if LIBSYSTEMD_VERSION >= 254 + { + if (sd_journal_get_seqnum(journal.get(), &startSeqNum, nullptr) < 0) + { + messages::internalError(asyncResp->res); + return; + } + } +#endif + + BMCWEB_LOG_DEBUG("journal Sequence IDs start:{} end:{}", startSeqNum, + endSeqNum); + + // Add 1 to account for the last entry + uint64_t totalEntries = endSeqNum - startSeqNum + 1; + asyncResp->res.jsonValue["Members@odata.count"] = totalEntries; + if (skip + top < totalEntries) + { + asyncResp->res.jsonValue["Members@odata.nextLink"] = + boost::urls::format( + "/redfish/v1/Managers/{}/LogServices/Journal/Entries?$skip={}", + BMCWEB_REDFISH_MANAGER_URI_NAME, std::to_string(skip + top)); + } + uint64_t index = 0; + sd_id128_t curBootID{}; + uint64_t curTs = 0; + if (skip > 0) + { + if (sd_journal_next_skip(journal.get(), skip) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + // Get the entry timestamp + ret = sd_journal_get_monotonic_usec(journal.get(), &curTs, &curBootID); + if (ret < 0) + { + BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", + strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + + uint64_t endChunkSeqNum = 0; +#if LIBSYSTEMD_VERSION >= 254 + { + if (sd_journal_get_seqnum(journal.get(), &endChunkSeqNum, nullptr) < + 0) + { + messages::internalError(asyncResp->res); + return; + } + } +#endif + + // Seek to the first entry with the same timestamp and boot + ret = sd_journal_seek_monotonic_usec(journal.get(), curBootID, curTs); + if (ret < 0) + { + BMCWEB_LOG_ERROR("Failed to seek: {}", strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + if (sd_journal_next(journal.get()) < 0) + { + messages::internalError(asyncResp->res); + return; + } + uint64_t startChunkSeqNum = 0; +#if LIBSYSTEMD_VERSION >= 254 + { + if (sd_journal_get_seqnum(journal.get(), &startChunkSeqNum, + nullptr) < 0) + { + messages::internalError(asyncResp->res); + return; + } + } +#endif + + // Get the difference between the start and end. Most of the time this + // will be 0 + BMCWEB_LOG_DEBUG("start={} end={}", startChunkSeqNum, endChunkSeqNum); + index = endChunkSeqNum - startChunkSeqNum; + if (index > endChunkSeqNum) + { + // Detect underflows. Should never happen. + messages::internalError(asyncResp->res); + return; + } + if (index > 0) + { + BMCWEB_LOG_DEBUG("index = {}", index); + if (sd_journal_next_skip(journal.get(), index) < 0) + { + messages::internalError(asyncResp->res); + return; + } + } + } + // If this is the first entry of this series, reset the timestamps so the + // Index doesn't increment + if (index == 0) + { + curBootID = {}; + curTs = 0; + } + else + { + index -= 1; + } + BMCWEB_LOG_DEBUG("Index was {}", index); + readJournalEntries(top, asyncResp, + {std::move(journal), index, curBootID, curTs}); +} + +inline void handleManagersJournalEntriesLogEntryGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& managerId, const std::string& entryID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", managerId); + return; + } + + // Convert the unique ID back to a timestamp to find the entry + sd_id128_t bootID{}; + uint64_t ts = 0; + uint64_t index = 0; + if (!getTimestampFromID(asyncResp, entryID, bootID, ts, index)) + { + return; + } + + sd_journal* journalTmp = nullptr; + int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); + if (ret < 0) + { + BMCWEB_LOG_ERROR("failed to open journal: {}", strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( + journalTmp, sd_journal_close); + journalTmp = nullptr; + // Go to the timestamp in the log and move to the entry at the + // index tracking the unique ID + ret = sd_journal_seek_monotonic_usec(journal.get(), bootID, ts); + if (ret < 0) + { + BMCWEB_LOG_ERROR("failed to seek to an entry in journal{}", + strerror(-ret)); + messages::internalError(asyncResp->res); + return; + } + + if (sd_journal_next_skip(journal.get(), index + 1) < 0) + { + messages::internalError(asyncResp->res); + return; + } + + nlohmann::json::object_t bmcJournalLogEntry; + if (!fillBMCJournalLogEntryJson(entryID, journal.get(), bmcJournalLogEntry)) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue.update(bmcJournalLogEntry); +} + +inline void requestRoutesBMCJournalLogService(App& app) +{ + BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/") + .privileges(redfish::privileges::getLogService) + .methods(boost::beast::http::verb::get)( + std::bind_front(handleManagersLogServiceJournalGet, std::ref(app))); + + BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/") + .privileges(redfish::privileges::getLogEntryCollection) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleManagersJournalLogEntryCollectionGet, std::ref(app))); + + BMCWEB_ROUTE( + app, "/redfish/v1/Managers/<str>/LogServices/Journal/Entries/<str>/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleManagersJournalEntriesLogEntryGet, std::ref(app))); +} +} // namespace redfish diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index f0a4e0ab21..c4f9956cf5 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -19,6 +19,9 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/action_info.hpp" +#include "generated/enums/manager.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "redfish_util.hpp" #include "registries/privilege_registry.hpp" @@ -67,16 +70,16 @@ inline void *crow::connections::systemBus, processName, objectPath, interfaceName, destProperty, propertyValue, [asyncResp](const boost::system::error_code& ec) { - // Use "Set" method to set the property value. - if (ec) - { - BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } + // Use "Set" method to set the property value. + if (ec) + { + BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - messages::success(asyncResp->res); - }); + messages::success(asyncResp->res); + }); } inline void @@ -94,16 +97,16 @@ inline void *crow::connections::systemBus, processName, objectPath, interfaceName, destProperty, propertyValue, [asyncResp](const boost::system::error_code& ec) { - // Use "Set" method to set the property value. - if (ec) - { - BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } + // Use "Set" method to set the property value. + if (ec) + { + BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); + messages::internalError(asyncResp->res); + return; + } - messages::success(asyncResp->res); - }); + messages::success(asyncResp->res); + }); } /** @@ -124,44 +127,46 @@ inline void requestRoutesManagerResetAction(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - BMCWEB_LOG_DEBUG("Post Manager Reset."); + BMCWEB_LOG_DEBUG("Post Manager Reset."); - std::string resetType; + std::string resetType; - if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", - resetType)) - { - return; - } + if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", + resetType)) + { + return; + } - if (resetType == "GracefulRestart") - { - BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); - doBMCGracefulRestart(asyncResp); - return; - } - if (resetType == "ForceRestart") - { - BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); - doBMCForceRestart(asyncResp); - return; - } - BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); - messages::actionParameterNotSupported(asyncResp->res, resetType, - "ResetType"); + if (resetType == "GracefulRestart") + { + BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); + doBMCGracefulRestart(asyncResp); + return; + } + if (resetType == "ForceRestart") + { + BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); + doBMCForceRestart(asyncResp); + return; + } + BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", + resetType); + messages::actionParameterNotSupported(asyncResp->res, resetType, + "ResetType"); - return; - }); + return; + }); } /** @@ -185,70 +190,73 @@ inline void requestRoutesManagerResetToDefaultsAction(App& app) BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Actions/Manager.ResetToDefaults/") .privileges(redfish::privileges::postManager) - .methods(boost::beast::http::verb::post)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + .methods( + boost::beast::http::verb:: + post)([&app]( + const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& managerId) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - BMCWEB_LOG_DEBUG("Post ResetToDefaults."); + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - std::optional<std::string> resetType; - std::optional<std::string> resetToDefaultsType; + BMCWEB_LOG_DEBUG("Post ResetToDefaults."); - if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", - resetType, "ResetToDefaultsType", - resetToDefaultsType)) - { - BMCWEB_LOG_DEBUG("Missing property ResetType."); + std::optional<std::string> resetType; + std::optional<std::string> resetToDefaultsType; - messages::actionParameterMissing(asyncResp->res, "ResetToDefaults", - "ResetType"); - return; - } + if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", + resetType, "ResetToDefaultsType", + resetToDefaultsType)) + { + BMCWEB_LOG_DEBUG("Missing property ResetType."); - if (resetToDefaultsType && !resetType) - { - BMCWEB_LOG_WARNING( - "Using deprecated ResetToDefaultsType, should be ResetType." - "Support for the ResetToDefaultsType will be dropped in 2Q24"); - resetType = resetToDefaultsType; - } + messages::actionParameterMissing( + asyncResp->res, "ResetToDefaults", "ResetType"); + return; + } - if (resetType != "ResetAll") - { - BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", - *resetType); - messages::actionParameterNotSupported(asyncResp->res, *resetType, - "ResetType"); - return; - } + if (resetToDefaultsType && !resetType) + { + BMCWEB_LOG_WARNING( + "Using deprecated ResetToDefaultsType, should be ResetType." + "Support for the ResetToDefaultsType will be dropped in 2Q24"); + resetType = resetToDefaultsType; + } - crow::connections::systemBus->async_method_call( - [asyncResp](const boost::system::error_code& ec) { - if (ec) + if (resetType != "ResetAll") { - BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec); - messages::internalError(asyncResp->res); + BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", + *resetType); + messages::actionParameterNotSupported(asyncResp->res, + *resetType, "ResetType"); return; } - // Factory Reset doesn't actually happen until a reboot - // Can't erase what the BMC is running on - doBMCGracefulRestart(asyncResp); - }, - "xyz.openbmc_project.Software.BMC.Updater", - "/xyz/openbmc_project/software", - "xyz.openbmc_project.Common.FactoryReset", "Reset"); - }); + + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec) { + if (ec) + { + BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec); + messages::internalError(asyncResp->res); + return; + } + // Factory Reset doesn't actually happen until a reboot + // Can't erase what the BMC is running on + doBMCGracefulRestart(asyncResp); + }, + "xyz.openbmc_project.Software.BMC.Updater", + "/xyz/openbmc_project/software", + "xyz.openbmc_project.Common.FactoryReset", "Reset"); + }); } /** @@ -267,39 +275,40 @@ inline void requestRoutesManagerResetActionInfo(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#ActionInfo.v1_1_2.ActionInfo"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["Name"] = "Reset Action Info"; - asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; - nlohmann::json::object_t parameter; - parameter["Name"] = "ResetType"; - parameter["Required"] = true; - parameter["DataType"] = "String"; - - nlohmann::json::array_t allowableValues; - allowableValues.emplace_back("GracefulRestart"); - allowableValues.emplace_back("ForceRestart"); - parameter["AllowableValues"] = std::move(allowableValues); - - nlohmann::json::array_t parameters; - parameters.emplace_back(std::move(parameter)); - - asyncResp->res.jsonValue["Parameters"] = std::move(parameters); - }); + asyncResp->res.jsonValue["@odata.type"] = + "#ActionInfo.v1_1_2.ActionInfo"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/ResetActionInfo", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["Name"] = "Reset Action Info"; + asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; + nlohmann::json::object_t parameter; + parameter["Name"] = "ResetType"; + parameter["Required"] = true; + parameter["DataType"] = action_info::ParameterTypes::String; + + nlohmann::json::array_t allowableValues; + allowableValues.emplace_back("GracefulRestart"); + allowableValues.emplace_back("ForceRestart"); + parameter["AllowableValues"] = std::move(allowableValues); + + nlohmann::json::array_t parameters; + parameters.emplace_back(std::move(parameter)); + + asyncResp->res.jsonValue["Parameters"] = std::move(parameters); + }); } static constexpr const char* objectManagerIface = @@ -325,231 +334,221 @@ inline void [asyncResp, currentProfile, supportedProfiles]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& managedObj) { - if (ec) - { - BMCWEB_LOG_ERROR("{}", ec); - messages::internalError(asyncResp->res); - return; - } - nlohmann::json& configRoot = - asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"]; - nlohmann::json& fans = configRoot["FanControllers"]; - fans["@odata.type"] = "#OemManager.FanControllers"; - fans["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanControllers", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - nlohmann::json& pids = configRoot["PidControllers"]; - pids["@odata.type"] = "#OemManager.PidControllers"; - pids["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/PidControllers", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - nlohmann::json& stepwise = configRoot["StepwiseControllers"]; - stepwise["@odata.type"] = "#OemManager.StepwiseControllers"; - stepwise["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/StepwiseControllers", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - nlohmann::json& zones = configRoot["FanZones"]; - zones["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanZones", - BMCWEB_REDFISH_MANAGER_URI_NAME); - zones["@odata.type"] = "#OemManager.FanZones"; - configRoot["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan", - BMCWEB_REDFISH_MANAGER_URI_NAME); - configRoot["@odata.type"] = "#OemManager.Fan"; - configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles; - - if (!currentProfile.empty()) - { - configRoot["Profile"] = currentProfile; - } - BMCWEB_LOG_DEBUG("profile = {} !", currentProfile); - - for (const auto& pathPair : managedObj) - { - for (const auto& intfPair : pathPair.second) + if (ec) { - if (intfPair.first != pidConfigurationIface && - intfPair.first != pidZoneConfigurationIface && - intfPair.first != stepwiseConfigurationIface) - { - continue; - } + BMCWEB_LOG_ERROR("{}", ec); + messages::internalError(asyncResp->res); + return; + } + nlohmann::json& configRoot = + asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"]; + nlohmann::json& fans = configRoot["FanControllers"]; + fans["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.FanControllers"; + fans["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanControllers", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + nlohmann::json& pids = configRoot["PidControllers"]; + pids["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.PidControllers"; + pids["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/PidControllers", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + nlohmann::json& stepwise = configRoot["StepwiseControllers"]; + stepwise["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.StepwiseControllers"; + stepwise["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/StepwiseControllers", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + nlohmann::json& zones = configRoot["FanZones"]; + zones["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan/FanZones", + BMCWEB_REDFISH_MANAGER_URI_NAME); + zones["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager.FanZones"; + configRoot["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc/Fan", + BMCWEB_REDFISH_MANAGER_URI_NAME); + configRoot["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager.Fan"; + configRoot["Profile@Redfish.AllowableValues"] = supportedProfiles; - std::string name; + if (!currentProfile.empty()) + { + configRoot["Profile"] = currentProfile; + } + BMCWEB_LOG_DEBUG("profile = {} !", currentProfile); - for (const std::pair<std::string, - dbus::utility::DbusVariantType>& propPair : - intfPair.second) + for (const auto& pathPair : managedObj) + { + for (const auto& intfPair : pathPair.second) { - if (propPair.first == "Name") + if (intfPair.first != pidConfigurationIface && + intfPair.first != pidZoneConfigurationIface && + intfPair.first != stepwiseConfigurationIface) { - const std::string* namePtr = - std::get_if<std::string>(&propPair.second); - if (namePtr == nullptr) - { - BMCWEB_LOG_ERROR("Pid Name Field illegal"); - messages::internalError(asyncResp->res); - return; - } - name = *namePtr; - dbus::utility::escapePathForDbus(name); + continue; } - else if (propPair.first == "Profiles") + + std::string name; + + for (const std::pair<std::string, + dbus::utility::DbusVariantType>& + propPair : intfPair.second) { - const std::vector<std::string>* profiles = - std::get_if<std::vector<std::string>>( - &propPair.second); - if (profiles == nullptr) + if (propPair.first == "Name") { - BMCWEB_LOG_ERROR("Pid Profiles Field illegal"); - messages::internalError(asyncResp->res); - return; + const std::string* namePtr = + std::get_if<std::string>(&propPair.second); + if (namePtr == nullptr) + { + BMCWEB_LOG_ERROR("Pid Name Field illegal"); + messages::internalError(asyncResp->res); + return; + } + name = *namePtr; + dbus::utility::escapePathForDbus(name); } - if (std::find(profiles->begin(), profiles->end(), - currentProfile) == profiles->end()) + else if (propPair.first == "Profiles") { - BMCWEB_LOG_INFO( - "{} not supported in current profile", name); - continue; + const std::vector<std::string>* profiles = + std::get_if<std::vector<std::string>>( + &propPair.second); + if (profiles == nullptr) + { + BMCWEB_LOG_ERROR("Pid Profiles Field illegal"); + messages::internalError(asyncResp->res); + return; + } + if (std::find(profiles->begin(), profiles->end(), + currentProfile) == profiles->end()) + { + BMCWEB_LOG_INFO( + "{} not supported in current profile", + name); + continue; + } } } - } - nlohmann::json* config = nullptr; - const std::string* classPtr = nullptr; - - for (const std::pair<std::string, - dbus::utility::DbusVariantType>& propPair : - intfPair.second) - { - if (propPair.first == "Class") - { - classPtr = std::get_if<std::string>(&propPair.second); - } - } + nlohmann::json* config = nullptr; + const std::string* classPtr = nullptr; - boost::urls::url url( - boost::urls::format("/redfish/v1/Managers/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME)); - if (intfPair.first == pidZoneConfigurationIface) - { - std::string chassis; - if (!dbus::utility::getNthStringFromPath(pathPair.first.str, - 5, chassis)) + for (const std::pair<std::string, + dbus::utility::DbusVariantType>& + propPair : intfPair.second) { - chassis = "#IllegalValue"; - } - nlohmann::json& zone = zones[name]; - zone["Chassis"]["@odata.id"] = - boost::urls::format("/redfish/v1/Chassis/{}", chassis); - url.set_fragment( - ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name) - .to_string()); - zone["@odata.id"] = std::move(url); - zone["@odata.type"] = "#OemManager.FanZone"; - config = &zone; - } - - else if (intfPair.first == stepwiseConfigurationIface) - { - if (classPtr == nullptr) - { - BMCWEB_LOG_ERROR("Pid Class Field illegal"); - messages::internalError(asyncResp->res); - return; + if (propPair.first == "Class") + { + classPtr = + std::get_if<std::string>(&propPair.second); + } } - nlohmann::json& controller = stepwise[name]; - config = &controller; - url.set_fragment( - ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer / - name) - .to_string()); - controller["@odata.id"] = std::move(url); - controller["@odata.type"] = - "#OemManager.StepwiseController"; - - controller["Direction"] = *classPtr; - } - - // pid and fans are off the same configuration - else if (intfPair.first == pidConfigurationIface) - { - if (classPtr == nullptr) - { - BMCWEB_LOG_ERROR("Pid Class Field illegal"); - messages::internalError(asyncResp->res); - return; - } - bool isFan = *classPtr == "fan"; - nlohmann::json& element = isFan ? fans[name] : pids[name]; - config = &element; - if (isFan) + boost::urls::url url( + boost::urls::format("/redfish/v1/Managers/{}", + BMCWEB_REDFISH_MANAGER_URI_NAME)); + if (intfPair.first == pidZoneConfigurationIface) { + std::string chassis; + if (!dbus::utility::getNthStringFromPath( + pathPair.first.str, 5, chassis)) + { + chassis = "#IllegalValue"; + } + nlohmann::json& zone = zones[name]; + zone["Chassis"]["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}", chassis); url.set_fragment( - ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer / - name) + ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / name) .to_string()); - element["@odata.id"] = std::move(url); - element["@odata.type"] = "#OemManager.FanController"; + zone["@odata.id"] = std::move(url); + zone["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.FanZone"; + config = &zone; } - else + + else if (intfPair.first == stepwiseConfigurationIface) { + if (classPtr == nullptr) + { + BMCWEB_LOG_ERROR("Pid Class Field illegal"); + messages::internalError(asyncResp->res); + return; + } + + nlohmann::json& controller = stepwise[name]; + config = &controller; url.set_fragment( - ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer / + ("/Oem/OpenBmc/Fan/StepwiseControllers"_json_pointer / name) .to_string()); - element["@odata.id"] = std::move(url); - element["@odata.type"] = "#OemManager.PidController"; - } - } - else - { - BMCWEB_LOG_ERROR("Unexpected configuration"); - messages::internalError(asyncResp->res); - return; - } - - // used for making maps out of 2 vectors - const std::vector<double>* keys = nullptr; - const std::vector<double>* values = nullptr; + controller["@odata.id"] = std::move(url); + controller["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.StepwiseController"; - for (const auto& propertyPair : intfPair.second) - { - if (propertyPair.first == "Type" || - propertyPair.first == "Class" || - propertyPair.first == "Name") - { - continue; + controller["Direction"] = *classPtr; } - // zones - if (intfPair.first == pidZoneConfigurationIface) + // pid and fans are off the same configuration + else if (intfPair.first == pidConfigurationIface) { - const double* ptr = - std::get_if<double>(&propertyPair.second); - if (ptr == nullptr) + if (classPtr == nullptr) { - BMCWEB_LOG_ERROR("Field Illegal {}", - propertyPair.first); + BMCWEB_LOG_ERROR("Pid Class Field illegal"); messages::internalError(asyncResp->res); return; } - (*config)[propertyPair.first] = *ptr; + bool isFan = *classPtr == "fan"; + nlohmann::json& element = + isFan ? fans[name] : pids[name]; + config = &element; + if (isFan) + { + url.set_fragment( + ("/Oem/OpenBmc/Fan/FanControllers"_json_pointer / + name) + .to_string()); + element["@odata.id"] = std::move(url); + element["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.FanController"; + } + else + { + url.set_fragment( + ("/Oem/OpenBmc/Fan/PidControllers"_json_pointer / + name) + .to_string()); + element["@odata.id"] = std::move(url); + element["@odata.type"] = + "#OpenBMCManager.v1_0_0.Manager.PidController"; + } } + else + { + BMCWEB_LOG_ERROR("Unexpected configuration"); + messages::internalError(asyncResp->res); + return; + } + + // used for making maps out of 2 vectors + const std::vector<double>* keys = nullptr; + const std::vector<double>* values = nullptr; - if (intfPair.first == stepwiseConfigurationIface) + for (const auto& propertyPair : intfPair.second) { - if (propertyPair.first == "Reading" || - propertyPair.first == "Output") + if (propertyPair.first == "Type" || + propertyPair.first == "Class" || + propertyPair.first == "Name") { - const std::vector<double>* ptr = - std::get_if<std::vector<double>>( - &propertyPair.second); + continue; + } + // zones + if (intfPair.first == pidZoneConfigurationIface) + { + const double* ptr = + std::get_if<double>(&propertyPair.second); if (ptr == nullptr) { BMCWEB_LOG_ERROR("Field Illegal {}", @@ -557,178 +556,202 @@ inline void messages::internalError(asyncResp->res); return; } + (*config)[propertyPair.first] = *ptr; + } - if (propertyPair.first == "Reading") - { - keys = ptr; - } - else - { - values = ptr; - } - if (keys != nullptr && values != nullptr) + if (intfPair.first == stepwiseConfigurationIface) + { + if (propertyPair.first == "Reading" || + propertyPair.first == "Output") { - if (keys->size() != values->size()) + const std::vector<double>* ptr = + std::get_if<std::vector<double>>( + &propertyPair.second); + + if (ptr == nullptr) { - BMCWEB_LOG_ERROR( - "Reading and Output size don't match "); + BMCWEB_LOG_ERROR("Field Illegal {}", + propertyPair.first); messages::internalError(asyncResp->res); return; } - nlohmann::json& steps = (*config)["Steps"]; - steps = nlohmann::json::array(); - for (size_t ii = 0; ii < keys->size(); ii++) + + if (propertyPair.first == "Reading") + { + keys = ptr; + } + else { - nlohmann::json::object_t step; - step["Target"] = (*keys)[ii]; - step["Output"] = (*values)[ii]; - steps.emplace_back(std::move(step)); + values = ptr; + } + if (keys != nullptr && values != nullptr) + { + if (keys->size() != values->size()) + { + BMCWEB_LOG_ERROR( + "Reading and Output size don't match "); + messages::internalError(asyncResp->res); + return; + } + nlohmann::json& steps = (*config)["Steps"]; + steps = nlohmann::json::array(); + for (size_t ii = 0; ii < keys->size(); ii++) + { + nlohmann::json::object_t step; + step["Target"] = (*keys)[ii]; + step["Output"] = (*values)[ii]; + steps.emplace_back(std::move(step)); + } } } - } - if (propertyPair.first == "NegativeHysteresis" || - propertyPair.first == "PositiveHysteresis") - { - const double* ptr = - std::get_if<double>(&propertyPair.second); - if (ptr == nullptr) + if (propertyPair.first == "NegativeHysteresis" || + propertyPair.first == "PositiveHysteresis") { - BMCWEB_LOG_ERROR("Field Illegal {}", - propertyPair.first); - messages::internalError(asyncResp->res); - return; + const double* ptr = + std::get_if<double>(&propertyPair.second); + if (ptr == nullptr) + { + BMCWEB_LOG_ERROR("Field Illegal {}", + propertyPair.first); + messages::internalError(asyncResp->res); + return; + } + (*config)[propertyPair.first] = *ptr; } - (*config)[propertyPair.first] = *ptr; } - } - - // pid and fans are off the same configuration - if (intfPair.first == pidConfigurationIface || - intfPair.first == stepwiseConfigurationIface) - { - if (propertyPair.first == "Zones") - { - const std::vector<std::string>* inputs = - std::get_if<std::vector<std::string>>( - &propertyPair.second); - if (inputs == nullptr) - { - BMCWEB_LOG_ERROR("Zones Pid Field Illegal"); - messages::internalError(asyncResp->res); - return; - } - auto& data = (*config)[propertyPair.first]; - data = nlohmann::json::array(); - for (std::string itemCopy : *inputs) - { - dbus::utility::escapePathForDbus(itemCopy); - nlohmann::json::object_t input; - boost::urls::url managerUrl = boost::urls::format( - "/redfish/v1/Managers/{}#{}", - BMCWEB_REDFISH_MANAGER_URI_NAME, - ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / - itemCopy) - .to_string()); - input["@odata.id"] = std::move(managerUrl); - data.emplace_back(std::move(input)); - } - } - // todo(james): may never happen, but this - // assumes configuration data referenced in the - // PID config is provided by the same daemon, we - // could add another loop to cover all cases, - // but I'm okay kicking this can down the road a - // bit - - else if (propertyPair.first == "Inputs" || - propertyPair.first == "Outputs") + // pid and fans are off the same configuration + if (intfPair.first == pidConfigurationIface || + intfPair.first == stepwiseConfigurationIface) { - auto& data = (*config)[propertyPair.first]; - const std::vector<std::string>* inputs = - std::get_if<std::vector<std::string>>( - &propertyPair.second); - - if (inputs == nullptr) + if (propertyPair.first == "Zones") { - BMCWEB_LOG_ERROR("Field Illegal {}", - propertyPair.first); - messages::internalError(asyncResp->res); - return; - } - data = *inputs; - } - else if (propertyPair.first == "SetPointOffset") - { - const std::string* ptr = - std::get_if<std::string>(&propertyPair.second); + const std::vector<std::string>* inputs = + std::get_if<std::vector<std::string>>( + &propertyPair.second); - if (ptr == nullptr) - { - BMCWEB_LOG_ERROR("Field Illegal {}", - propertyPair.first); - messages::internalError(asyncResp->res); - return; - } - // translate from dbus to redfish - if (*ptr == "WarningHigh") - { - (*config)["SetPointOffset"] = - "UpperThresholdNonCritical"; - } - else if (*ptr == "WarningLow") - { - (*config)["SetPointOffset"] = - "LowerThresholdNonCritical"; - } - else if (*ptr == "CriticalHigh") - { - (*config)["SetPointOffset"] = - "UpperThresholdCritical"; + if (inputs == nullptr) + { + BMCWEB_LOG_ERROR("Zones Pid Field Illegal"); + messages::internalError(asyncResp->res); + return; + } + auto& data = (*config)[propertyPair.first]; + data = nlohmann::json::array(); + for (std::string itemCopy : *inputs) + { + dbus::utility::escapePathForDbus(itemCopy); + nlohmann::json::object_t input; + boost::urls::url managerUrl = + boost::urls::format( + "/redfish/v1/Managers/{}#{}", + BMCWEB_REDFISH_MANAGER_URI_NAME, + ("/Oem/OpenBmc/Fan/FanZones"_json_pointer / + itemCopy) + .to_string()); + input["@odata.id"] = std::move(managerUrl); + data.emplace_back(std::move(input)); + } } - else if (*ptr == "CriticalLow") + // todo(james): may never happen, but this + // assumes configuration data referenced in the + // PID config is provided by the same daemon, we + // could add another loop to cover all cases, + // but I'm okay kicking this can down the road a + // bit + + else if (propertyPair.first == "Inputs" || + propertyPair.first == "Outputs") { - (*config)["SetPointOffset"] = - "LowerThresholdCritical"; + auto& data = (*config)[propertyPair.first]; + const std::vector<std::string>* inputs = + std::get_if<std::vector<std::string>>( + &propertyPair.second); + + if (inputs == nullptr) + { + BMCWEB_LOG_ERROR("Field Illegal {}", + propertyPair.first); + messages::internalError(asyncResp->res); + return; + } + data = *inputs; } - else + else if (propertyPair.first == "SetPointOffset") { - BMCWEB_LOG_ERROR("Value Illegal {}", *ptr); - messages::internalError(asyncResp->res); - return; + const std::string* ptr = + std::get_if<std::string>( + &propertyPair.second); + + if (ptr == nullptr) + { + BMCWEB_LOG_ERROR("Field Illegal {}", + propertyPair.first); + messages::internalError(asyncResp->res); + return; + } + // translate from dbus to redfish + if (*ptr == "WarningHigh") + { + (*config)["SetPointOffset"] = + "UpperThresholdNonCritical"; + } + else if (*ptr == "WarningLow") + { + (*config)["SetPointOffset"] = + "LowerThresholdNonCritical"; + } + else if (*ptr == "CriticalHigh") + { + (*config)["SetPointOffset"] = + "UpperThresholdCritical"; + } + else if (*ptr == "CriticalLow") + { + (*config)["SetPointOffset"] = + "LowerThresholdCritical"; + } + else + { + BMCWEB_LOG_ERROR("Value Illegal {}", *ptr); + messages::internalError(asyncResp->res); + return; + } } - } - // doubles - else if (propertyPair.first == "FFGainCoefficient" || - propertyPair.first == "FFOffCoefficient" || - propertyPair.first == "ICoefficient" || - propertyPair.first == "ILimitMax" || - propertyPair.first == "ILimitMin" || - propertyPair.first == "PositiveHysteresis" || - propertyPair.first == "NegativeHysteresis" || - propertyPair.first == "OutLimitMax" || - propertyPair.first == "OutLimitMin" || - propertyPair.first == "PCoefficient" || - propertyPair.first == "SetPoint" || - propertyPair.first == "SlewNeg" || - propertyPair.first == "SlewPos") - { - const double* ptr = - std::get_if<double>(&propertyPair.second); - if (ptr == nullptr) + // doubles + else if (propertyPair.first == + "FFGainCoefficient" || + propertyPair.first == "FFOffCoefficient" || + propertyPair.first == "ICoefficient" || + propertyPair.first == "ILimitMax" || + propertyPair.first == "ILimitMin" || + propertyPair.first == + "PositiveHysteresis" || + propertyPair.first == + "NegativeHysteresis" || + propertyPair.first == "OutLimitMax" || + propertyPair.first == "OutLimitMin" || + propertyPair.first == "PCoefficient" || + propertyPair.first == "SetPoint" || + propertyPair.first == "SlewNeg" || + propertyPair.first == "SlewPos") { - BMCWEB_LOG_ERROR("Field Illegal {}", - propertyPair.first); - messages::internalError(asyncResp->res); - return; + const double* ptr = + std::get_if<double>(&propertyPair.second); + if (ptr == nullptr) + { + BMCWEB_LOG_ERROR("Field Illegal {}", + propertyPair.first); + messages::internalError(asyncResp->res); + return; + } + (*config)[propertyPair.first] = *ptr; } - (*config)[propertyPair.first] = *ptr; } } } } - } - }); + }); } enum class CreatePIDRet @@ -841,14 +864,14 @@ inline CreatePIDRet createPidInterface( // delete interface crow::connections::systemBus->async_method_call( [response, path](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Error patching {}: {}", path, ec); - messages::internalError(response->res); - return; - } - messages::success(response->res); - }, + if (ec) + { + BMCWEB_LOG_ERROR("Error patching {}: {}", path, ec); + messages::internalError(response->res); + return; + } + messages::success(response->res); + }, "xyz.openbmc_project.EntityManager", path, iface, "Delete"); return CreatePIDRet::del; } @@ -909,8 +932,8 @@ inline CreatePIDRet createPidInterface( return CreatePIDRet::fail; } if (std::find(curProfiles->begin(), - curProfiles->end(), - profile) == curProfiles->end()) + curProfiles->end(), profile) == + curProfiles->end()) { std::vector<std::string> newProfiles = *curProfiles; @@ -1046,10 +1069,10 @@ inline CreatePIDRet createPidInterface( std::optional<std::string> chassisId; std::optional<double> failSafePercent; std::optional<double> minThermalOutput; - if (!redfish::json_util::readJson(jsonValue, response->res, - "Chassis/@odata.id", chassisId, - "FailSafePercent", failSafePercent, - "MinThermalOutput", minThermalOutput)) + if (!redfish::json_util::readJson( + jsonValue, response->res, "Chassis/@odata.id", chassisId, + "FailSafePercent", failSafePercent, "MinThermalOutput", + minThermalOutput)) { return CreatePIDRet::fail; } @@ -1199,14 +1222,14 @@ struct GetPIDValues : std::enable_shared_from_this<GetPIDValues> [self]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { - if (ec) - { - BMCWEB_LOG_ERROR("{}", ec); - messages::internalError(self->asyncResp->res); - return; - } - self->complete.subtree = subtreeLocal; - }); + if (ec) + { + BMCWEB_LOG_ERROR("{}", ec); + messages::internalError(self->asyncResp->res); + return; + } + self->complete.subtree = subtreeLocal; + }); // at the same time get the selected profile constexpr std::array<std::string_view, 1> thermalModeIfaces = { @@ -1216,58 +1239,61 @@ struct GetPIDValues : std::enable_shared_from_this<GetPIDValues> [self]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtreeLocal) { - if (ec || subtreeLocal.empty()) - { - return; - } - if (subtreeLocal[0].second.size() != 1) - { - // invalid mapper response, should never happen - BMCWEB_LOG_ERROR("GetPIDValues: Mapper Error"); - messages::internalError(self->asyncResp->res); - return; - } - - const std::string& path = subtreeLocal[0].first; - const std::string& owner = subtreeLocal[0].second[0].first; - - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, owner, path, thermalModeIface, - [path, owner, - self](const boost::system::error_code& ec2, - const dbus::utility::DBusPropertiesMap& resp) { - if (ec2) + if (ec || subtreeLocal.empty()) { - BMCWEB_LOG_ERROR( - "GetPIDValues: Can't get thermalModeIface {}", path); - messages::internalError(self->asyncResp->res); return; } - - const std::string* current = nullptr; - const std::vector<std::string>* supported = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), resp, "Current", current, - "Supported", supported); - - if (!success) + if (subtreeLocal[0].second.size() != 1) { + // invalid mapper response, should never happen + BMCWEB_LOG_ERROR("GetPIDValues: Mapper Error"); messages::internalError(self->asyncResp->res); return; } - if (current == nullptr || supported == nullptr) - { - BMCWEB_LOG_ERROR( - "GetPIDValues: thermal mode iface invalid {}", path); - messages::internalError(self->asyncResp->res); - return; - } - self->complete.currentProfile = *current; - self->complete.supportedProfiles = *supported; + const std::string& path = subtreeLocal[0].first; + const std::string& owner = subtreeLocal[0].second[0].first; + + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, owner, path, + thermalModeIface, + [path, owner, + self](const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& resp) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "GetPIDValues: Can't get thermalModeIface {}", + path); + messages::internalError(self->asyncResp->res); + return; + } + + const std::string* current = nullptr; + const std::vector<std::string>* supported = nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), resp, "Current", + current, "Supported", supported); + + if (!success) + { + messages::internalError(self->asyncResp->res); + return; + } + + if (current == nullptr || supported == nullptr) + { + BMCWEB_LOG_ERROR( + "GetPIDValues: thermal mode iface invalid {}", + path); + messages::internalError(self->asyncResp->res); + return; + } + self->complete.currentProfile = *current; + self->complete.supportedProfiles = *supported; + }); }); - }); } static void @@ -1354,8 +1380,7 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> std::pair<std::string, std::optional<nlohmann::json::object_t>>>&& configurationsIn, std::optional<std::string>& profileIn) : - asyncResp(asyncRespIn), - configuration(std::move(configurationsIn)), + asyncResp(asyncRespIn), configuration(std::move(configurationsIn)), profile(std::move(profileIn)) {} @@ -1381,30 +1406,30 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> "xyz.openbmc_project.EntityManager", objPath, [self](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& mObj) { - if (ec) - { - BMCWEB_LOG_ERROR("Error communicating to Entity Manager"); - messages::internalError(self->asyncResp->res); - return; - } - const std::array<const char*, 3> configurations = { - pidConfigurationIface, pidZoneConfigurationIface, - stepwiseConfigurationIface}; + if (ec) + { + BMCWEB_LOG_ERROR("Error communicating to Entity Manager"); + messages::internalError(self->asyncResp->res); + return; + } + const std::array<const char*, 3> configurations = { + pidConfigurationIface, pidZoneConfigurationIface, + stepwiseConfigurationIface}; - for (const auto& [path, object] : mObj) - { - for (const auto& [interface, _] : object) + for (const auto& [path, object] : mObj) { - if (std::ranges::find(configurations, interface) != - configurations.end()) + for (const auto& [interface, _] : object) { - self->objectCount++; - break; + if (std::ranges::find(configurations, interface) != + configurations.end()) + { + self->objectCount++; + break; + } } } - } - self->managedObj = mObj; - }); + self->managedObj = mObj; + }); // at the same time get the profile information constexpr std::array<std::string_view, 1> thermalModeIfaces = { @@ -1413,57 +1438,61 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> "/", 0, thermalModeIfaces, [self](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec || subtree.empty()) - { - return; - } - if (subtree[0].second.empty()) - { - // invalid mapper response, should never happen - BMCWEB_LOG_ERROR("SetPIDValues: Mapper Error"); - messages::internalError(self->asyncResp->res); - return; - } - - const std::string& path = subtree[0].first; - const std::string& owner = subtree[0].second[0].first; - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, owner, path, thermalModeIface, - [self, path, owner](const boost::system::error_code& ec2, - const dbus::utility::DBusPropertiesMap& r) { - if (ec2) + if (ec || subtree.empty()) { - BMCWEB_LOG_ERROR( - "SetPIDValues: Can't get thermalModeIface {}", path); - messages::internalError(self->asyncResp->res); return; } - const std::string* current = nullptr; - const std::vector<std::string>* supported = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), r, "Current", current, - "Supported", supported); - - if (!success) + if (subtree[0].second.empty()) { + // invalid mapper response, should never happen + BMCWEB_LOG_ERROR("SetPIDValues: Mapper Error"); messages::internalError(self->asyncResp->res); return; } - if (current == nullptr || supported == nullptr) - { - BMCWEB_LOG_ERROR( - "SetPIDValues: thermal mode iface invalid {}", path); - messages::internalError(self->asyncResp->res); - return; - } - self->currentProfile = *current; - self->supportedProfiles = *supported; - self->profileConnection = owner; - self->profilePath = path; + const std::string& path = subtree[0].first; + const std::string& owner = subtree[0].second[0].first; + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, owner, path, + thermalModeIface, + [self, path, + owner](const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& r) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "SetPIDValues: Can't get thermalModeIface {}", + path); + messages::internalError(self->asyncResp->res); + return; + } + const std::string* current = nullptr; + const std::vector<std::string>* supported = nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), r, "Current", + current, "Supported", supported); + + if (!success) + { + messages::internalError(self->asyncResp->res); + return; + } + + if (current == nullptr || supported == nullptr) + { + BMCWEB_LOG_ERROR( + "SetPIDValues: thermal mode iface invalid {}", + path); + messages::internalError(self->asyncResp->res); + return; + } + self->currentProfile = *current; + self->supportedProfiles = *supported; + self->profileConnection = owner; + self->profilePath = path; + }); }); - }); } void pidSetDone() { @@ -1486,12 +1515,12 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> *crow::connections::systemBus, profileConnection, profilePath, thermalModeIface, "Current", *profile, [response](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Error patching profile{}", ec); - messages::internalError(response->res); - } - }); + if (ec) + { + BMCWEB_LOG_ERROR("Error patching profile{}", ec); + messages::internalError(response->res); + } + }); } for (auto& containerPair : configuration) @@ -1512,8 +1541,8 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> auto pathItr = std::ranges::find_if( managedObj, [&dbusObjName](const auto& obj) { - return obj.first.filename() == dbusObjName; - }); + return obj.first.filename() == dbusObjName; + }); dbus::utility::DBusPropertiesMap output; output.reserve(16); // The pid interface length @@ -1614,15 +1643,15 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> [response, propertyName{std::string(property.first)}]( const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Error patching {}: {}", - propertyName, ec); - messages::internalError(response->res); - return; - } - messages::success(response->res); - }, + if (ec) + { + BMCWEB_LOG_ERROR("Error patching {}: {}", + propertyName, ec); + messages::internalError(response->res); + return; + } + messages::success(response->res); + }, "xyz.openbmc_project.EntityManager", path, "org.freedesktop.DBus.Properties", "Set", iface, property.first, property.second); @@ -1659,14 +1688,15 @@ struct SetPIDValues : std::enable_shared_from_this<SetPIDValues> crow::connections::systemBus->async_method_call( [response](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Error Adding Pid Object {}", ec); - messages::internalError(response->res); - return; - } - messages::success(response->res); - }, + if (ec) + { + BMCWEB_LOG_ERROR("Error Adding Pid Object {}", + ec); + messages::internalError(response->res); + return; + } + messages::success(response->res); + }, "xyz.openbmc_project.EntityManager", chassis, "xyz.openbmc_project.AddObject", "AddObject", output); } @@ -1717,17 +1747,18 @@ inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error for " - "Location"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error for " + "Location"); + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - property; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + property; + }); } // avoid name collision systems.hpp inline void @@ -1741,20 +1772,20 @@ inline void "LastRebootTime", [asyncResp](const boost::system::error_code& ec, const uint64_t lastResetTime) { - if (ec) - { - BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); + return; + } - // LastRebootTime is epoch time, in milliseconds - // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 - uint64_t lastResetTimeStamp = lastResetTime / 1000; + // LastRebootTime is epoch time, in milliseconds + // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 + uint64_t lastResetTimeStamp = lastResetTime / 1000; - // Convert to ISO 8601 standard - asyncResp->res.jsonValue["LastResetTime"] = - redfish::time_utils::getDateTimeUint(lastResetTimeStamp); - }); + // Convert to ISO 8601 standard + asyncResp->res.jsonValue["LastResetTime"] = + redfish::time_utils::getDateTimeUint(lastResetTimeStamp); + }); } /** @@ -1795,81 +1826,80 @@ inline void [asyncResp, firmwareId, runningFirmwareTarget]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); - messages::internalError(asyncResp->res); - return; - } - - if (subtree.empty()) - { - BMCWEB_LOG_DEBUG("Can't find image!"); - messages::internalError(asyncResp->res); - return; - } - - bool foundImage = false; - for (const auto& object : subtree) - { - const std::string& path = - static_cast<const std::string&>(object.first); - std::size_t idPos2 = path.rfind('/'); - - if (idPos2 == std::string::npos) + if (ec) { - continue; + BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); + messages::internalError(asyncResp->res); + return; } - idPos2++; - if (idPos2 >= path.size()) + if (subtree.empty()) { - continue; + BMCWEB_LOG_DEBUG("Can't find image!"); + messages::internalError(asyncResp->res); + return; } - if (path.substr(idPos2) == firmwareId) + bool foundImage = false; + for (const auto& object : subtree) { - foundImage = true; - break; - } - } + const std::string& path = + static_cast<const std::string&>(object.first); + std::size_t idPos2 = path.rfind('/'); - if (!foundImage) - { - messages::propertyValueNotInList( - asyncResp->res, runningFirmwareTarget, "@odata.id"); - BMCWEB_LOG_DEBUG("Invalid firmware ID."); - return; - } + if (idPos2 == std::string::npos) + { + continue; + } + + idPos2++; + if (idPos2 >= path.size()) + { + continue; + } + + if (path.substr(idPos2) == firmwareId) + { + foundImage = true; + break; + } + } - BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", - firmwareId); - - // Only support Immediate - // An addition could be a Redfish Setting like - // ActiveSoftwareImageApplyTime and support OnReset - sdbusplus::asio::setProperty( - *crow::connections::systemBus, - "xyz.openbmc_project.Software.BMC.Updater", - "/xyz/openbmc_project/software/" + firmwareId, - "xyz.openbmc_project.Software.RedundancyPriority", "Priority", - static_cast<uint8_t>(0), - [asyncResp](const boost::system::error_code& ec2) { - if (ec2) + if (!foundImage) { - BMCWEB_LOG_DEBUG("D-Bus response error setting."); - messages::internalError(asyncResp->res); + messages::propertyValueNotInList( + asyncResp->res, runningFirmwareTarget, "@odata.id"); + BMCWEB_LOG_DEBUG("Invalid firmware ID."); return; } - doBMCGracefulRestart(asyncResp); + + BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", + firmwareId); + + // Only support Immediate + // An addition could be a Redfish Setting like + // ActiveSoftwareImageApplyTime and support OnReset + sdbusplus::asio::setProperty( + *crow::connections::systemBus, + "xyz.openbmc_project.Software.BMC.Updater", + "/xyz/openbmc_project/software/" + firmwareId, + "xyz.openbmc_project.Software.RedundancyPriority", "Priority", + static_cast<uint8_t>(0), + [asyncResp](const boost::system::error_code& ec2) { + if (ec2) + { + BMCWEB_LOG_DEBUG("D-Bus response error setting."); + messages::internalError(asyncResp->res); + return; + } + doBMCGracefulRestart(asyncResp); + }); }); - }); } -inline void - afterSetDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const boost::system::error_code& ec, - const sdbusplus::message_t& msg) +inline void afterSetDateTime( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const boost::system::error_code& ec, const sdbusplus::message_t& msg) { if (ec) { @@ -1914,8 +1944,8 @@ inline void setDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec, const sdbusplus::message_t& msg) { - afterSetDateTime(asyncResp, ec, msg); - }, + afterSetDateTime(asyncResp, ec, msg); + }, "org.freedesktop.timedate1", "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "SetTime", us->count(), relative, interactive); @@ -1930,18 +1960,21 @@ inline void "org.freedesktop.systemd1.Unit", "ActiveState", [asyncResp](const boost::system::error_code& ec, const std::string& val) { - if (!ec) - { - if (val == "active") + if (!ec) { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; - asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; - return; + if (val == "active") + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Quiesced; + return; + } } - } - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - }); + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + }); } inline void requestRoutesManager(App& app) @@ -1950,295 +1983,321 @@ inline void requestRoutesManager(App& app) BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") .privileges(redfish::privileges::getManager) - .methods(boost::beast::http::verb::get)( - [&app, uuid](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } - - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_14_0.Manager"; - asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME; - asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; - asyncResp->res.jsonValue["Description"] = - "Baseboard Management Controller"; - asyncResp->res.jsonValue["PowerState"] = "On"; - - asyncResp->res.jsonValue["ManagerType"] = "BMC"; - asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); - asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; - asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model - - asyncResp->res.jsonValue["LogServices"]["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/LogServices", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - if constexpr (BMCWEB_VM_NBDPROXY) - { - asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", - BMCWEB_REDFISH_MANAGER_URI_NAME); - } - - // default oem data - nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; - nlohmann::json& oemOpenbmc = oem["OpenBmc"]; - oem["@odata.type"] = "#OemManager.Oem"; - oem["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}#/Oem", - BMCWEB_REDFISH_MANAGER_URI_NAME); - oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; - oemOpenbmc["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - nlohmann::json::object_t certificates; - certificates["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/Truststore/Certificates", - BMCWEB_REDFISH_MANAGER_URI_NAME); - oemOpenbmc["Certificates"] = std::move(certificates); - - // Manager.Reset (an action) can be many values, OpenBMC only - // supports BMC reboot. - nlohmann::json& managerReset = - asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; - managerReset["target"] = - boost::urls::format("/redfish/v1/Managers/{}/Actions/Manager.Reset", - BMCWEB_REDFISH_MANAGER_URI_NAME); - managerReset["@Redfish.ActionInfo"] = - boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - // ResetToDefaults (Factory Reset) has values like - // PreserveNetworkAndUsers and PreserveNetwork that aren't supported - // on OpenBMC - nlohmann::json& resetToDefaults = - asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; - resetToDefaults["target"] = boost::urls::format( - "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", - BMCWEB_REDFISH_MANAGER_URI_NAME); - resetToDefaults["ResetType@Redfish.AllowableValues"] = - nlohmann::json::array_t({"ResetAll"}); - - std::pair<std::string, std::string> redfishDateTimeOffset = - redfish::time_utils::getDateTimeOffsetNow(); - - asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; - asyncResp->res.jsonValue["DateTimeLocalOffset"] = - redfishDateTimeOffset.second; - - // TODO (Gunnar): Remove these one day since moved to ComputerSystem - // Still used by OCP profiles - // https://github.com/opencomputeproject/OCP-Profiles/issues/23 - // Fill in SerialConsole info - asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; - asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; - asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = - nlohmann::json::array_t({"IPMI", "SSH"}); - if constexpr (BMCWEB_KVM) - { - // Fill in GraphicalConsole info - asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = - true; - asyncResp->res - .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4; - asyncResp->res - .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = - nlohmann::json::array_t({"KVMIP"}); - } - if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = - 1; - - nlohmann::json::array_t managerForServers; - nlohmann::json::object_t manager; - manager["@odata.id"] = std::format("/redfish/v1/Systems/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME); - managerForServers.emplace_back(std::move(manager)); - - asyncResp->res.jsonValue["Links"]["ManagerForServers"] = - std::move(managerForServers); - } - - sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, - "FirmwareVersion", true); - - managerGetLastResetTime(asyncResp); - - // ManagerDiagnosticData is added for all BMCs. - nlohmann::json& managerDiagnosticData = - asyncResp->res.jsonValue["ManagerDiagnosticData"]; - managerDiagnosticData["@odata.id"] = - boost::urls::format("/redfish/v1/Managers/{}/ManagerDiagnosticData", - BMCWEB_REDFISH_MANAGER_URI_NAME); - - if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) - { - auto pids = std::make_shared<GetPIDValues>(asyncResp); - pids->run(); - } - - getMainChassisId(asyncResp, - [](const std::string& chassisId, - const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { - aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; - nlohmann::json::array_t managerForChassis; - nlohmann::json::object_t managerObj; - boost::urls::url chassiUrl = - boost::urls::format("/redfish/v1/Chassis/{}", chassisId); - managerObj["@odata.id"] = chassiUrl; - managerForChassis.emplace_back(std::move(managerObj)); - aRsp->res.jsonValue["Links"]["ManagerForChassis"] = - std::move(managerForChassis); - aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = - chassiUrl; - }); - - sdbusplus::asio::getProperty<double>( - *crow::connections::systemBus, "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "Progress", - [asyncResp](const boost::system::error_code& ec, double val) { - if (ec) + .methods( + boost::beast::http::verb:: + get)([&app, + uuid](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& managerId) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - BMCWEB_LOG_ERROR("Error while getting progress"); - messages::internalError(asyncResp->res); return; } - if (val < 1.0) + + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) { - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; - asyncResp->res.jsonValue["Status"]["State"] = "Starting"; + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); return; } - checkForQuiesced(asyncResp); - }); - constexpr std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.Inventory.Item.Bmc"}; - dbus::utility::getSubTree( - "/xyz/openbmc_project/inventory", 0, interfaces, - [asyncResp]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = + "#Manager.v1_14_0.Manager"; + asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME; + asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; + asyncResp->res.jsonValue["Description"] = + "Baseboard Management Controller"; + asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On; + + asyncResp->res.jsonValue["ManagerType"] = manager::ManagerType::BMC; + asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); + asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; + asyncResp->res.jsonValue["Model"] = + "OpenBmc"; // TODO(ed), get model + + asyncResp->res.jsonValue["LogServices"]["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}/LogServices", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol", + BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = + boost::urls::format( + "/redfish/v1/Managers/{}/EthernetInterfaces", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + if constexpr (BMCWEB_VM_NBDPROXY) { - BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); - return; + asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", + BMCWEB_REDFISH_MANAGER_URI_NAME); } - if (subtree.empty()) + + // default oem data + nlohmann::json& oem = asyncResp->res.jsonValue["Oem"]; + nlohmann::json& oemOpenbmc = oem["OpenBmc"]; + oem["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}#/Oem", + BMCWEB_REDFISH_MANAGER_URI_NAME); + oemOpenbmc["@odata.type"] = "#OpenBMCManager.v1_0_0.Manager"; + oemOpenbmc["@odata.id"] = + boost::urls::format("/redfish/v1/Managers/{}#/Oem/OpenBmc", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + nlohmann::json::object_t certificates; + certificates["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/Truststore/Certificates", + BMCWEB_REDFISH_MANAGER_URI_NAME); + oemOpenbmc["Certificates"] = std::move(certificates); + + // Manager.Reset (an action) can be many values, OpenBMC only + // supports BMC reboot. + nlohmann::json& managerReset = + asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; + managerReset["target"] = boost::urls::format( + "/redfish/v1/Managers/{}/Actions/Manager.Reset", + BMCWEB_REDFISH_MANAGER_URI_NAME); + managerReset["@Redfish.ActionInfo"] = + boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo", + BMCWEB_REDFISH_MANAGER_URI_NAME); + + // ResetToDefaults (Factory Reset) has values like + // PreserveNetworkAndUsers and PreserveNetwork that aren't supported + // on OpenBMC + nlohmann::json& resetToDefaults = + asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; + resetToDefaults["target"] = boost::urls::format( + "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", + BMCWEB_REDFISH_MANAGER_URI_NAME); + resetToDefaults["ResetType@Redfish.AllowableValues"] = + nlohmann::json::array_t({"ResetAll"}); + + std::pair<std::string, std::string> redfishDateTimeOffset = + redfish::time_utils::getDateTimeOffsetNow(); + + asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; + asyncResp->res.jsonValue["DateTimeLocalOffset"] = + redfishDateTimeOffset.second; + + // TODO (Gunnar): Remove these one day since moved to ComputerSystem + // Still used by OCP profiles + // https://github.com/opencomputeproject/OCP-Profiles/issues/23 + // Fill in SerialConsole info + asyncResp->res.jsonValue["SerialConsole"]["ServiceEnabled"] = true; + asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = + 15; + asyncResp->res.jsonValue["SerialConsole"]["ConnectTypesSupported"] = + nlohmann::json::array_t({"IPMI", "SSH"}); + if constexpr (BMCWEB_KVM) { - BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); - return; + // Fill in GraphicalConsole info + asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = + true; + asyncResp->res + .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4; + asyncResp->res + .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = + nlohmann::json::array_t({"KVMIP"}); } - // Assume only 1 bmc D-Bus object - // Throw an error if there is more than 1 - if (subtree.size() > 1) + if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!"); - messages::internalError(asyncResp->res); - return; - } + asyncResp->res + .jsonValue["Links"]["ManagerForServers@odata.count"] = 1; - if (subtree[0].first.empty() || subtree[0].second.size() != 1) - { - BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); - messages::internalError(asyncResp->res); - return; + nlohmann::json::array_t managerForServers; + nlohmann::json::object_t manager; + manager["@odata.id"] = std::format( + "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); + managerForServers.emplace_back(std::move(manager)); + + asyncResp->res.jsonValue["Links"]["ManagerForServers"] = + std::move(managerForServers); } - const std::string& path = subtree[0].first; - const std::string& connectionName = subtree[0].second[0].first; + sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, + "FirmwareVersion", true); - for (const auto& interfaceName : subtree[0].second[0].second) - { - if (interfaceName == - "xyz.openbmc_project.Inventory.Decorator.Asset") - { - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, connectionName, path, - "xyz.openbmc_project.Inventory.Decorator.Asset", - [asyncResp](const boost::system::error_code& ec2, - const dbus::utility::DBusPropertiesMap& - propertiesList) { - if (ec2) - { - BMCWEB_LOG_DEBUG("Can't get bmc asset!"); - return; - } + managerGetLastResetTime(asyncResp); - const std::string* partNumber = nullptr; - const std::string* serialNumber = nullptr; - const std::string* manufacturer = nullptr; - const std::string* model = nullptr; - const std::string* sparePartNumber = nullptr; + // ManagerDiagnosticData is added for all BMCs. + nlohmann::json& managerDiagnosticData = + asyncResp->res.jsonValue["ManagerDiagnosticData"]; + managerDiagnosticData["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/ManagerDiagnosticData", + BMCWEB_REDFISH_MANAGER_URI_NAME); - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, - "PartNumber", partNumber, "SerialNumber", - serialNumber, "Manufacturer", manufacturer, "Model", - model, "SparePartNumber", sparePartNumber); + if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) + { + auto pids = std::make_shared<GetPIDValues>(asyncResp); + pids->run(); + } - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + getMainChassisId(asyncResp, [](const std::string& chassisId, + const std::shared_ptr< + bmcweb::AsyncResp>& aRsp) { + aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = + 1; + nlohmann::json::array_t managerForChassis; + nlohmann::json::object_t managerObj; + boost::urls::url chassiUrl = + boost::urls::format("/redfish/v1/Chassis/{}", chassisId); + managerObj["@odata.id"] = chassiUrl; + managerForChassis.emplace_back(std::move(managerObj)); + aRsp->res.jsonValue["Links"]["ManagerForChassis"] = + std::move(managerForChassis); + aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = + chassiUrl; + }); - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = - *partNumber; - } + sdbusplus::asio::getProperty<double>( + *crow::connections::systemBus, "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", + "Progress", + [asyncResp](const boost::system::error_code& ec, double val) { + if (ec) + { + BMCWEB_LOG_ERROR("Error while getting progress"); + messages::internalError(asyncResp->res); + return; + } + if (val < 1.0) + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::OK; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Starting; + return; + } + checkForQuiesced(asyncResp); + }); - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = - *serialNumber; - } + constexpr std::array<std::string_view, 1> interfaces = { + "xyz.openbmc_project.Inventory.Item.Bmc"}; + dbus::utility::getSubTree( + "/xyz/openbmc_project/inventory", 0, interfaces, + [asyncResp]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree) { + if (ec) + { + BMCWEB_LOG_DEBUG( + "D-Bus response error on GetSubTree {}", ec); + return; + } + if (subtree.empty()) + { + BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); + return; + } + // Assume only 1 bmc D-Bus object + // Throw an error if there is more than 1 + if (subtree.size() > 1) + { + BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!"); + messages::internalError(asyncResp->res); + return; + } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = - *manufacturer; - } + if (subtree[0].first.empty() || + subtree[0].second.size() != 1) + { + BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); + messages::internalError(asyncResp->res); + return; + } + + const std::string& path = subtree[0].first; + const std::string& connectionName = + subtree[0].second[0].first; - if (model != nullptr) + for (const auto& interfaceName : + subtree[0].second[0].second) + { + if (interfaceName == + "xyz.openbmc_project.Inventory.Decorator.Asset") { - asyncResp->res.jsonValue["Model"] = *model; + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, connectionName, + path, + "xyz.openbmc_project.Inventory.Decorator.Asset", + [asyncResp]( + const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& + propertiesList) { + if (ec2) + { + BMCWEB_LOG_DEBUG( + "Can't get bmc asset!"); + return; + } + + const std::string* partNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* manufacturer = nullptr; + const std::string* model = nullptr; + const std::string* sparePartNumber = + nullptr; + + const bool success = + sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), + propertiesList, "PartNumber", + partNumber, "SerialNumber", + serialNumber, "Manufacturer", + manufacturer, "Model", model, + "SparePartNumber", sparePartNumber); + + if (!success) + { + messages::internalError(asyncResp->res); + return; + } + + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = + *partNumber; + } + + if (serialNumber != nullptr) + { + asyncResp->res + .jsonValue["SerialNumber"] = + *serialNumber; + } + + if (manufacturer != nullptr) + { + asyncResp->res + .jsonValue["Manufacturer"] = + *manufacturer; + } + + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = + *model; + } + + if (sparePartNumber != nullptr) + { + asyncResp->res + .jsonValue["SparePartNumber"] = + *sparePartNumber; + } + }); } - - if (sparePartNumber != nullptr) + else if ( + interfaceName == + "xyz.openbmc_project.Inventory.Decorator.LocationCode") { - asyncResp->res.jsonValue["SparePartNumber"] = - *sparePartNumber; + getLocation(asyncResp, connectionName, path); } - }); - } - else if (interfaceName == - "xyz.openbmc_project.Inventory.Decorator.LocationCode") - { - getLocation(asyncResp, connectionName, path); - } - } + } + }); }); - }); BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") .privileges(redfish::privileges::patchManager) @@ -2246,26 +2305,27 @@ inline void requestRoutesManager(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& managerId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "Manager", managerId); - return; - } + if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "Manager", + managerId); + return; + } - std::optional<std::string> activeSoftwareImageOdataId; - std::optional<std::string> datetime; - std::optional<nlohmann::json::object_t> pidControllers; - std::optional<nlohmann::json::object_t> fanControllers; - std::optional<nlohmann::json::object_t> fanZones; - std::optional<nlohmann::json::object_t> stepwiseControllers; - std::optional<std::string> profile; + std::optional<std::string> activeSoftwareImageOdataId; + std::optional<std::string> datetime; + std::optional<nlohmann::json::object_t> pidControllers; + std::optional<nlohmann::json::object_t> fanControllers; + std::optional<nlohmann::json::object_t> fanZones; + std::optional<nlohmann::json::object_t> stepwiseControllers; + std::optional<std::string> profile; - // clang-format off + // clang-format off if (!json_util::readJsonPatch(req, asyncResp->res, "DateTime", datetime, "Links/ActiveSoftwareImage/@odata.id", activeSoftwareImageOdataId, @@ -2278,56 +2338,60 @@ inline void requestRoutesManager(App& app) { return; } - // clang-format on + // clang-format on - if (pidControllers || fanControllers || fanZones || - stepwiseControllers || profile) - { - if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) - { - std::vector<std::pair<std::string, - std::optional<nlohmann::json::object_t>>> - configuration; - if (pidControllers) + if (pidControllers || fanControllers || fanZones || + stepwiseControllers || profile) { - configuration.emplace_back("PidControllers", - std::move(pidControllers)); - } - if (fanControllers) - { - configuration.emplace_back("FanControllers", - std::move(fanControllers)); + if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) + { + std::vector< + std::pair<std::string, + std::optional<nlohmann::json::object_t>>> + configuration; + if (pidControllers) + { + configuration.emplace_back( + "PidControllers", std::move(pidControllers)); + } + if (fanControllers) + { + configuration.emplace_back( + "FanControllers", std::move(fanControllers)); + } + if (fanZones) + { + configuration.emplace_back("FanZones", + std::move(fanZones)); + } + if (stepwiseControllers) + { + configuration.emplace_back( + "StepwiseControllers", + std::move(stepwiseControllers)); + } + auto pid = std::make_shared<SetPIDValues>( + asyncResp, std::move(configuration), profile); + pid->run(); + } + else + { + messages::propertyUnknown(asyncResp->res, "Oem"); + return; + } } - if (fanZones) + + if (activeSoftwareImageOdataId) { - configuration.emplace_back("FanZones", std::move(fanZones)); + setActiveFirmwareImage(asyncResp, + *activeSoftwareImageOdataId); } - if (stepwiseControllers) + + if (datetime) { - configuration.emplace_back("StepwiseControllers", - std::move(stepwiseControllers)); + setDateTime(asyncResp, *datetime); } - auto pid = std::make_shared<SetPIDValues>( - asyncResp, std::move(configuration), profile); - pid->run(); - } - else - { - messages::propertyUnknown(asyncResp->res, "Oem"); - return; - } - } - - if (activeSoftwareImageOdataId) - { - setActiveFirmwareImage(asyncResp, *activeSoftwareImageOdataId); - } - - if (datetime) - { - setDateTime(asyncResp, *datetime); - } - }); + }); } inline void requestRoutesManagerCollection(App& app) @@ -2337,22 +2401,22 @@ inline void requestRoutesManagerCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - // Collections don't include the static data added by SubRoute - // because it has a duplicate entry for members - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; - asyncResp->res.jsonValue["@odata.type"] = - "#ManagerCollection.ManagerCollection"; - asyncResp->res.jsonValue["Name"] = "Manager Collection"; - asyncResp->res.jsonValue["Members@odata.count"] = 1; - nlohmann::json::array_t members; - nlohmann::json& bmc = members.emplace_back(); - bmc["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", - BMCWEB_REDFISH_MANAGER_URI_NAME); - asyncResp->res.jsonValue["Members"] = std::move(members); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + // Collections don't include the static data added by SubRoute + // because it has a duplicate entry for members + asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; + asyncResp->res.jsonValue["@odata.type"] = + "#ManagerCollection.ManagerCollection"; + asyncResp->res.jsonValue["Name"] = "Manager Collection"; + asyncResp->res.jsonValue["Members@odata.count"] = 1; + nlohmann::json::array_t members; + nlohmann::json& bmc = members.emplace_back(); + bmc["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); + asyncResp->res.jsonValue["Members"] = std::move(members); + }); } } // namespace redfish diff --git a/redfish-core/lib/memory.hpp b/redfish-core/lib/memory.hpp index c77a5346a6..eb5fb2312c 100644 --- a/redfish-core/lib/memory.hpp +++ b/redfish-core/lib/memory.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -19,6 +19,8 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/memory.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/collection.hpp" @@ -368,39 +370,45 @@ inline void getPersistentMemoryProperties( if (dataLockCapable != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"] - ["DataLockCapable"] = *dataLockCapable; + asyncResp->res + .jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] = + *dataLockCapable; } if (passphraseCapable != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"] - ["PassphraseCapable"] = *passphraseCapable; + asyncResp->res + .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] = + *passphraseCapable; } if (maxPassphraseCount != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"] - ["MaxPassphraseCount"] = *maxPassphraseCount; + asyncResp->res + .jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] = + *maxPassphraseCount; } if (passphraseLockLimit != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["SecurityCapabilities"] - ["PassphraseLockLimit"] = *passphraseLockLimit; + asyncResp->res + .jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] = + *passphraseLockLimit; } } -inline void - assembleDimmProperties(std::string_view dimmId, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const dbus::utility::DBusPropertiesMap& properties, - const nlohmann::json::json_pointer& jsonPtr) +inline void assembleDimmProperties( + std::string_view dimmId, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const dbus::utility::DBusPropertiesMap& properties, + const nlohmann::json::json_pointer& jsonPtr) { asyncResp->res.jsonValue[jsonPtr]["Id"] = dimmId; asyncResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot"; - asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = + resource::State::Enabled; + asyncResp->res.jsonValue[jsonPtr]["Status"]["Health"] = + resource::Health::OK; const uint16_t* memoryDataWidth = nullptr; const size_t* memorySizeInKB = nullptr; @@ -477,7 +485,8 @@ inline void if (present != nullptr && !*present) { - asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; + asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = + resource::State::Absent; } if (memoryTotalWidth != nullptr) @@ -552,11 +561,13 @@ inline void } if (memoryType->find("DDR") != std::string::npos) { - asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = "DRAM"; + asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = + memory::MemoryType::DRAM; } else if (memoryType->ends_with("Logical")) { - asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = "IntelOptane"; + asyncResp->res.jsonValue[jsonPtr]["MemoryType"] = + memory::MemoryType::IntelOptane; } } @@ -568,8 +579,9 @@ inline void if (memoryController != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["MemoryLocation"] - ["MemoryController"] = *memoryController; + asyncResp->res + .jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] = + *memoryController; } if (slot != nullptr) @@ -594,17 +606,17 @@ inline void if (locationCode != nullptr) { - asyncResp->res.jsonValue[jsonPtr]["Location"]["PartLocation"] - ["ServiceLabel"] = *locationCode; + asyncResp->res + .jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] = + *locationCode; } getPersistentMemoryProperties(asyncResp, properties, jsonPtr); } -inline void getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, - const std::string& dimmId, - const std::string& service, - const std::string& objPath) +inline void getDimmDataByService( + std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& dimmId, + const std::string& service, const std::string& objPath) { BMCWEB_LOG_DEBUG("Get available system components."); sdbusplus::asio::getAllProperties( @@ -612,14 +624,15 @@ inline void getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [dimmId, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } - assembleDimmProperties(dimmId, asyncResp, properties, ""_json_pointer); - }); + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } + assembleDimmProperties(dimmId, asyncResp, properties, + ""_json_pointer); + }); } inline void assembleDimmPartitionData( @@ -685,16 +698,16 @@ inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); - return; + return; + } + nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer; + assembleDimmPartitionData(asyncResp, properties, regionPtr); } - nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer; - assembleDimmPartitionData(asyncResp, properties, regionPtr); - } ); } @@ -711,57 +724,57 @@ inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [dimmId, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); - return; - } - bool found = false; - for (const auto& [rawPath, object] : subtree) - { - sdbusplus::message::object_path path(rawPath); - for (const auto& [service, interfaces] : object) + return; + } + bool found = false; + for (const auto& [rawPath, object] : subtree) { - for (const auto& interface : interfaces) + sdbusplus::message::object_path path(rawPath); + for (const auto& [service, interfaces] : object) { - if (interface == - "xyz.openbmc_project.Inventory.Item.Dimm" && - path.filename() == dimmId) - { - getDimmDataByService(asyncResp, dimmId, service, - rawPath); - found = true; - } - - // partitions are separate as there can be multiple - // per - // device, i.e. - // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1 - // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2 - if (interface == - "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" && - path.parent_path().filename() == dimmId) + for (const auto& interface : interfaces) { - getDimmPartitionData(asyncResp, service, rawPath); + if (interface == + "xyz.openbmc_project.Inventory.Item.Dimm" && + path.filename() == dimmId) + { + getDimmDataByService(asyncResp, dimmId, service, + rawPath); + found = true; + } + + // partitions are separate as there can be multiple + // per + // device, i.e. + // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1 + // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2 + if (interface == + "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" && + path.parent_path().filename() == dimmId) + { + getDimmPartitionData(asyncResp, service, rawPath); + } } } } - } - // Object not found - if (!found) - { - messages::resourceNotFound(asyncResp->res, "Memory", dimmId); + // Object not found + if (!found) + { + messages::resourceNotFound(asyncResp->res, "Memory", dimmId); + return; + } + // Set @odata only if object is found + asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory"; + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format("/redfish/v1/Systems/{}/Memory/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId); return; - } - // Set @odata only if object is found - asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Systems/{}/Memory/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId); - return; - }); + }); } inline void requestRoutesMemoryCollection(App& app) @@ -775,38 +788,39 @@ inline void requestRoutesMemoryCollection(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - asyncResp->res.jsonValue["@odata.type"] = - "#MemoryCollection.MemoryCollection"; - asyncResp->res.jsonValue["Name"] = "Memory Module Collection"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/Memory", BMCWEB_REDFISH_SYSTEM_URI_NAME); - - constexpr std::array<std::string_view, 1> interfaces{ - "xyz.openbmc_project.Inventory.Item.Dimm"}; - collection_util::getCollectionMembers( - asyncResp, - boost::urls::format("/redfish/v1/Systems/{}/Memory", - BMCWEB_REDFISH_SYSTEM_URI_NAME), - interfaces, "/xyz/openbmc_project/inventory"); - }); + asyncResp->res.jsonValue["@odata.type"] = + "#MemoryCollection.MemoryCollection"; + asyncResp->res.jsonValue["Name"] = "Memory Module Collection"; + asyncResp->res.jsonValue["@odata.id"] = + boost::urls::format("/redfish/v1/Systems/{}/Memory", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + + constexpr std::array<std::string_view, 1> interfaces{ + "xyz.openbmc_project.Inventory.Item.Dimm"}; + collection_util::getCollectionMembers( + asyncResp, + boost::urls::format("/redfish/v1/Systems/{}/Memory", + BMCWEB_REDFISH_SYSTEM_URI_NAME), + interfaces, "/xyz/openbmc_project/inventory"); + }); } inline void requestRoutesMemory(App& app) @@ -820,28 +834,28 @@ inline void requestRoutesMemory(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& dimmId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - getDimmData(asyncResp, dimmId); - }); + getDimmData(asyncResp, dimmId); + }); } } // namespace redfish diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp index 94588ac1ca..c41b144023 100644 --- a/redfish-core/lib/message_registries.hpp +++ b/redfish-core/lib/message_registries.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2019 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -56,8 +56,8 @@ inline void handleMessageRegistryFileCollectionGet( {"Base", "TaskEvent", "ResourceEvent", "OpenBMC", "Telemetry"})) { nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format("/redfish/v1/Registries/{}", - memberName); + member["@odata.id"] = + boost::urls::format("/redfish/v1/Registries/{}", memberName); members.emplace_back(std::move(member)); } } @@ -123,8 +123,8 @@ inline void handleMessageRoutesMessageRegistryFileGet( asyncResp->res.jsonValue["@odata.type"] = "#MessageRegistryFile.v1_1_0.MessageRegistryFile"; asyncResp->res.jsonValue["Name"] = registry + " Message Registry File"; - asyncResp->res.jsonValue["Description"] = dmtf + registry + - " Message Registry File Location"; + asyncResp->res.jsonValue["Description"] = + dmtf + registry + " Message Registry File Location"; asyncResp->res.jsonValue["Id"] = header->registryPrefix; asyncResp->res.jsonValue["Registry"] = header->id; nlohmann::json::array_t languages; diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp index 3eb692776c..c47ba13181 100644 --- a/redfish-core/lib/metric_report.hpp +++ b/redfish-core/lib/metric_report.hpp @@ -65,24 +65,25 @@ inline void requestRoutesMetricReportCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - asyncResp->res.jsonValue["@odata.type"] = - "#MetricReportCollection.MetricReportCollection"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/TelemetryService/MetricReports"; - asyncResp->res.jsonValue["Name"] = "Metric Report Collection"; - constexpr std::array<std::string_view, 1> interfaces{ - telemetry::reportInterface}; - collection_util::getCollectionMembers( - asyncResp, - boost::urls::url("/redfish/v1/TelemetryService/MetricReports"), - interfaces, - "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + asyncResp->res.jsonValue["@odata.type"] = + "#MetricReportCollection.MetricReportCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReports"; + asyncResp->res.jsonValue["Name"] = "Metric Report Collection"; + constexpr std::array<std::string_view, 1> interfaces{ + telemetry::reportInterface}; + collection_util::getCollectionMembers( + asyncResp, + boost::urls::url( + "/redfish/v1/TelemetryService/MetricReports"), + interfaces, + "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); + }); } inline void requestRoutesMetricReport(App& app) @@ -93,43 +94,49 @@ inline void requestRoutesMetricReport(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - const std::string reportPath = telemetry::getDbusReportPath(id); - crow::connections::systemBus->async_method_call( - [asyncResp, id, reportPath](const boost::system::error_code& ec) { - if (ec.value() == EBADR || - ec == boost::system::errc::host_unreachable) - { - messages::resourceNotFound(asyncResp->res, "MetricReport", id); - return; - } - if (ec) - { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - sdbusplus::asio::getProperty<telemetry::TimestampReadings>( - *crow::connections::systemBus, telemetry::service, reportPath, - telemetry::reportInterface, "Readings", - [asyncResp, id](const boost::system::error_code& ec2, - const telemetry::TimestampReadings& ret) { - if (ec2) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec2); - messages::internalError(asyncResp->res); return; } - - telemetry::fillReport(asyncResp->res.jsonValue, id, ret); + const std::string reportPath = telemetry::getDbusReportPath(id); + crow::connections::systemBus->async_method_call( + [asyncResp, id, + reportPath](const boost::system::error_code& ec) { + if (ec.value() == EBADR || + ec == boost::system::errc::host_unreachable) + { + messages::resourceNotFound(asyncResp->res, + "MetricReport", id); + return; + } + if (ec) + { + BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); + messages::internalError(asyncResp->res); + return; + } + + sdbusplus::asio::getProperty< + telemetry::TimestampReadings>( + *crow::connections::systemBus, telemetry::service, + reportPath, telemetry::reportInterface, "Readings", + [asyncResp, + id](const boost::system::error_code& ec2, + const telemetry::TimestampReadings& ret) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "respHandler DBus error {}", ec2); + messages::internalError(asyncResp->res); + return; + } + + telemetry::fillReport(asyncResp->res.jsonValue, + id, ret); + }); + }, + telemetry::service, reportPath, telemetry::reportInterface, + "Update"); }); - }, - telemetry::service, reportPath, telemetry::reportInterface, - "Update"); - }); } } // namespace redfish diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp index 36f2334638..d92343d0fe 100644 --- a/redfish-core/lib/metric_report_definition.hpp +++ b/redfish-core/lib/metric_report_definition.hpp @@ -3,6 +3,7 @@ #include "app.hpp" #include "dbus_utility.hpp" #include "generated/enums/metric_report_definition.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "sensors.hpp" @@ -245,10 +246,9 @@ inline std::optional<nlohmann::json::array_t> getLinkedTriggers( return triggers; } -inline void - fillReportDefinition(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& id, - const dbus::utility::DBusPropertiesMap& properties) +inline void fillReportDefinition( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const dbus::utility::DBusPropertiesMap& properties) { std::vector<std::string> reportActions; ReadingParameters readingParams; @@ -356,11 +356,11 @@ inline void if (enabled) { - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; } else { - asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Disabled; } metric_report_definition::ReportUpdatesEnum redfishReportUpdates = @@ -641,22 +641,120 @@ inline bool getChassisSensorNodeFromMetrics( getChassisSensorNode(metric.uris, matched); if (error) { - messages::propertyValueIncorrect(asyncResp->res, error->uri, - "MetricProperties/" + - std::to_string(error->index)); + messages::propertyValueIncorrect( + asyncResp->res, error->uri, + "MetricProperties/" + std::to_string(error->index)); + return false; + } + } + return true; +} + +inline std::string toRedfishProperty(std::string_view dbusMessage) +{ + if (dbusMessage == "Id") + { + return "Id"; + } + if (dbusMessage == "Name") + { + return "Name"; + } + if (dbusMessage == "ReportingType") + { + return "MetricReportDefinitionType"; + } + if (dbusMessage == "AppendLimit") + { + return "AppendLimit"; + } + if (dbusMessage == "ReportActions") + { + return "ReportActions"; + } + if (dbusMessage == "Interval") + { + return "RecurrenceInterval"; + } + if (dbusMessage == "ReportUpdates") + { + return "ReportUpdates"; + } + if (dbusMessage == "ReadingParameters") + { + return "Metrics"; + } + return ""; +} + +inline bool handleParamError(crow::Response& res, const char* errorMessage, + std::string_view key) +{ + if (errorMessage == nullptr) + { + BMCWEB_LOG_ERROR("errorMessage was null"); + return true; + } + std::string_view errorMessageSv(errorMessage); + if (errorMessageSv.starts_with(key)) + { + std::string redfishProperty = toRedfishProperty(key); + if (redfishProperty.empty()) + { + // Getting here means most possibly that toRedfishProperty has + // incomplete implementation. Return internal error for now. + BMCWEB_LOG_ERROR("{} has no corresponding Redfish property", key); + messages::internalError(res); return false; } + messages::propertyValueError(res, redfishProperty); + return false; } + return true; } +inline void afterAddReport(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const AddReportArgs& args, + const boost::system::error_code& ec, + const sdbusplus::message_t& msg) +{ + if (!ec) + { + messages::created(asyncResp->res); + return; + } + + if (ec == boost::system::errc::invalid_argument) + { + const sd_bus_error* errorMessage = msg.get_error(); + if (errorMessage != nullptr) + { + for (const auto& arg : + {"Id", "Name", "ReportingType", "AppendLimit", "ReportActions", + "Interval", "ReportUpdates", "ReadingParameters"}) + { + if (!handleParamError(asyncResp->res, errorMessage->message, + arg)) + { + return; + } + } + } + } + if (!verifyCommonErrors(asyncResp->res, args.id, ec)) + { + return; + } + messages::internalError(asyncResp->res); +} + class AddReport { public: AddReport(AddReportArgs&& argsIn, const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : - asyncResp(asyncRespIn), - args(std::move(argsIn)) + asyncResp(asyncRespIn), args(std::move(argsIn)) {} ~AddReport() @@ -695,9 +793,9 @@ class AddReport BMCWEB_LOG_ERROR( "Failed to find DBus sensor corresponding to URI {}", uri); - messages::propertyValueNotInList(asyncResp->res, uri, - "MetricProperties/" + - std::to_string(i)); + messages::propertyValueNotInList( + asyncResp->res, uri, + "MetricProperties/" + std::to_string(i)); return; } @@ -709,41 +807,12 @@ class AddReport std::move(sensorParams), metric.collectionFunction, metric.collectionTimeScope, metric.collectionDuration); } - crow::connections::systemBus->async_method_call( - [asyncResp, id = args.id, uriToDbus]( - const boost::system::error_code& ec, const std::string&) { - if (ec == boost::system::errc::file_exists) - { - messages::resourceAlreadyExists( - asyncResp->res, "MetricReportDefinition", "Id", id); - return; - } - if (ec == boost::system::errc::too_many_files_open) - { - messages::createLimitReachedForResource(asyncResp->res); - return; - } - if (ec == boost::system::errc::argument_list_too_long) - { - nlohmann::json metricProperties = nlohmann::json::array(); - for (const auto& [uri, _] : uriToDbus) - { - metricProperties.emplace_back(uri); - } - messages::propertyValueIncorrect( - asyncResp->res, "MetricProperties", metricProperties); - return; - } - if (ec) - { - messages::internalError(asyncResp->res); - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - return; - } - - messages::created(asyncResp->res); - }, + [asyncResp, args](const boost::system::error_code& ec, + const sdbusplus::message_t& msg, + const std::string& /*arg1*/) { + afterAddReport(asyncResp, args, ec, msg); + }, telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", "TelemetryService/" + args.id, args.name, args.reportingType, @@ -762,36 +831,128 @@ class AddReport } private: - std::shared_ptr<bmcweb::AsyncResp> asyncResp; + const std::shared_ptr<bmcweb::AsyncResp> asyncResp; AddReportArgs args; boost::container::flat_map<std::string, std::string> uriToDbus; }; -class UpdateMetrics +inline std::optional< + std::vector<std::tuple<sdbusplus::message::object_path, std::string>>> + sensorPathToUri( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + std::span<const std::string> uris, + const std::map<std::string, std::string>& metricPropertyToDbusPaths) { - public: - UpdateMetrics(std::string_view idIn, - const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : - id(idIn), - asyncResp(asyncRespIn) - {} + std::vector<std::tuple<sdbusplus::message::object_path, std::string>> + result; - ~UpdateMetrics() + for (const std::string& uri : uris) { - try + auto it = metricPropertyToDbusPaths.find(uri); + if (it == metricPropertyToDbusPaths.end()) { - setReadingParams(); + messages::propertyValueNotInList(asyncResp->res, uri, + "MetricProperties"); + return {}; } - catch (const std::exception& e) + result.emplace_back(it->second, uri); + } + + return result; +} + +inline void afterSetReadingParams( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& reportId, const boost::system::error_code& ec, + const sdbusplus::message_t& msg) +{ + if (!ec) + { + messages::success(asyncResp->res); + return; + } + if (ec == boost::system::errc::invalid_argument) + { + const sd_bus_error* errorMessage = msg.get_error(); + if (errorMessage != nullptr) { - BMCWEB_LOG_ERROR("{}", e.what()); + for (const auto& arg : {"Id", "ReadingParameters"}) + { + if (!handleParamError(asyncResp->res, errorMessage->message, + arg)) + { + return; + } + } } - catch (...) + } + if (!verifyCommonErrors(asyncResp->res, reportId, ec)) + { + return; + } + messages::internalError(asyncResp->res); +} + +inline void setReadingParams( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& reportId, ReadingParameters readingParams, + const std::vector<std::vector<std::string>>& readingParamsUris, + const std::map<std::string, std::string>& metricPropertyToDbusPaths) +{ + if (asyncResp->res.result() != boost::beast::http::status::ok) + { + return; + } + + for (size_t index = 0; index < readingParamsUris.size(); ++index) + { + std::span<const std::string> newUris = readingParamsUris[index]; + + const std::optional<std::vector< + std::tuple<sdbusplus::message::object_path, std::string>>> + readingParam = + sensorPathToUri(asyncResp, newUris, metricPropertyToDbusPaths); + + if (!readingParam) + { + return; + } + + for (const std::tuple<sdbusplus::message::object_path, std::string>& + value : *readingParam) { - BMCWEB_LOG_ERROR("Unknown error"); + std::get<0>(readingParams[index]).emplace_back(value); } } + crow::connections::systemBus->async_method_call( + [asyncResp, reportId](const boost::system::error_code& ec, + const sdbusplus::message_t& msg) { + afterSetReadingParams(asyncResp, reportId, ec, msg); + }, + "xyz.openbmc_project.Telemetry", getDbusReportPath(reportId), + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Telemetry.Report", "ReadingParameters", + dbus::utility::DbusVariantType{readingParams}); +} + +class UpdateMetrics +{ + public: + UpdateMetrics(std::string_view idIn, + const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : + id(idIn), asyncResp(asyncRespIn) + {} + + ~UpdateMetrics() + { + boost::asio::post( + crow::connections::systemBus->get_io_context(), + std::bind_front(&setReadingParams, asyncResp, id, + std::move(readingParams), readingParamsUris, + metricPropertyToDbusPaths)); + } + UpdateMetrics(const UpdateMetrics&) = delete; UpdateMetrics(UpdateMetrics&&) = delete; UpdateMetrics& operator=(const UpdateMetrics&) = delete; @@ -808,10 +969,11 @@ class UpdateMetrics additionalMetricPropertyToDbusPaths.end()); } - void emplace(std::span<const std::tuple<sdbusplus::message::object_path, - std::string>> - pathAndUri, - const AddReportArgs::MetricArgs& metricArgs) + void emplace( + std::span< + const std::tuple<sdbusplus::message::object_path, std::string>> + pathAndUri, + const AddReportArgs::MetricArgs& metricArgs) { readingParamsUris.emplace_back(metricArgs.uris); readingParams.emplace_back( @@ -820,66 +982,7 @@ class UpdateMetrics metricArgs.collectionDuration); } - void setReadingParams() - { - if (asyncResp->res.result() != boost::beast::http::status::ok) - { - return; - } - - for (size_t index = 0; index < readingParamsUris.size(); ++index) - { - std::span<const std::string> newUris = readingParamsUris[index]; - - const std::optional<std::vector< - std::tuple<sdbusplus::message::object_path, std::string>>> - readingParam = sensorPathToUri(newUris); - - if (!readingParam) - { - return; - } - - std::get<0>(readingParams[index]) = *readingParam; - } - - crow::connections::systemBus->async_method_call( - [asyncResp(this->asyncResp), - reportId = id](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, reportId, ec)) - { - return; - } - }, - "xyz.openbmc_project.Telemetry", getDbusReportPath(id), - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Telemetry.Report", "ReadingParameters", - dbus::utility::DbusVariantType{readingParams}); - } - private: - std::optional< - std::vector<std::tuple<sdbusplus::message::object_path, std::string>>> - sensorPathToUri(std::span<const std::string> uris) const - { - std::vector<std::tuple<sdbusplus::message::object_path, std::string>> - result; - - for (const std::string& uri : uris) - { - auto it = metricPropertyToDbusPaths.find(uri); - if (it == metricPropertyToDbusPaths.end()) - { - messages::propertyValueNotInList(asyncResp->res, uri, - "MetricProperties"); - return {}; - } - result.emplace_back(it->second, uri); - } - - return result; - } - const std::shared_ptr<bmcweb::AsyncResp> asyncResp; std::vector<std::vector<std::string>> readingParamsUris; ReadingParameters readingParams; @@ -891,160 +994,282 @@ inline void { crow::connections::systemBus->async_method_call( [asyncResp, id = std::string(id)](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, id, ec)) - { - return; - } - }, + if (!verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } + }, "xyz.openbmc_project.Telemetry", getDbusReportPath(id), "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.Telemetry.Report", "Enabled", dbus::utility::DbusVariantType{enabled}); } +inline void afterSetReportingProperties( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const boost::system::error_code& ec, const sdbusplus::message_t& msg) +{ + if (!ec) + { + asyncResp->res.result(boost::beast::http::status::no_content); + return; + } + + if (ec == boost::system::errc::invalid_argument) + { + const sd_bus_error* errorMessage = msg.get_error(); + if (errorMessage != nullptr) + { + for (const auto& arg : {"Id", "ReportingType", "Interval"}) + { + if (!handleParamError(asyncResp->res, errorMessage->message, + arg)) + { + return; + } + } + } + } + if (!verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } + messages::internalError(asyncResp->res); +} + inline void setReportTypeAndInterval( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, - const std::string& reportingType, uint64_t recurrenceInterval) + const std::optional<std::string>& reportingType, + const std::optional<std::string>& recurrenceIntervalStr) { - crow::connections::systemBus->async_method_call( - [asyncResp, id = std::string(id)](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, id, ec)) + std::string dbusReportingType; + if (reportingType) + { + dbusReportingType = toDbusReportingType(*reportingType); + if (dbusReportingType.empty()) { + messages::propertyValueNotInList(asyncResp->res, *reportingType, + "MetricReportDefinitionType"); return; } - }, + } + + uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max(); + if (recurrenceIntervalStr) + { + std::optional<std::chrono::milliseconds> durationNum = + time_utils::fromDurationString(*recurrenceIntervalStr); + if (!durationNum || durationNum->count() < 0) + { + messages::propertyValueIncorrect( + asyncResp->res, "RecurrenceInterval", *recurrenceIntervalStr); + return; + } + + recurrenceInterval = static_cast<uint64_t>(durationNum->count()); + } + + crow::connections::systemBus->async_method_call( + [asyncResp, id = std::string(id)](const boost::system::error_code& ec, + const sdbusplus::message_t& msg) { + afterSetReportingProperties(asyncResp, id, ec, msg); + }, "xyz.openbmc_project.Telemetry", getDbusReportPath(id), "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties", - reportingType, recurrenceInterval); + dbusReportingType, recurrenceInterval); +} + +inline void afterSetReportUpdates( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const boost::system::error_code& ec, const sdbusplus::message_t& msg) +{ + if (!ec) + { + asyncResp->res.result(boost::beast::http::status::no_content); + return; + } + if (ec == boost::system::errc::invalid_argument) + { + const sd_bus_error* errorMessage = msg.get_error(); + if (errorMessage != nullptr) + { + for (const auto& arg : {"Id", "ReportUpdates"}) + { + if (!handleParamError(asyncResp->res, errorMessage->message, + arg)) + { + return; + } + } + } + } + if (!verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } } inline void setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, const std::string& reportUpdates) { + std::string dbusReportUpdates = toDbusReportUpdates(reportUpdates); + if (dbusReportUpdates.empty()) + { + messages::propertyValueNotInList(asyncResp->res, reportUpdates, + "ReportUpdates"); + return; + } crow::connections::systemBus->async_method_call( - [asyncResp, id = std::string(id)](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, id, ec)) - { - return; - } - }, + [asyncResp, id = std::string(id)](const boost::system::error_code& ec, + const sdbusplus::message_t& msg) { + afterSetReportUpdates(asyncResp, id, ec, msg); + }, "xyz.openbmc_project.Telemetry", getDbusReportPath(id), "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.Telemetry.Report", "ReportUpdates", - dbus::utility::DbusVariantType{reportUpdates}); + dbus::utility::DbusVariantType{dbusReportUpdates}); } -inline void - setReportActions(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - std::string_view id, - const std::vector<std::string>& dbusReportActions) +inline void afterSetReportActions( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const boost::system::error_code& ec, const sdbusplus::message_t& msg) { - crow::connections::systemBus->async_method_call( - [asyncResp, id = std::string(id)](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, id, ec)) + if (ec == boost::system::errc::invalid_argument) + { + const sd_bus_error* errorMessage = msg.get_error(); + if (errorMessage != nullptr) { - return; + for (const auto& arg : {"Id", "ReportActions"}) + { + if (!handleParamError(asyncResp->res, errorMessage->message, + arg)) + { + return; + } + } } - }, + } + + if (!verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } + + messages::internalError(asyncResp->res); +} + +inline void setReportActions( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, + const std::vector<std::string>& reportActions) +{ + std::vector<std::string> dbusReportActions; + if (!toDbusReportActions(asyncResp->res, reportActions, dbusReportActions)) + { + return; + } + + crow::connections::systemBus->async_method_call( + [asyncResp, id = std::string(id)](const boost::system::error_code& ec, + const sdbusplus::message_t& msg) { + afterSetReportActions(asyncResp, id, ec, msg); + }, "xyz.openbmc_project.Telemetry", getDbusReportPath(id), "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.Telemetry.Report", "ReportActions", dbus::utility::DbusVariantType{dbusReportActions}); } -inline void - setReportMetrics(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - std::string_view id, - std::span<nlohmann::json::object_t> metrics) +inline void setReportMetrics( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, + std::vector<nlohmann::json::object_t>&& metrics) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, telemetry::service, telemetry::getDbusReportPath(id), telemetry::reportInterface, - [asyncResp, id = std::string(id), - redfishMetrics = std::vector<nlohmann::json::object_t>(metrics.begin(), - metrics.end())]( + [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)]( boost::system::error_code ec, const dbus::utility::DBusPropertiesMap& properties) mutable { - if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec)) - { - return; - } - - ReadingParameters readingParams; + if (!verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "ReadingParameters", - readingParams); + ReadingParameters readingParams; - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, + "ReadingParameters", readingParams); - auto updateMetricsReq = std::make_shared<UpdateMetrics>(id, asyncResp); + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - boost::container::flat_set<std::pair<std::string, std::string>> - chassisSensors; + auto updateMetricsReq = + std::make_shared<UpdateMetrics>(id, asyncResp); - size_t index = 0; - for (nlohmann::json::object_t& metric : redfishMetrics) - { - AddReportArgs::MetricArgs metricArgs; - std::vector< - std::tuple<sdbusplus::message::object_path, std::string>> - pathAndUri; + boost::container::flat_set<std::pair<std::string, std::string>> + chassisSensors; - if (index < readingParams.size()) + size_t index = 0; + for (nlohmann::json::object_t& metric : redfishMetrics) { - const ReadingParameters::value_type& existing = - readingParams[index]; - - pathAndUri = std::get<0>(existing); - metricArgs.collectionFunction = std::get<1>(existing); - metricArgs.collectionTimeScope = std::get<2>(existing); - metricArgs.collectionDuration = std::get<3>(existing); - } + AddReportArgs::MetricArgs metricArgs; + std::vector< + std::tuple<sdbusplus::message::object_path, std::string>> + pathAndUri; - if (!getUserMetric(asyncResp->res, metric, metricArgs)) - { - return; - } + if (index < readingParams.size()) + { + const ReadingParameters::value_type& existing = + readingParams[index]; - std::optional<IncorrectMetricUri> error = - getChassisSensorNode(metricArgs.uris, chassisSensors); + pathAndUri = std::get<0>(existing); + metricArgs.collectionFunction = std::get<1>(existing); + metricArgs.collectionTimeScope = std::get<2>(existing); + metricArgs.collectionDuration = std::get<3>(existing); + } - if (error) - { - messages::propertyValueIncorrect( - asyncResp->res, error->uri, - "MetricProperties/" + std::to_string(error->index)); - return; - } + if (!getUserMetric(asyncResp->res, metric, metricArgs)) + { + return; + } - updateMetricsReq->emplace(pathAndUri, metricArgs); - index++; - } + std::optional<IncorrectMetricUri> error = + getChassisSensorNode(metricArgs.uris, chassisSensors); - for (const auto& [chassis, sensorType] : chassisSensors) - { - retrieveUriToDbusMap( - chassis, sensorType, - [asyncResp, updateMetricsReq]( - const boost::beast::http::status status, - const std::map<std::string, std::string>& uriToDbus) { - if (status != boost::beast::http::status::ok) + if (error) { - BMCWEB_LOG_ERROR( - "Failed to retrieve URI to dbus sensors map with err {}", - static_cast<unsigned>(status)); + messages::propertyValueIncorrect( + asyncResp->res, error->uri, + "MetricProperties/" + std::to_string(error->index)); return; } - updateMetricsReq->insert(uriToDbus); - }); - } - }); + + updateMetricsReq->emplace(pathAndUri, metricArgs); + index++; + } + + for (const auto& [chassis, sensorType] : chassisSensors) + { + retrieveUriToDbusMap( + chassis, sensorType, + [asyncResp, updateMetricsReq]( + const boost::beast::http::status status, + const std::map<std::string, std::string>& uriToDbus) { + if (status != boost::beast::http::status::ok) + { + BMCWEB_LOG_ERROR( + "Failed to retrieve URI to dbus sensors map with err {}", + static_cast<unsigned>(status)); + return; + } + updateMetricsReq->insert(uriToDbus); + }); + } + }); } inline void handleMetricReportDefinitionCollectionHead( @@ -1087,10 +1312,9 @@ inline void handleMetricReportDefinitionCollectionGet( interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); } -inline void - handleReportPatch(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - std::string_view id) +inline void handleReportPatch( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -1121,71 +1345,29 @@ inline void if (reportUpdatesStr) { - std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr); - if (dbusReportUpdates.empty()) - { - messages::propertyValueNotInList(asyncResp->res, *reportUpdatesStr, - "ReportUpdates"); - return; - } - setReportUpdates(asyncResp, id, dbusReportUpdates); + setReportUpdates(asyncResp, id, *reportUpdatesStr); } if (reportActionsStr) { - std::vector<std::string> dbusReportActions; - if (!toDbusReportActions(asyncResp->res, *reportActionsStr, - dbusReportActions)) - { - return; - } - setReportActions(asyncResp, id, dbusReportActions); + setReportActions(asyncResp, id, *reportActionsStr); } if (reportingTypeStr || scheduleDurationStr) { - std::string dbusReportingType; - if (reportingTypeStr) - { - dbusReportingType = toDbusReportingType(*reportingTypeStr); - if (dbusReportingType.empty()) - { - messages::propertyValueNotInList(asyncResp->res, - *reportingTypeStr, - "MetricReportDefinitionType"); - return; - } - } - - uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max(); - if (scheduleDurationStr) - { - std::optional<std::chrono::milliseconds> durationNum = - time_utils::fromDurationString(*scheduleDurationStr); - if (!durationNum || durationNum->count() < 0) - { - messages::propertyValueIncorrect( - asyncResp->res, "RecurrenceInterval", *scheduleDurationStr); - return; - } - - recurrenceInterval = static_cast<uint64_t>(durationNum->count()); - } - - setReportTypeAndInterval(asyncResp, id, dbusReportingType, - recurrenceInterval); + setReportTypeAndInterval(asyncResp, id, reportingTypeStr, + scheduleDurationStr); } if (metrics) { - setReportMetrics(asyncResp, id, *metrics); + setReportMetrics(asyncResp, id, std::move(*metrics)); } } -inline void - handleReportDelete(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - std::string_view id) +inline void handleReportDelete( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -1197,12 +1379,12 @@ inline void crow::connections::systemBus->async_method_call( [asyncResp, reportId = std::string(id)](const boost::system::error_code& ec) { - if (!verifyCommonErrors(asyncResp->res, reportId, ec)) - { - return; - } - asyncResp->res.result(boost::beast::http::status::no_content); - }, + if (!verifyCommonErrors(asyncResp->res, reportId, ec)) + { + return; + } + asyncResp->res.result(boost::beast::http::status::no_content); + }, service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete"); } } // namespace telemetry @@ -1246,8 +1428,8 @@ inline void handleMetricReportDefinitionsPost( return; } - auto addReportReq = std::make_shared<telemetry::AddReport>(std::move(args), - asyncResp); + auto addReportReq = + std::make_shared<telemetry::AddReport>(std::move(args), asyncResp); for (const auto& [chassis, sensorType] : chassisSensors) { retrieveUriToDbusMap(chassis, sensorType, @@ -1270,10 +1452,9 @@ inline void "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); } -inline void - handleMetricReportGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& id) +inline void handleMetricReportGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -1288,13 +1469,13 @@ inline void telemetry::getDbusReportPath(id), telemetry::reportInterface, [asyncResp, id](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec)) - { - return; - } + if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec)) + { + return; + } - telemetry::fillReportDefinition(asyncResp, id, properties); - }); + telemetry::fillReportDefinition(asyncResp, id, properties); + }); } inline void handleMetricReportDelete( @@ -1311,26 +1492,26 @@ inline void handleMetricReportDelete( crow::connections::systemBus->async_method_call( [asyncResp, id](const boost::system::error_code& ec) { - /* - * boost::system::errc and std::errc are missing value - * for EBADR error that is defined in Linux. - */ - if (ec.value() == EBADR) - { - messages::resourceNotFound(asyncResp->res, "MetricReportDefinition", - id); - return; - } + /* + * boost::system::errc and std::errc are missing value + * for EBADR error that is defined in Linux. + */ + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, + "MetricReportDefinition", id); + return; + } - if (ec) - { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.result(boost::beast::http::status::no_content); - }, + asyncResp->res.result(boost::beast::http::status::no_content); + }, telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete"); } diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp index 410c32cc4c..f5b64f8aa7 100644 --- a/redfish-core/lib/network_protocol.hpp +++ b/redfish-core/lib/network_protocol.hpp @@ -1,23 +1,24 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "redfish_util.hpp" #include "registries/privilege_registry.hpp" @@ -32,6 +33,7 @@ #include <optional> #include <string_view> #include <variant> +#include <vector> namespace redfish { @@ -52,7 +54,8 @@ static constexpr std::array<std::pair<std::string_view, std::string_view>, 3> inline void extractNTPServersAndDomainNamesData( const dbus::utility::ManagedObjectType& dbusData, - std::vector<std::string>& ntpData, std::vector<std::string>& dnData) + std::vector<std::string>& ntpData, std::vector<std::string>& dynamicNtpData, + std::vector<std::string>& dnData) { for (const auto& obj : dbusData) { @@ -77,6 +80,16 @@ inline void extractNTPServersAndDomainNamesData( ntpServers->end()); } } + else if (propertyPair.first == "NTPServers") + { + const std::vector<std::string>* dynamicNtpServers = + std::get_if<std::vector<std::string>>( + &propertyPair.second); + if (dynamicNtpServers != nullptr) + { + dynamicNtpData = *dynamicNtpServers; + } + } else if (propertyPair.first == "DomainName") { const std::vector<std::string>* domainNames = @@ -104,19 +117,21 @@ void getEthernetIfaceData(CallbackFunc&& callback) [callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& dbusData) { - std::vector<std::string> ntpServers; - std::vector<std::string> domainNames; + std::vector<std::string> ntpServers; + std::vector<std::string> dynamicNtpServers; + std::vector<std::string> domainNames; - if (ec) - { - callback(false, ntpServers, domainNames); - return; - } + if (ec) + { + callback(false, ntpServers, dynamicNtpServers, domainNames); + return; + } - extractNTPServersAndDomainNamesData(dbusData, ntpServers, domainNames); + extractNTPServersAndDomainNamesData(dbusData, ntpServers, + dynamicNtpServers, domainNames); - callback(true, ntpServers, domainNames); - }); + callback(true, ntpServers, dynamicNtpServers, domainNames); + }); } inline void afterNetworkPortRequest( @@ -164,16 +179,16 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, boost::beast::http::field::link, "</redfish/v1/JsonSchemas/ManagerNetworkProtocol/NetworkProtocol.json>; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = - "#ManagerNetworkProtocol.v1_5_0.ManagerNetworkProtocol"; + "#ManagerNetworkProtocol.v1_9_0.ManagerNetworkProtocol"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol", BMCWEB_REDFISH_MANAGER_URI_NAME); asyncResp->res.jsonValue["Id"] = "NetworkProtocol"; asyncResp->res.jsonValue["Name"] = "Manager Network Protocol"; asyncResp->res.jsonValue["Description"] = "Manager Network Service"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; - asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; + asyncResp->res.jsonValue["Status"]["HealthRollup"] = resource::Health::OK; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; // HTTP is Mandatory attribute as per OCP Baseline Profile - v1.0.0, // but from security perspective it is not recommended to use. @@ -199,10 +214,11 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, getNTPProtocolEnabled(asyncResp); - getEthernetIfaceData( - [hostName, asyncResp](const bool& success, - const std::vector<std::string>& ntpServers, - const std::vector<std::string>& domainNames) { + getEthernetIfaceData([hostName, asyncResp]( + const bool& success, + const std::vector<std::string>& ntpServers, + const std::vector<std::string>& dynamicNtpServers, + const std::vector<std::string>& domainNames) { if (!success) { messages::resourceNotFound(asyncResp->res, "ManagerNetworkProtocol", @@ -210,6 +226,8 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, return; } asyncResp->res.jsonValue["NTP"]["NTPServers"] = ntpServers; + asyncResp->res.jsonValue["NTP"]["NetworkSuppliedServers"] = + dynamicNtpServers; if (!hostName.empty()) { std::string fqdn = hostName; @@ -289,9 +307,9 @@ inline void // Can't delete an item that doesn't exist if (currentNtpServer == currentNtpServers.end()) { - messages::propertyValueNotInList(asyncResp->res, "null", - "NTP/NTPServers/" + - std::to_string(index)); + messages::propertyValueNotInList( + asyncResp->res, "null", + "NTP/NTPServers/" + std::to_string(index)); return; } @@ -350,32 +368,32 @@ inline void [asyncResp, currentNtpServers]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); + messages::internalError(asyncResp->res); + return; + } - for (const auto& [objectPath, serviceMap] : subtree) - { - for (const auto& [service, interfaces] : serviceMap) + for (const auto& [objectPath, serviceMap] : subtree) { - for (const auto& interface : interfaces) + for (const auto& [service, interfaces] : serviceMap) { - if (interface != - "xyz.openbmc_project.Network.EthernetInterface") + for (const auto& interface : interfaces) { - continue; + if (interface != + "xyz.openbmc_project.Network.EthernetInterface") + { + continue; + } + + setDbusProperty(asyncResp, "NTP/NTPServers/", service, + objectPath, interface, + "StaticNTPServers", currentNtpServers); } - - setDbusProperty(asyncResp, "NTP/NTPServers/", service, - objectPath, interface, "StaticNTPServers", - currentNtpServers); } } - } - }); + }); } inline void @@ -390,29 +408,29 @@ inline void [protocolEnabled, asyncResp, netBasePath](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + messages::internalError(asyncResp->res); + return; + } - for (const auto& entry : subtree) - { - if (entry.first.starts_with(netBasePath)) + for (const auto& entry : subtree) { - setDbusProperty( - asyncResp, "IPMI/ProtocolEnabled", - entry.second.begin()->first, entry.first, - "xyz.openbmc_project.Control.Service.Attributes", "Running", - protocolEnabled); - setDbusProperty( - asyncResp, "IPMI/ProtocolEnabled", - entry.second.begin()->first, entry.first, - "xyz.openbmc_project.Control.Service.Attributes", "Enabled", - protocolEnabled); + if (entry.first.starts_with(netBasePath)) + { + setDbusProperty( + asyncResp, "IPMI/ProtocolEnabled", + entry.second.begin()->first, entry.first, + "xyz.openbmc_project.Control.Service.Attributes", + "Running", protocolEnabled); + setDbusProperty( + asyncResp, "IPMI/ProtocolEnabled", + entry.second.begin()->first, entry.first, + "xyz.openbmc_project.Control.Service.Attributes", + "Enabled", protocolEnabled); + } } - } - }); + }); } inline std::string getHostName() @@ -434,15 +452,15 @@ inline void *crow::connections::systemBus, "org.freedesktop.timedate1", "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", [asyncResp](const boost::system::error_code& ec, bool enabled) { - if (ec) - { - BMCWEB_LOG_WARNING( - "Failed to get NTP status, assuming not supported"); - return; - } + if (ec) + { + BMCWEB_LOG_WARNING( + "Failed to get NTP status, assuming not supported"); + return; + } - asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = enabled; - }); + asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = enabled; + }); } inline std::string encodeServiceObjectPath(std::string_view serviceName) @@ -518,15 +536,16 @@ inline void handleManagersNetworkProtocolPatch( getEthernetIfaceData( [asyncResp, ntpServerObjects]( const bool success, std::vector<std::string>& currentNtpServers, + const std::vector<std::string>& /*dynamicNtpServers*/, const std::vector<std::string>& /*domainNames*/) { - if (!success) - { - messages::internalError(asyncResp->res); - return; - } - handleNTPServersPatch(asyncResp, *ntpServerObjects, - std::move(currentNtpServers)); - }); + if (!success) + { + messages::internalError(asyncResp->res); + return; + } + handleNTPServersPatch(asyncResp, *ntpServerObjects, + std::move(currentNtpServers)); + }); } if (ipmiEnabled) diff --git a/redfish-core/lib/odata.hpp b/redfish-core/lib/odata.hpp new file mode 100644 index 0000000000..beb26477a5 --- /dev/null +++ b/redfish-core/lib/odata.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "app.hpp" +#include "error_messages.hpp" +#include "http_request.hpp" +#include "http_response.hpp" +#include "query.hpp" +#include "registries/privilege_registry.hpp" +#include "utility.hpp" + +#include <boost/url/format.hpp> +#include <nlohmann/json.hpp> + +#include <memory> +#include <ranges> +#include <string> +#include <string_view> + +namespace redfish +{ + +inline void redfishOdataGet(const crow::Request& /*req*/, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) +{ + nlohmann::json::object_t obj; + obj["@odata.context"] = "/redfish/v1/$metadata"; + nlohmann::json::array_t value; + for (std::string_view service : + {"$metadata", "odata", "JsonSchemas", "Service", "ServiceRoot", + "Systems", "Chassis", "Managers", "SessionService", "AccountService", + "UpdateService"}) + { + nlohmann::json::object_t serviceObj; + serviceObj["kind"] = "Singleton"; + serviceObj["name"] = "$metadata"; + boost::urls::url url = boost::urls::format("/redfish/v1/{}", service); + if (service == "Service") + { + url = boost::urls::url("/redfish/v1"); + } + serviceObj["url"] = url; + value.emplace_back(std::move(serviceObj)); + } + + obj["value"] = std::move(value); + + asyncResp->res.jsonValue = std::move(obj); +} + +inline void requestRoutesOdata(App& app) +{ + BMCWEB_ROUTE(app, "/redfish/v1/odata/") + .methods(boost::beast::http::verb::get)(redfishOdataGet); +} + +} // namespace redfish diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp index 3cfed3a3ca..7ddbaa0177 100644 --- a/redfish-core/lib/pcie.hpp +++ b/redfish-core/lib/pcie.hpp @@ -1,23 +1,24 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/collection.hpp" @@ -40,7 +41,7 @@ static constexpr std::array<std::string_view, 1> pcieDeviceInterface = { static constexpr std::array<std::string_view, 1> pcieSlotInterface = { "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; -static inline void handlePCIeDevicePath( +inline void handlePCIeDevicePath( const std::string& pcieDeviceId, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths, @@ -58,18 +59,18 @@ static inline void handlePCIeDevicePath( } dbus::utility::getDbusObject( - pcieDevicePath, {}, + pcieDevicePath, pcieDeviceInterface, [pcieDevicePath, asyncResp, callback](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& object) { - if (ec || object.empty()) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - callback(pcieDevicePath, object.begin()->first); - }); + if (ec || object.empty()) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } + callback(pcieDevicePath, object.begin()->first); + }); return; } @@ -77,7 +78,7 @@ static inline void handlePCIeDevicePath( messages::resourceNotFound(asyncResp->res, "PCIeDevice", pcieDeviceId); } -static inline void getValidPCIeDevicePath( +inline void getValidPCIeDevicePath( const std::string& pcieDeviceId, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::function<void(const std::string& pcieDevicePath, @@ -89,19 +90,19 @@ static inline void getValidPCIeDevicePath( callback](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths) { - if (ec) - { - BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec); - messages::internalError(asyncResp->res); + if (ec) + { + BMCWEB_LOG_ERROR("D-Bus response error on GetSubTree {}", ec); + messages::internalError(asyncResp->res); + return; + } + handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths, + callback); return; - } - handlePCIeDevicePath(pcieDeviceId, asyncResp, pcieDevicePaths, - callback); - return; - }); + }); } -static inline void handlePCIeDeviceCollectionGet( +inline void handlePCIeDeviceCollectionGet( crow::App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName) @@ -226,43 +227,42 @@ inline void getPCIeDeviceSlotPath( [callback = std::move(callback), asyncResp, pcieDevicePath]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& endpoints) { - if (ec) - { - if (ec.value() == EBADR) + if (ec) { - // Missing association is not an error + if (ec.value() == EBADR) + { + // Missing association is not an error + return; + } + BMCWEB_LOG_ERROR( + "DBUS response error for getAssociatedSubTreePaths {}", + ec.value()); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_ERROR( - "DBUS response error for getAssociatedSubTreePaths {}", - ec.value()); - messages::internalError(asyncResp->res); - return; - } - if (endpoints.size() > 1) - { - BMCWEB_LOG_ERROR( - "PCIeDevice is associated with more than one PCIeSlot: {}", - endpoints.size()); - messages::internalError(asyncResp->res); - return; - } - if (endpoints.empty()) - { - // If the device doesn't have an association, return without PCIe - // Slot properties - BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot"); - return; - } - callback(endpoints[0]); - }); + if (endpoints.size() > 1) + { + BMCWEB_LOG_ERROR( + "PCIeDevice is associated with more than one PCIeSlot: {}", + endpoints.size()); + messages::internalError(asyncResp->res); + return; + } + if (endpoints.empty()) + { + // If the device doesn't have an association, return without + // PCIe Slot properties + BMCWEB_LOG_DEBUG("PCIeDevice is not associated with PCIeSlot"); + return; + } + callback(endpoints[0]); + }); } -inline void - afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& pcieDeviceSlot, - const boost::system::error_code& ec, - const dbus::utility::MapperGetObject& object) +inline void afterGetDbusObject( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& pcieDeviceSlot, const boost::system::error_code& ec, + const dbus::utility::MapperGetObject& object) { if (ec || object.empty()) { @@ -277,8 +277,8 @@ inline void [asyncResp]( const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& pcieSlotProperties) { - addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties); - }); + addPCIeSlotProperties(asyncResp->res, ec2, pcieSlotProperties); + }); } inline void afterGetPCIeDeviceSlotPath( @@ -290,67 +290,66 @@ inline void afterGetPCIeDeviceSlotPath( [asyncResp, pcieDeviceSlot](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& object) { - afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object); - }); + afterGetDbusObject(asyncResp, pcieDeviceSlot, ec, object); + }); } -inline void - getPCIeDeviceHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& pcieDevicePath, - const std::string& service) +inline void getPCIeDeviceHealth( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& pcieDevicePath, const std::string& service) { sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, service, pcieDevicePath, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp](const boost::system::error_code& ec, const bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Health {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Health {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; + } + }); } -inline void - getPCIeDeviceState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& pcieDevicePath, - const std::string& service) +inline void getPCIeDeviceState( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& pcieDevicePath, const std::string& service) { sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, service, pcieDevicePath, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for State"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for State"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); } -inline void - getPCIeDeviceAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& pcieDevicePath, - const std::string& service) +inline void getPCIeDeviceAsset( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& pcieDevicePath, const std::string& service) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, pcieDevicePath, @@ -358,58 +357,59 @@ inline void [pcieDevicePath, asyncResp{asyncResp}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& assetList) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Properties{}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Properties{}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - const std::string* manufacturer = nullptr; - const std::string* model = nullptr; - const std::string* partNumber = nullptr; - const std::string* serialNumber = nullptr; - const std::string* sparePartNumber = nullptr; + const std::string* manufacturer = nullptr; + const std::string* model = nullptr; + const std::string* partNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* sparePartNumber = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", - manufacturer, "Model", model, "PartNumber", partNumber, - "SerialNumber", serialNumber, "SparePartNumber", sparePartNumber); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), assetList, "Manufacturer", + manufacturer, "Model", model, "PartNumber", partNumber, + "SerialNumber", serialNumber, "SparePartNumber", + sparePartNumber); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; - } - if (model != nullptr) - { - asyncResp->res.jsonValue["Model"] = *model; - } + if (manufacturer != nullptr) + { + asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + } + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = *model; + } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } + if (serialNumber != nullptr) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } - if (sparePartNumber != nullptr && !sparePartNumber->empty()) - { - asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; - } - }); + if (sparePartNumber != nullptr && !sparePartNumber->empty()) + { + asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; + } + }); } inline void addPCIeDeviceProperties( @@ -520,17 +520,17 @@ inline void getPCIeDeviceProperties( [asyncResp, callback](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& pcieDevProperties) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Properties"); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Properties"); + messages::internalError(asyncResp->res); + } + return; } - return; - } - callback(pcieDevProperties); - }); + callback(pcieDevProperties); + }); } inline void addPCIeDeviceCommonProperties( @@ -546,8 +546,8 @@ inline void addPCIeDeviceCommonProperties( BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); asyncResp->res.jsonValue["Name"] = "PCIe Device"; asyncResp->res.jsonValue["Id"] = pcieDeviceId; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; } inline void afterGetValidPcieDevicePath( @@ -567,11 +567,10 @@ inline void afterGetValidPcieDevicePath( std::bind_front(afterGetPCIeDeviceSlotPath, asyncResp)); } -inline void - handlePCIeDeviceGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& pcieDeviceId) +inline void handlePCIeDeviceGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& pcieDeviceId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -616,8 +615,8 @@ inline void addPCIeFunctionList( { // Check if this function exists by // looking for a device ID - std::string devIDProperty = "Function" + std::to_string(functionNum) + - "DeviceId"; + std::string devIDProperty = + "Function" + std::to_string(functionNum) + "DeviceId"; const std::string* property = nullptr; for (const auto& propEntry : pcieDevProperties) { @@ -663,25 +662,25 @@ inline void handlePCIeFunctionCollectionGet( pcieDeviceId, asyncResp, [asyncResp, pcieDeviceId](const std::string& pcieDevicePath, const std::string& service) { - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = - "#PCIeFunctionCollection.PCIeFunctionCollection"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", - BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); - asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; - asyncResp->res.jsonValue["Description"] = - "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; - getPCIeDeviceProperties( - asyncResp, pcieDevicePath, service, - [asyncResp, pcieDeviceId]( - const dbus::utility::DBusPropertiesMap& pcieDevProperties) { - addPCIeFunctionList(asyncResp->res, pcieDeviceId, - pcieDevProperties); + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/PCIeFunctionCollection/PCIeFunctionCollection.json>; rel=describedby"); + asyncResp->res.jsonValue["@odata.type"] = + "#PCIeFunctionCollection.PCIeFunctionCollection"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/PCIeDevices/{}/PCIeFunctions", + BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); + asyncResp->res.jsonValue["Name"] = "PCIe Function Collection"; + asyncResp->res.jsonValue["Description"] = + "Collection of PCIe Functions for PCIe Device " + pcieDeviceId; + getPCIeDeviceProperties( + asyncResp, pcieDevicePath, service, + [asyncResp, pcieDeviceId]( + const dbus::utility::DBusPropertiesMap& pcieDevProperties) { + addPCIeFunctionList(asyncResp->res, pcieDeviceId, + pcieDevProperties); + }); }); - }); } inline void requestRoutesSystemPCIeFunctionCollection(App& app) @@ -726,7 +725,6 @@ inline void addPCIeFunctionProperties( std::get_if<std::string>(&property.second); if (strProperty == nullptr) { - BMCWEB_LOG_ERROR("Function wasn't a string?"); continue; } if (property.first == functionName + "DeviceId") @@ -793,12 +791,11 @@ inline void addPCIeFunctionCommonProperties(crow::Response& resp, BMCWEB_REDFISH_SYSTEM_URI_NAME, pcieDeviceId); } -inline void - handlePCIeFunctionGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& pcieDeviceId, - const std::string& pcieFunctionIdStr) +inline void handlePCIeFunctionGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& pcieDeviceId, + const std::string& pcieFunctionIdStr) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -829,20 +826,20 @@ inline void return; } - getValidPCIeDevicePath(pcieDeviceId, asyncResp, - [asyncResp, pcieDeviceId, - pcieFunctionId](const std::string& pcieDevicePath, - const std::string& service) { - getPCIeDeviceProperties( - asyncResp, pcieDevicePath, service, - [asyncResp, pcieDeviceId, pcieFunctionId]( - const dbus::utility::DBusPropertiesMap& pcieDevProperties) { - addPCIeFunctionCommonProperties(asyncResp->res, pcieDeviceId, - pcieFunctionId); - addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, - pcieDevProperties); + getValidPCIeDevicePath( + pcieDeviceId, asyncResp, + [asyncResp, pcieDeviceId, pcieFunctionId]( + const std::string& pcieDevicePath, const std::string& service) { + getPCIeDeviceProperties( + asyncResp, pcieDevicePath, service, + [asyncResp, pcieDeviceId, pcieFunctionId]( + const dbus::utility::DBusPropertiesMap& pcieDevProperties) { + addPCIeFunctionCommonProperties( + asyncResp->res, pcieDeviceId, pcieFunctionId); + addPCIeFunctionProperties(asyncResp->res, pcieFunctionId, + pcieDevProperties); + }); }); - }); } inline void requestRoutesSystemPCIeFunction(App& app) diff --git a/redfish-core/lib/pcie_slots.hpp b/redfish-core/lib/pcie_slots.hpp index b8e6389275..a6796b9913 100644 --- a/redfish-core/lib/pcie_slots.hpp +++ b/redfish-core/lib/pcie_slots.hpp @@ -153,15 +153,14 @@ inline void onMapperAssociationDone( "xyz.openbmc_project.Inventory.Item.PCIeSlot", [asyncResp](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& propertiesList) { - onPcieSlotGetAllDone(asyncResp, ec2, propertiesList); - }); + onPcieSlotGetAllDone(asyncResp, ec2, propertiesList); + }); } -inline void - onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisID, - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) +inline void onMapperSubtreeDone( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisID, const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { @@ -202,9 +201,9 @@ inline void [asyncResp, chassisID, pcieSlotPath, connectionName]( const boost::system::error_code& ec2, const dbus::utility::MapperEndPoints& endpoints) { - onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath, - connectionName, ec2, endpoints); - }); + onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath, + connectionName, ec2, endpoints); + }); } } } @@ -226,8 +225,8 @@ inline void handlePCIeSlotCollectionGet( [asyncResp, chassisID](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - onMapperSubtreeDone(asyncResp, chassisID, ec, subtree); - }); + onMapperSubtreeDone(asyncResp, chassisID, ec, subtree); + }); } inline void requestRoutesPCIeSlots(App& app) diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp index cd7fa0f08a..d2843615e0 100644 --- a/redfish-core/lib/power.hpp +++ b/redfish-core/lib/power.hpp @@ -1,28 +1,30 @@ /* -// Copyright (c) 2018 Intel Corporation -// Copyright (c) 2018 Ampere Computing LLC -/ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation +Copyright (c) 2018 Ampere Computing LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/power.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "sensors.hpp" #include "utils/chassis_utils.hpp" #include "utils/json_utils.hpp" +#include "utils/sensor_utils.hpp" #include <sdbusplus/asio/property.hpp> @@ -125,9 +127,9 @@ inline void afterPowerCapSettingGet( // A warning without a odata.type nlohmann::json::object_t powerControl; powerControl["@odata.type"] = "#Power.v1_0_0.PowerControl"; - powerControl["@odata.id"] = "/redfish/v1/Chassis/" + - sensorAsyncResp->chassisId + - "/Power#/PowerControl/0"; + powerControl["@odata.id"] = + "/redfish/v1/Chassis/" + sensorAsyncResp->chassisId + + "/Power#/PowerControl/0"; powerControl["Name"] = "Chassis Power Control"; powerControl["MemberId"] = "0"; tempArray.emplace_back(std::move(powerControl)); @@ -181,24 +183,24 @@ inline void afterPowerCapSettingGet( } // LimitException is Mandatory attribute as per OCP - // Baseline Profile – v1.0.0, so currently making it + // Baseline Profile - v1.0.0, so currently making it // "NoAction" as default value to make it OCP Compliant. - sensorJson["PowerLimit"]["LimitException"] = "NoAction"; + sensorJson["PowerLimit"]["LimitException"] = + power::PowerLimitException::NoAction; if (enabled) { // Redfish specification indicates PowerLimit should // be null if the limit is not enabled. - sensorJson["PowerLimit"]["LimitInWatts"] = powerCap * - std::pow(10, scale); + sensorJson["PowerLimit"]["LimitInWatts"] = + powerCap * std::pow(10, scale); } } using Mapper = dbus::utility::MapperGetSubTreePathsResponse; -inline void - afterGetChassis(const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, - const boost::system::error_code& ec2, - const Mapper& chassisPaths) +inline void afterGetChassis( + const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, + const boost::system::error_code& ec2, const Mapper& chassisPaths) { if (ec2) { @@ -267,7 +269,8 @@ inline void auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( asyncResp, chassisName, sensors::dbus::powerPaths, - sensors::node::power); + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::powerNode)); getChassisData(sensorAsyncResp); @@ -296,7 +299,8 @@ inline void } auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( asyncResp, chassisName, sensors::dbus::powerPaths, - sensors::node::power); + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::powerNode)); std::optional<std::vector<nlohmann::json::object_t>> voltageCollections; std::optional<std::vector<nlohmann::json::object_t>> powerCtlCollections; diff --git a/redfish-core/lib/power_subsystem.hpp b/redfish-core/lib/power_subsystem.hpp index d54fd18389..ff7d52cd22 100644 --- a/redfish-core/lib/power_subsystem.hpp +++ b/redfish-core/lib/power_subsystem.hpp @@ -1,6 +1,7 @@ #pragma once #include "app.hpp" +#include "generated/enums/resource.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -35,8 +36,8 @@ inline void doPowerSubsystemCollection( asyncResp->res.jsonValue["Id"] = "PowerSubsystem"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}/PowerSubsystem", chassisId); - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; asyncResp->res.jsonValue["PowerSupplies"]["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId); diff --git a/redfish-core/lib/power_supply.hpp b/redfish-core/lib/power_supply.hpp index f59ed57d5f..26ea26c93c 100644 --- a/redfish-core/lib/power_supply.hpp +++ b/redfish-core/lib/power_supply.hpp @@ -2,6 +2,7 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/chassis_utils.hpp" @@ -46,17 +47,20 @@ inline void updatePowerSupplyList( asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size(); } -inline void - doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::optional<std::string>& validChassisPath) +inline void doPowerSupplyCollection( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { - if (!validChassisPath) + if (ec) { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error{}", ec.value()); + messages::internalError(asyncResp->res); + } return; } - asyncResp->res.addHeader( boost::beast::http::field::link, "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); @@ -70,26 +74,7 @@ inline void asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); asyncResp->res.jsonValue["Members@odata.count"] = 0; - std::string powerPath = *validChassisPath + "/powered_by"; - dbus::utility::getAssociatedSubTreePaths( - powerPath, - sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, - powerSupplyInterface, - [asyncResp, chassisId]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { - if (ec) - { - if (ec.value() != EBADR) - { - BMCWEB_LOG_ERROR("DBUS response error{}", ec.value()); - messages::internalError(asyncResp->res); - } - return; - } - - updatePowerSupplyList(asyncResp, chassisId, subtreePaths); - }); + updatePowerSupplyList(asyncResp, chassisId, subtreePaths); } inline void handlePowerSupplyCollectionHead( @@ -106,15 +91,16 @@ inline void handlePowerSupplyCollectionHead( asyncResp, chassisId, [asyncResp, chassisId](const std::optional<std::string>& validChassisPath) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); - }); + if (!validChassisPath) + { + messages::resourceNotFound(asyncResp->res, "Chassis", + chassisId); + return; + } + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); + }); } inline void handlePowerSupplyCollectionGet( @@ -127,9 +113,19 @@ inline void handlePowerSupplyCollectionGet( return; } - redfish::chassis_utils::getValidChassisPath( - asyncResp, chassisId, - std::bind_front(doPowerSupplyCollection, asyncResp, chassisId)); + constexpr std::array<std::string_view, 2> chasisInterfaces = { + "xyz.openbmc_project.Inventory.Item.Board", + "xyz.openbmc_project.Inventory.Item.Chassis"}; + const std::string reqpath = "/xyz/openbmc_project/inventory"; + + dbus::utility::getAssociatedSubTreePathsById( + chassisId, reqpath, chasisInterfaces, "powered_by", + powerSupplyInterface, + [asyncResp, chassisId]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { + doPowerSupplyCollection(asyncResp, chassisId, ec, subtreePaths); + }); } inline void requestRoutesPowerSupplyCollection(App& app) @@ -145,60 +141,56 @@ inline void requestRoutesPowerSupplyCollection(App& app) std::bind_front(handlePowerSupplyCollectionGet, std::ref(app))); } -inline bool checkPowerSupplyId(const std::string& powerSupplyPath, - const std::string& powerSupplyId) +inline void afterGetValidPowerSupplyPath( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& powerSupplyId, const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree, + const std::function<void(const std::string& powerSupplyPath, + const std::string& service)>& callback) { - std::string powerSupplyName = - sdbusplus::message::object_path(powerSupplyPath).filename(); + if (ec) + { + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error{}", ec.value()); + messages::internalError(asyncResp->res); + } + return; + } + for (const auto& [objectPath, service] : subtree) + { + sdbusplus::message::object_path path(objectPath); + if (path == powerSupplyId) + { + callback(path, service.begin()->first); + return; + } + } - return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId); + BMCWEB_LOG_WARNING("Power supply not found: {}", powerSupplyId); + messages::resourceNotFound(asyncResp->res, "PowerSupplies", powerSupplyId); } inline void getValidPowerSupplyPath( const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& validChassisPath, const std::string& powerSupplyId, - std::function<void(const std::string& powerSupplyPath)>&& callback) + const std::string& chassisId, const std::string& powerSupplyId, + std::function<void(const std::string& powerSupplyPath, + const std::string& service)>&& callback) { - std::string powerPath = validChassisPath + "/powered_by"; - dbus::utility::getAssociatedSubTreePaths( - powerPath, - sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, + constexpr std::array<std::string_view, 2> chasisInterfaces = { + "xyz.openbmc_project.Inventory.Item.Board", + "xyz.openbmc_project.Inventory.Item.Chassis"}; + const std::string reqpath = "/xyz/openbmc_project/inventory"; + + dbus::utility::getAssociatedSubTreeById( + chassisId, reqpath, chasisInterfaces, "powered_by", powerSupplyInterface, - [asyncResp, powerSupplyId, callback{std::move(callback)}]( + [asyncResp, chassisId, powerSupplyId, callback{std::move(callback)}]( const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { - if (ec) - { - if (ec.value() != EBADR) - { - BMCWEB_LOG_ERROR( - "DBUS response error for getAssociatedSubTreePaths{}", - ec.value()); - messages::internalError(asyncResp->res); - return; - } - messages::resourceNotFound(asyncResp->res, "PowerSupplies", - powerSupplyId); - return; - } - - for (const std::string& path : subtreePaths) - { - if (checkPowerSupplyId(path, powerSupplyId)) - { - callback(path); - return; - } - } - - if (!subtreePaths.empty()) - { - BMCWEB_LOG_WARNING("Power supply not found: {}", powerSupplyId); - messages::resourceNotFound(asyncResp->res, "PowerSupplies", - powerSupplyId); - return; - } - }); + const dbus::utility::MapperGetSubTreeResponse& subtree) { + afterGetValidPowerSupplyPath(asyncResp, powerSupplyId, ec, subtree, + callback); + }); } inline void @@ -209,22 +201,23 @@ inline void *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, const bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for State {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for State {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); } inline void @@ -235,22 +228,23 @@ inline void *crow::connections::systemBus, service, path, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp](const boost::system::error_code& ec, const bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Health {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Health {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (!value) - { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; - } - }); + if (!value) + { + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; + } + }); } inline void @@ -262,60 +256,61 @@ inline void "xyz.openbmc_project.Inventory.Decorator.Asset", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Asset {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Asset {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - const std::string* partNumber = nullptr; - const std::string* serialNumber = nullptr; - const std::string* manufacturer = nullptr; - const std::string* model = nullptr; - const std::string* sparePartNumber = nullptr; + const std::string* partNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* manufacturer = nullptr; + const std::string* model = nullptr; + const std::string* sparePartNumber = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", - partNumber, "SerialNumber", serialNumber, "Manufacturer", - manufacturer, "Model", model, "SparePartNumber", sparePartNumber); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", + partNumber, "SerialNumber", serialNumber, "Manufacturer", + manufacturer, "Model", model, "SparePartNumber", + sparePartNumber); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } + if (serialNumber != nullptr) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; - } + if (manufacturer != nullptr) + { + asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + } - if (model != nullptr) - { - asyncResp->res.jsonValue["Model"] = *model; - } + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = *model; + } - // SparePartNumber is optional on D-Bus so skip if it is empty - if (sparePartNumber != nullptr && !sparePartNumber->empty()) - { - asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; - } - }); + // SparePartNumber is optional on D-Bus so skip if it is empty + if (sparePartNumber != nullptr && !sparePartNumber->empty()) + { + asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; + } + }); } inline void getPowerSupplyFirmwareVersion( @@ -327,18 +322,19 @@ inline void getPowerSupplyFirmwareVersion( "xyz.openbmc_project.Software.Version", "Version", [asyncResp](const boost::system::error_code& ec, const std::string& value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for FirmwareVersion {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR( + "DBUS response error for FirmwareVersion {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - asyncResp->res.jsonValue["FirmwareVersion"] = value; - }); + asyncResp->res.jsonValue["FirmwareVersion"] = value; + }); } inline void @@ -350,19 +346,19 @@ inline void "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp](const boost::system::error_code& ec, const std::string& value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error for Location {}", - ec.value()); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error for Location {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; } - return; - } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - value; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = value; + }); } inline void handleGetEfficiencyResponse( @@ -430,8 +426,8 @@ inline void handlePowerSupplyAttributesSubTreeResponse( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor", [asyncResp](const boost::system::error_code& ec1, uint32_t value) { - handleGetEfficiencyResponse(asyncResp, ec1, value); - }); + handleGetEfficiencyResponse(asyncResp, ec1, value); + }); } inline void @@ -444,111 +440,67 @@ inline void "/xyz/openbmc_project", 0, efficiencyIntf, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - handlePowerSupplyAttributesSubTreeResponse(asyncResp, ec, subtree); - }); + handlePowerSupplyAttributesSubTreeResponse(asyncResp, ec, subtree); + }); } -inline void - doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& powerSupplyId, - const std::optional<std::string>& validChassisPath) +inline void doPowerSupplyGet( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& powerSupplyId, + const std::string& powerSupplyPath, const std::string& service) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - - // Get the correct Path and Service that match the input parameters - getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, - [asyncResp, chassisId, powerSupplyId]( - const std::string& powerSupplyPath) { - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = - "#PowerSupply.v1_5_0.PowerSupply"; - asyncResp->res.jsonValue["Name"] = "Power Supply"; - asyncResp->res.jsonValue["Id"] = powerSupplyId; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, - powerSupplyId); - - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; - - dbus::utility::getDbusObject( - powerSupplyPath, powerSupplyInterface, - [asyncResp, - powerSupplyPath](const boost::system::error_code& ec, - const dbus::utility::MapperGetObject& object) { - if (ec || object.empty()) - { - messages::internalError(asyncResp->res); - return; - } - - getPowerSupplyState(asyncResp, object.begin()->first, - powerSupplyPath); - getPowerSupplyHealth(asyncResp, object.begin()->first, - powerSupplyPath); - getPowerSupplyAsset(asyncResp, object.begin()->first, - powerSupplyPath); - getPowerSupplyFirmwareVersion(asyncResp, object.begin()->first, - powerSupplyPath); - getPowerSupplyLocation(asyncResp, object.begin()->first, - powerSupplyPath); - }); - - getEfficiencyPercent(asyncResp); - }); + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); + asyncResp->res.jsonValue["@odata.type"] = "#PowerSupply.v1_5_0.PowerSupply"; + asyncResp->res.jsonValue["Name"] = "Power Supply"; + asyncResp->res.jsonValue["Id"] = powerSupplyId; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, + powerSupplyId); + + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; + + getPowerSupplyState(asyncResp, service, powerSupplyPath); + getPowerSupplyHealth(asyncResp, service, powerSupplyPath); + getPowerSupplyAsset(asyncResp, service, powerSupplyPath); + getPowerSupplyFirmwareVersion(asyncResp, service, powerSupplyPath); + getPowerSupplyLocation(asyncResp, service, powerSupplyPath); + getEfficiencyPercent(asyncResp); } -inline void - handlePowerSupplyHead(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& powerSupplyId) +inline void handlePowerSupplyHead( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& powerSupplyId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } - redfish::chassis_utils::getValidChassisPath( - asyncResp, chassisId, - [asyncResp, chassisId, - powerSupplyId](const std::optional<std::string>& validChassisPath) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - - // Get the correct Path and Service that match the input parameters - getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, - [asyncResp](const std::string&) { + // Get the correct Path and Service that match the input parameters + getValidPowerSupplyPath( + asyncResp, chassisId, powerSupplyId, + [asyncResp](const std::string&, const std::string&) { asyncResp->res.addHeader( boost::beast::http::field::link, "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); }); - }); } -inline void - handlePowerSupplyGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& powerSupplyId) +inline void handlePowerSupplyGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& powerSupplyId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } - - redfish::chassis_utils::getValidChassisPath( - asyncResp, chassisId, + getValidPowerSupplyPath( + asyncResp, chassisId, powerSupplyId, std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); } diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp index 8015369dbf..82781d3bd9 100644 --- a/redfish-core/lib/processor.hpp +++ b/redfish-core/lib/processor.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -20,6 +20,7 @@ #include "dbus_utility.hpp" #include "error_messages.hpp" #include "generated/enums/processor.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/collection.hpp" @@ -66,14 +67,14 @@ inline void getProcessorUUID(std::shared_ptr<bmcweb::AsyncResp> asyncResp, "xyz.openbmc_project.Common.UUID", "UUID", [objPath, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["UUID"] = property; - }); + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["UUID"] = property; + }); } inline void getCpuDataByInterface( @@ -83,8 +84,8 @@ inline void getCpuDataByInterface( BMCWEB_LOG_DEBUG("Get CPU resources by interface."); // Set the default value of state - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; for (const auto& interface : cpuInterfacesProperties) { @@ -102,7 +103,8 @@ inline void getCpuDataByInterface( if (!*cpuPresent) { // Slot is not populated - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; } } else if (property.first == "Functional") @@ -115,7 +117,8 @@ inline void getCpuDataByInterface( } if (!*cpuFunctional) { - asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; + asyncResp->res.jsonValue["Status"]["Health"] = + resource::Health::Critical; } } else if (property.first == "CoreCount") @@ -219,10 +222,9 @@ inline void getCpuDataByInterface( } } -inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, - const std::string& cpuId, - const std::string& service, - const std::string& objPath) +inline void getCpuDataByService( + std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& cpuId, + const std::string& service, const std::string& objPath) { BMCWEB_LOG_DEBUG("Get available system cpu resources by service."); @@ -232,43 +234,46 @@ inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [cpuId, service, objPath, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& dbusData) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["Id"] = cpuId; - asyncResp->res.jsonValue["Name"] = "Processor"; - asyncResp->res.jsonValue["ProcessorType"] = "CPU"; - - bool slotPresent = false; - std::string corePath = objPath + "/core"; - size_t totalCores = 0; - for (const auto& object : dbusData) - { - if (object.first.str == objPath) + if (ec) { - getCpuDataByInterface(asyncResp, object.second); + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; } - else if (object.first.str.starts_with(corePath)) + asyncResp->res.jsonValue["Id"] = cpuId; + asyncResp->res.jsonValue["Name"] = "Processor"; + asyncResp->res.jsonValue["ProcessorType"] = + processor::ProcessorType::CPU; + + bool slotPresent = false; + std::string corePath = objPath + "/core"; + size_t totalCores = 0; + for (const auto& object : dbusData) { - for (const auto& interface : object.second) + if (object.first.str == objPath) { - if (interface.first == "xyz.openbmc_project.Inventory.Item") + getCpuDataByInterface(asyncResp, object.second); + } + else if (object.first.str.starts_with(corePath)) + { + for (const auto& interface : object.second) { - for (const auto& property : interface.second) + if (interface.first == + "xyz.openbmc_project.Inventory.Item") { - if (property.first == "Present") + for (const auto& property : interface.second) { - const bool* present = - std::get_if<bool>(&property.second); - if (present != nullptr) + if (property.first == "Present") { - if (*present) + const bool* present = + std::get_if<bool>(&property.second); + if (present != nullptr) { - slotPresent = true; - totalCores++; + if (*present) + { + slotPresent = true; + totalCores++; + } } } } @@ -276,16 +281,15 @@ inline void getCpuDataByService(std::shared_ptr<bmcweb::AsyncResp> asyncResp, } } } - } - // In getCpuDataByInterface(), state and health are set - // based on the present and functional status. If core - // count is zero, then it has a higher precedence. - if (slotPresent) - { - asyncResp->res.jsonValue["TotalCores"] = totalCores; - } - return; - }); + // In getCpuDataByInterface(), state and health are set + // based on the present and functional status. If core + // count is zero, then it has a higher precedence. + if (slotPresent) + { + asyncResp->res.jsonValue["TotalCores"] = totalCores; + } + return; + }); } /** @@ -366,10 +370,9 @@ inline void asyncResp->res.jsonValue["ThrottleCauses"] = std::move(rCauses); } -inline void - getThrottleProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& service, - const std::string& objectPath) +inline void getThrottleProperties( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& service, const std::string& objectPath) { BMCWEB_LOG_DEBUG("Get processor throttle resources"); @@ -378,8 +381,8 @@ inline void "xyz.openbmc_project.Control.Power.Throttle", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - readThrottleProperties(asyncResp, ec, properties); - }); + readThrottleProperties(asyncResp, ec, properties); + }); } inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, @@ -393,67 +396,67 @@ inline void getCpuAssetData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [objPath, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } - - const std::string* serialNumber = nullptr; - const std::string* model = nullptr; - const std::string* manufacturer = nullptr; - const std::string* partNumber = nullptr; - const std::string* sparePartNumber = nullptr; + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", - serialNumber, "Model", model, "Manufacturer", manufacturer, - "PartNumber", partNumber, "SparePartNumber", sparePartNumber); + const std::string* serialNumber = nullptr; + const std::string* model = nullptr; + const std::string* manufacturer = nullptr; + const std::string* partNumber = nullptr; + const std::string* sparePartNumber = nullptr; - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", + serialNumber, "Model", model, "Manufacturer", manufacturer, + "PartNumber", partNumber, "SparePartNumber", sparePartNumber); - if (serialNumber != nullptr && !serialNumber->empty()) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } - - if ((model != nullptr) && !model->empty()) - { - asyncResp->res.jsonValue["Model"] = *model; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + if (serialNumber != nullptr && !serialNumber->empty()) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } - // Otherwise would be unexpected. - if (manufacturer->find("Intel") != std::string::npos) + if ((model != nullptr) && !model->empty()) { - asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; - asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; + asyncResp->res.jsonValue["Model"] = *model; } - else if (manufacturer->find("IBM") != std::string::npos) + + if (manufacturer != nullptr) { - asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; - asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; + asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + + // Otherwise would be unexpected. + if (manufacturer->find("Intel") != std::string::npos) + { + asyncResp->res.jsonValue["ProcessorArchitecture"] = "x86"; + asyncResp->res.jsonValue["InstructionSet"] = "x86-64"; + } + else if (manufacturer->find("IBM") != std::string::npos) + { + asyncResp->res.jsonValue["ProcessorArchitecture"] = "Power"; + asyncResp->res.jsonValue["InstructionSet"] = "PowerISA"; + } } - } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } - if (sparePartNumber != nullptr && !sparePartNumber->empty()) - { - asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; - } - }); + if (sparePartNumber != nullptr && !sparePartNumber->empty()) + { + asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; + } + }); } inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, @@ -467,29 +470,30 @@ inline void getCpuRevisionData(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [objPath, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } - const std::string* version = nullptr; + const std::string* version = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "Version", version); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "Version", + version); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (version != nullptr) - { - asyncResp->res.jsonValue["Version"] = *version; - } - }); + if (version != nullptr) + { + asyncResp->res.jsonValue["Version"] = *version; + } + }); } inline void getAcceleratorDataByService( @@ -502,48 +506,49 @@ inline void getAcceleratorDataByService( [acclrtrId, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } - const bool* functional = nullptr; - const bool* present = nullptr; + const bool* functional = nullptr; + const bool* present = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "Functional", - functional, "Present", present); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "Functional", + functional, "Present", present); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - std::string state = "Enabled"; - std::string health = "OK"; + std::string state = "Enabled"; + std::string health = "OK"; - if (present != nullptr && !*present) - { - state = "Absent"; - } + if (present != nullptr && !*present) + { + state = "Absent"; + } - if (functional != nullptr && !*functional) - { - if (state == "Enabled") + if (functional != nullptr && !*functional) { - health = "Critical"; + if (state == "Enabled") + { + health = "Critical"; + } } - } - asyncResp->res.jsonValue["Id"] = acclrtrId; - asyncResp->res.jsonValue["Name"] = "Processor"; - asyncResp->res.jsonValue["Status"]["State"] = state; - asyncResp->res.jsonValue["Status"]["Health"] = health; - asyncResp->res.jsonValue["ProcessorType"] = "Accelerator"; - }); + asyncResp->res.jsonValue["Id"] = acclrtrId; + asyncResp->res.jsonValue["Name"] = "Processor"; + asyncResp->res.jsonValue["Status"]["State"] = state; + asyncResp->res.jsonValue["Status"]["Health"] = health; + asyncResp->res.jsonValue["ProcessorType"] = + processor::ProcessorType::Accelerator; + }); } // OperatingConfig D-Bus Types @@ -616,85 +621,87 @@ inline void [asyncResp, cpuId, service](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); - messages::internalError(asyncResp->res); - return; - } - - nlohmann::json& json = asyncResp->res.jsonValue; + if (ec) + { + BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); + messages::internalError(asyncResp->res); + return; + } - const sdbusplus::message::object_path* appliedConfig = nullptr; - const bool* baseSpeedPriorityEnabled = nullptr; + nlohmann::json& json = asyncResp->res.jsonValue; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", - appliedConfig, "BaseSpeedPriorityEnabled", - baseSpeedPriorityEnabled); + const sdbusplus::message::object_path* appliedConfig = nullptr; + const bool* baseSpeedPriorityEnabled = nullptr; - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", + appliedConfig, "BaseSpeedPriorityEnabled", + baseSpeedPriorityEnabled); - if (appliedConfig != nullptr) - { - const std::string& dbusPath = appliedConfig->str; - nlohmann::json::object_t operatingConfig; - operatingConfig["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", - BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId); - json["OperatingConfigs"] = std::move(operatingConfig); - - // Reuse the D-Bus config object name for the Redfish - // URI - size_t baseNamePos = dbusPath.rfind('/'); - if (baseNamePos == std::string::npos || - baseNamePos == (dbusPath.size() - 1)) - { - // If the AppliedConfig was somehow not a valid path, - // skip adding any more properties, since everything - // else is tied to this applied config. + if (!success) + { messages::internalError(asyncResp->res); return; } - nlohmann::json::object_t appliedOperatingConfig; - appliedOperatingConfig["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId, - dbusPath.substr(baseNamePos + 1)); - json["AppliedOperatingConfig"] = std::move(appliedOperatingConfig); - - // Once we found the current applied config, queue another - // request to read the base freq core ids out of that - // config. - sdbusplus::asio::getProperty<BaseSpeedPrioritySettingsProperty>( - *crow::connections::systemBus, service, dbusPath, - "xyz.openbmc_project.Inventory.Item.Cpu." - "OperatingConfig", - "BaseSpeedPrioritySettings", - [asyncResp]( - const boost::system::error_code& ec2, - const BaseSpeedPrioritySettingsProperty& baseSpeedList) { - if (ec2) + + if (appliedConfig != nullptr) + { + const std::string& dbusPath = appliedConfig->str; + nlohmann::json::object_t operatingConfig; + operatingConfig["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", + BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId); + json["OperatingConfigs"] = std::move(operatingConfig); + + // Reuse the D-Bus config object name for the Redfish + // URI + size_t baseNamePos = dbusPath.rfind('/'); + if (baseNamePos == std::string::npos || + baseNamePos == (dbusPath.size() - 1)) { - BMCWEB_LOG_WARNING("D-Bus Property Get error: {}", ec2); + // If the AppliedConfig was somehow not a valid path, + // skip adding any more properties, since everything + // else is tied to this applied config. messages::internalError(asyncResp->res); return; } + nlohmann::json::object_t appliedOperatingConfig; + appliedOperatingConfig["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuId, + dbusPath.substr(baseNamePos + 1)); + json["AppliedOperatingConfig"] = + std::move(appliedOperatingConfig); + + // Once we found the current applied config, queue another + // request to read the base freq core ids out of that + // config. + sdbusplus::asio::getProperty<BaseSpeedPrioritySettingsProperty>( + *crow::connections::systemBus, service, dbusPath, + "xyz.openbmc_project.Inventory.Item.Cpu." + "OperatingConfig", + "BaseSpeedPrioritySettings", + [asyncResp](const boost::system::error_code& ec2, + const BaseSpeedPrioritySettingsProperty& + baseSpeedList) { + if (ec2) + { + BMCWEB_LOG_WARNING("D-Bus Property Get error: {}", + ec2); + messages::internalError(asyncResp->res); + return; + } - highSpeedCoreIdsHandler(asyncResp, baseSpeedList); - }); - } + highSpeedCoreIdsHandler(asyncResp, baseSpeedList); + }); + } - if (baseSpeedPriorityEnabled != nullptr) - { - json["BaseSpeedPriorityState"] = - *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; - } - }); + if (baseSpeedPriorityEnabled != nullptr) + { + json["BaseSpeedPriorityState"] = + *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; + } + }); } /** @@ -715,16 +722,17 @@ inline void getCpuLocationCode(std::shared_ptr<bmcweb::AsyncResp> asyncResp, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [objPath, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const std::string& property) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - property; - }); + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + property; + }); } /** @@ -746,15 +754,15 @@ inline void getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "UniqueIdentifier", [asyncResp](const boost::system::error_code& ec, const std::string& id) { - if (ec) - { - BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec); - messages::internalError(asyncResp->res); - return; - } - asyncResp->res - .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id; - }); + if (ec) + { + BMCWEB_LOG_ERROR("Failed to read cpu unique id: {}", ec); + messages::internalError(asyncResp->res); + return; + } + asyncResp->res + .jsonValue["ProcessorId"]["ProtectedIdentificationNumber"] = id; + }); } /** @@ -790,57 +798,56 @@ inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, [resp, processorId, handler = std::forward<Handler>(handler)]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error: {}", ec); - messages::internalError(resp->res); - return; - } - for (const auto& [objectPath, serviceMap] : subtree) - { - // Ignore any objects which don't end with our desired cpu name - if (!objectPath.ends_with(processorId)) + if (ec) { - continue; + BMCWEB_LOG_DEBUG("DBUS response error: {}", ec); + messages::internalError(resp->res); + return; } - - bool found = false; - // Filter out objects that don't have the CPU-specific - // interfaces to make sure we can return 404 on non-CPUs - // (e.g. /redfish/../Processors/dimm0) - for (const auto& [serviceName, interfaceList] : serviceMap) + for (const auto& [objectPath, serviceMap] : subtree) { - if (std::ranges::find_first_of(interfaceList, - processorInterfaces) != - std::end(interfaceList)) + // Ignore any objects which don't end with our desired cpu name + if (!objectPath.ends_with(processorId)) { - found = true; - break; + continue; } - } - if (!found) - { - continue; - } + bool found = false; + // Filter out objects that don't have the CPU-specific + // interfaces to make sure we can return 404 on non-CPUs + // (e.g. /redfish/../Processors/dimm0) + for (const auto& [serviceName, interfaceList] : serviceMap) + { + if (std::ranges::find_first_of(interfaceList, + processorInterfaces) != + std::end(interfaceList)) + { + found = true; + break; + } + } - // Process the first object which does match our cpu name and - // required interfaces, and potentially ignore any other - // matching objects. Assume all interfaces we want to process - // must be on the same object path. + if (!found) + { + continue; + } - handler(objectPath, serviceMap); - return; - } - messages::resourceNotFound(resp->res, "Processor", processorId); - }); + // Process the first object which does match our cpu name and + // required interfaces, and potentially ignore any other + // matching objects. Assume all interfaces we want to process + // must be on the same object path. + + handler(objectPath, serviceMap); + return; + } + messages::resourceNotFound(resp->res, "Processor", processorId); + }); } -inline void - getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& processorId, - const std::string& objectPath, - const dbus::utility::MapperServiceMap& serviceMap) +inline void getProcessorData( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& processorId, const std::string& objectPath, + const dbus::utility::MapperServiceMap& serviceMap) { for (const auto& [serviceName, interfaceList] : serviceMap) { @@ -903,100 +910,101 @@ inline void * @param[in] service D-Bus service name to query. * @param[in] objPath D-Bus object to query. */ -inline void - getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& service, - const std::string& objPath) +inline void getOperatingConfigData( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& service, const std::string& objPath) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, objPath, "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); + messages::internalError(asyncResp->res); + return; + } - const size_t* availableCoreCount = nullptr; - const uint32_t* baseSpeed = nullptr; - const uint32_t* maxJunctionTemperature = nullptr; - const uint32_t* maxSpeed = nullptr; - const uint32_t* powerLimit = nullptr; - const TurboProfileProperty* turboProfile = nullptr; - const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = - nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "AvailableCoreCount", - availableCoreCount, "BaseSpeed", baseSpeed, - "MaxJunctionTemperature", maxJunctionTemperature, "MaxSpeed", - maxSpeed, "PowerLimit", powerLimit, "TurboProfile", turboProfile, - "BaseSpeedPrioritySettings", baseSpeedPrioritySettings); - - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + const size_t* availableCoreCount = nullptr; + const uint32_t* baseSpeed = nullptr; + const uint32_t* maxJunctionTemperature = nullptr; + const uint32_t* maxSpeed = nullptr; + const uint32_t* powerLimit = nullptr; + const TurboProfileProperty* turboProfile = nullptr; + const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = + nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, + "AvailableCoreCount", availableCoreCount, "BaseSpeed", + baseSpeed, "MaxJunctionTemperature", maxJunctionTemperature, + "MaxSpeed", maxSpeed, "PowerLimit", powerLimit, "TurboProfile", + turboProfile, "BaseSpeedPrioritySettings", + baseSpeedPrioritySettings); + + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - nlohmann::json& json = asyncResp->res.jsonValue; + nlohmann::json& json = asyncResp->res.jsonValue; - if (availableCoreCount != nullptr) - { - json["TotalAvailableCoreCount"] = *availableCoreCount; - } + if (availableCoreCount != nullptr) + { + json["TotalAvailableCoreCount"] = *availableCoreCount; + } - if (baseSpeed != nullptr) - { - json["BaseSpeedMHz"] = *baseSpeed; - } + if (baseSpeed != nullptr) + { + json["BaseSpeedMHz"] = *baseSpeed; + } - if (maxJunctionTemperature != nullptr) - { - json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; - } + if (maxJunctionTemperature != nullptr) + { + json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; + } - if (maxSpeed != nullptr) - { - json["MaxSpeedMHz"] = *maxSpeed; - } + if (maxSpeed != nullptr) + { + json["MaxSpeedMHz"] = *maxSpeed; + } - if (powerLimit != nullptr) - { - json["TDPWatts"] = *powerLimit; - } + if (powerLimit != nullptr) + { + json["TDPWatts"] = *powerLimit; + } - if (turboProfile != nullptr) - { - nlohmann::json& turboArray = json["TurboProfile"]; - turboArray = nlohmann::json::array(); - for (const auto& [turboSpeed, coreCount] : *turboProfile) + if (turboProfile != nullptr) { - nlohmann::json::object_t turbo; - turbo["ActiveCoreCount"] = coreCount; - turbo["MaxSpeedMHz"] = turboSpeed; - turboArray.emplace_back(std::move(turbo)); + nlohmann::json& turboArray = json["TurboProfile"]; + turboArray = nlohmann::json::array(); + for (const auto& [turboSpeed, coreCount] : *turboProfile) + { + nlohmann::json::object_t turbo; + turbo["ActiveCoreCount"] = coreCount; + turbo["MaxSpeedMHz"] = turboSpeed; + turboArray.emplace_back(std::move(turbo)); + } } - } - if (baseSpeedPrioritySettings != nullptr) - { - nlohmann::json& baseSpeedArray = json["BaseSpeedPrioritySettings"]; - baseSpeedArray = nlohmann::json::array(); - for (const auto& [baseSpeedMhz, coreList] : - *baseSpeedPrioritySettings) + if (baseSpeedPrioritySettings != nullptr) { - nlohmann::json::object_t speed; - speed["CoreCount"] = coreList.size(); - speed["CoreIDs"] = coreList; - speed["BaseSpeedMHz"] = baseSpeedMhz; - baseSpeedArray.emplace_back(std::move(speed)); + nlohmann::json& baseSpeedArray = + json["BaseSpeedPrioritySettings"]; + baseSpeedArray = nlohmann::json::array(); + for (const auto& [baseSpeedMhz, coreList] : + *baseSpeedPrioritySettings) + { + nlohmann::json::object_t speed; + speed["CoreCount"] = coreList.size(); + speed["CoreIDs"] = coreList; + speed["BaseSpeedMHz"] = baseSpeedMhz; + baseSpeedArray.emplace_back(std::move(speed)); + } } - } - }); + }); } /** @@ -1064,11 +1072,10 @@ inline void patchAppliedOperatingConfig( "AppliedConfig", configPath); } -inline void - handleProcessorHead(crow::App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& /* systemName */, - const std::string& /* processorId */) +inline void handleProcessorHead( + crow::App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& /* systemName */, const std::string& /* processorId */) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -1098,77 +1105,81 @@ inline void requestRoutesOperatingConfigCollection(App& app) BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/") .privileges(redfish::privileges::getOperatingConfigCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& cpuName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#OperatingConfigCollection.OperatingConfigCollection"; - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", - BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName); - asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; - - // First find the matching CPU object so we know how to - // constrain our search for related Config objects. - const std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; - dbus::utility::getSubTreePaths( - "/xyz/openbmc_project/inventory", 0, interfaces, - [asyncResp, cpuName]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& objects) { - if (ec) + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, + const std::string& cpuName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); - messages::internalError(asyncResp->res); return; } - for (const std::string& object : objects) + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - if (!object.ends_with(cpuName)) - { - continue; - } + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - // Not expected that there will be multiple matching - // CPU objects, but if there are just use the first - // one. - - // Use the common search routine to construct the - // Collection of all Config objects under this CPU. - constexpr std::array<std::string_view, 1> interface{ - "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; - collection_util::getCollectionMembers( - asyncResp, - boost::urls::format( - "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", - BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName), - interface, object); + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); return; } + asyncResp->res.jsonValue["@odata.type"] = + "#OperatingConfigCollection.OperatingConfigCollection"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", + BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName); + asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; + + // First find the matching CPU object so we know how to + // constrain our search for related Config objects. + const std::array<std::string_view, 1> interfaces = { + "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; + dbus::utility::getSubTreePaths( + "/xyz/openbmc_project/inventory", 0, interfaces, + [asyncResp, + cpuName](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& + objects) { + if (ec) + { + BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, + ec.message()); + messages::internalError(asyncResp->res); + return; + } + + for (const std::string& object : objects) + { + if (!object.ends_with(cpuName)) + { + continue; + } + + // Not expected that there will be multiple matching + // CPU objects, but if there are just use the first + // one. + + // Use the common search routine to construct the + // Collection of all Config objects under this CPU. + constexpr std::array<std::string_view, 1> interface{ + "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; + collection_util::getCollectionMembers( + asyncResp, + boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs", + BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName), + interface, object); + return; + } + }); }); - }); } inline void requestRoutesOperatingConfig(App& app) @@ -1177,72 +1188,79 @@ inline void requestRoutesOperatingConfig(App& app) app, "/redfish/v1/Systems/<str>/Processors/<str>/OperatingConfigs/<str>/") .privileges(redfish::privileges::getOperatingConfig) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, const std::string& cpuName, - const std::string& configName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - // Ask for all objects implementing OperatingConfig so we can search - // for one with a matching name - constexpr std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; - dbus::utility::getSubTree( - "/xyz/openbmc_project/inventory", 0, interfaces, - [asyncResp, cpuName, configName]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, + const std::string& cpuName, + const std::string& configName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, ec.message()); - messages::internalError(asyncResp->res); return; } - const std::string expectedEnding = cpuName + '/' + configName; - for (const auto& [objectPath, serviceMap] : subtree) + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - // Ignore any configs without matching cpuX/configY - if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) - { - continue; - } + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - nlohmann::json& json = asyncResp->res.jsonValue; - json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; - json["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, configName); - json["Name"] = "Processor Profile"; - json["Id"] = configName; - - // Just use the first implementation of the object - not - // expected that there would be multiple matching - // services - getOperatingConfigData(asyncResp, serviceMap.begin()->first, - objectPath); + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); return; } - messages::resourceNotFound(asyncResp->res, "OperatingConfig", - configName); + // Ask for all objects implementing OperatingConfig so we can search + // for one with a matching name + constexpr std::array<std::string_view, 1> interfaces = { + "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; + dbus::utility::getSubTree( + "/xyz/openbmc_project/inventory", 0, interfaces, + [asyncResp, cpuName, configName]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree) { + if (ec) + { + BMCWEB_LOG_WARNING("D-Bus error: {}, {}", ec, + ec.message()); + messages::internalError(asyncResp->res); + return; + } + const std::string expectedEnding = + cpuName + '/' + configName; + for (const auto& [objectPath, serviceMap] : subtree) + { + // Ignore any configs without matching cpuX/configY + if (!objectPath.ends_with(expectedEnding) || + serviceMap.empty()) + { + continue; + } + + nlohmann::json& json = asyncResp->res.jsonValue; + json["@odata.type"] = + "#OperatingConfig.v1_0_0.OperatingConfig"; + json["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, cpuName, + configName); + json["Name"] = "Processor Profile"; + json["Id"] = configName; + + // Just use the first implementation of the object - not + // expected that there would be multiple matching + // services + getOperatingConfigData( + asyncResp, serviceMap.begin()->first, objectPath); + return; + } + messages::resourceNotFound(asyncResp->res, + "OperatingConfig", configName); + }); }); - }); } inline void requestRoutesProcessorCollection(App& app) @@ -1257,47 +1275,48 @@ inline void requestRoutesProcessorCollection(App& app) BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") .privileges(redfish::privileges::getProcessorCollection) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = - "#ProcessorCollection.ProcessorCollection"; - asyncResp->res.jsonValue["Name"] = "Processor Collection"; + asyncResp->res.jsonValue["@odata.type"] = + "#ProcessorCollection.ProcessorCollection"; + asyncResp->res.jsonValue["Name"] = "Processor Collection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/Processors", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/Processors", + BMCWEB_REDFISH_SYSTEM_URI_NAME); - collection_util::getCollectionMembers( - asyncResp, - boost::urls::format("/redfish/v1/Systems/{}/Processors", - BMCWEB_REDFISH_SYSTEM_URI_NAME), - processorInterfaces, "/xyz/openbmc_project/inventory"); - }); + collection_util::getCollectionMembers( + asyncResp, + boost::urls::format("/redfish/v1/Systems/{}/Processors", + BMCWEB_REDFISH_SYSTEM_URI_NAME), + processorInterfaces, "/xyz/openbmc_project/inventory"); + }); } inline void requestRoutesProcessor(App& app) @@ -1313,42 +1332,43 @@ inline void requestRoutesProcessor(App& app) BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") .privileges(redfish::privileges::getProcessor) - .methods(boost::beast::http::verb::get)( - [&app](const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& processorId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = - "#Processor.v1_18_0.Processor"; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Systems/{}/Processors/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId); + .methods( + boost::beast::http::verb:: + get)([&app](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, + const std::string& processorId) { + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - getProcessorObject( - asyncResp, processorId, - std::bind_front(getProcessorData, asyncResp, processorId)); - }); + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); + asyncResp->res.jsonValue["@odata.type"] = + "#Processor.v1_18_0.Processor"; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId); + + getProcessorObject( + asyncResp, processorId, + std::bind_front(getProcessorData, asyncResp, processorId)); + }); BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") .privileges(redfish::privileges::patchProcessor) @@ -1357,42 +1377,42 @@ inline void requestRoutesProcessor(App& app) const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& systemName, const std::string& processorId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) - { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } - std::optional<std::string> appliedConfigUri; - if (!json_util::readJsonPatch(req, asyncResp->res, - "AppliedOperatingConfig/@odata.id", - appliedConfigUri)) - { - return; - } + std::optional<std::string> appliedConfigUri; + if (!json_util::readJsonPatch( + req, asyncResp->res, "AppliedOperatingConfig/@odata.id", + appliedConfigUri)) + { + return; + } - if (appliedConfigUri) - { - // Check for 404 and find matching D-Bus object, then run - // property patch handlers if that all succeeds. - getProcessorObject(asyncResp, processorId, - std::bind_front(patchAppliedOperatingConfig, - asyncResp, processorId, - *appliedConfigUri)); - } - }); + if (appliedConfigUri) + { + // Check for 404 and find matching D-Bus object, then run + // property patch handlers if that all succeeds. + getProcessorObject( + asyncResp, processorId, + std::bind_front(patchAppliedOperatingConfig, asyncResp, + processorId, *appliedConfigUri)); + } + }); } } // namespace redfish diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp index 555e7f32ac..48d53bfd94 100644 --- a/redfish-core/lib/redfish_sessions.hpp +++ b/redfish-core/lib/redfish_sessions.hpp @@ -1,22 +1,23 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "account_service.hpp" #include "app.hpp" +#include "cookies.hpp" #include "error_messages.hpp" #include "http/utility.hpp" #include "persistent_data.hpp" @@ -26,6 +27,9 @@ #include <boost/url/format.hpp> +#include <string> +#include <vector> + namespace redfish { @@ -125,21 +129,26 @@ inline void } } + if (req.session != nullptr && req.session->uniqueId == sessionId && + session->cookieAuth) + { + bmcweb::clearSessionCookies(asyncResp->res); + } + persistent_data::SessionStore::getInstance().removeSession(session); messages::success(asyncResp->res); } inline nlohmann::json getSessionCollectionMembers() { - std::vector<const std::string*> sessionIds = - persistent_data::SessionStore::getInstance().getUniqueIds( - false, persistent_data::PersistenceType::TIMEOUT); + std::vector<std::string> sessionIds = + persistent_data::SessionStore::getInstance().getAllUniqueIds(); nlohmann::json ret = nlohmann::json::array(); - for (const std::string* uid : sessionIds) + for (const std::string& uid : sessionIds) { nlohmann::json::object_t session; session["@odata.id"] = - boost::urls::format("/redfish/v1/SessionService/Sessions/{}", *uid); + boost::urls::format("/redfish/v1/SessionService/Sessions/{}", uid); ret.emplace_back(std::move(session)); } return ret; @@ -203,12 +212,13 @@ inline void handleSessionCollectionPost( std::string username; std::string password; std::optional<std::string> clientId; + std::optional<std::string> token; if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username, - "Password", password, "Context", clientId)) + "Password", password, "Token", token, + "Context", clientId)) { return; } - if (password.empty() || username.empty() || asyncResp->res.result() != boost::beast::http::status::ok) { @@ -225,7 +235,7 @@ inline void handleSessionCollectionPost( return; } - int pamrc = pamAuthenticateUser(username, password); + int pamrc = pamAuthenticateUser(username, password, token); bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) { @@ -238,14 +248,25 @@ inline void handleSessionCollectionPost( std::shared_ptr<persistent_data::UserSession> session = persistent_data::SessionStore::getInstance().generateUserSession( username, req.ipAddress, clientId, - persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly); + persistent_data::SessionType::Session, isConfigureSelfOnly); if (session == nullptr) { messages::internalError(asyncResp->res); return; } - asyncResp->res.addHeader("X-Auth-Token", session->sessionToken); + // When session is created by webui-vue give it session cookies as a + // non-standard Redfish extension. This is needed for authentication for + // WebSockets-based functionality. + if (!req.getHeaderValue("X-Requested-With").empty()) + { + bmcweb::setSessionCookies(asyncResp->res, *session); + } + else + { + asyncResp->res.addHeader("X-Auth-Token", session->sessionToken); + } + asyncResp->res.addHeader( "Location", "/redfish/v1/SessionService/Sessions/" + session->uniqueId); asyncResp->res.result(boost::beast::http::status::created); diff --git a/redfish-core/lib/redfish_util.hpp b/redfish-core/lib/redfish_util.hpp index 8fe2bbc400..4164e10775 100644 --- a/redfish-core/lib/redfish_util.hpp +++ b/redfish-core/lib/redfish_util.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2019 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -71,29 +71,29 @@ void getMainChassisId(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [callback = std::forward<CallbackFunc>(callback), asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR("{}", ec); - return; - } - if (subtree.empty()) - { - BMCWEB_LOG_DEBUG("Can't find chassis!"); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("{}", ec); + return; + } + if (subtree.empty()) + { + BMCWEB_LOG_DEBUG("Can't find chassis!"); + return; + } - std::size_t idPos = subtree[0].first.rfind('/'); - if (idPos == std::string::npos || - (idPos + 1) >= subtree[0].first.size()) - { - messages::internalError(asyncResp->res); - BMCWEB_LOG_DEBUG("Can't parse chassis ID!"); - return; - } - std::string chassisId = subtree[0].first.substr(idPos + 1); - BMCWEB_LOG_DEBUG("chassisId = {}", chassisId); - callback(chassisId, asyncResp); - }); + std::size_t idPos = subtree[0].first.rfind('/'); + if (idPos == std::string::npos || + (idPos + 1) >= subtree[0].first.size()) + { + messages::internalError(asyncResp->res); + BMCWEB_LOG_DEBUG("Can't parse chassis ID!"); + return; + } + std::string chassisId = subtree[0].first.substr(idPos + 1); + BMCWEB_LOG_DEBUG("chassisId = {}", chassisId); + callback(chassisId, asyncResp); + }); } template <typename CallbackFunc> @@ -106,99 +106,101 @@ void getPortStatusAndPath( [protocolToDBus, callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const std::vector<UnitStruct>& r) { - std::vector<std::tuple<std::string, std::string, bool>> socketData; - if (ec) - { - BMCWEB_LOG_ERROR("{}", ec); - // return error code - callback(ec, socketData); - return; - } - - // save all service output into vector - for (const UnitStruct& unit : r) - { - // Only traverse through <xyz>.socket units - const std::string& unitName = std::get<NET_PROTO_UNIT_NAME>(unit); - - // find "." into unitsName - size_t lastCharPos = unitName.rfind('.'); - if (lastCharPos == std::string::npos) + std::vector<std::tuple<std::string, std::string, bool>> socketData; + if (ec) { - continue; + BMCWEB_LOG_ERROR("{}", ec); + // return error code + callback(ec, socketData); + return; } - // is unitsName end with ".socket" - std::string unitNameEnd = unitName.substr(lastCharPos); - if (unitNameEnd != ".socket") + // save all service output into vector + for (const UnitStruct& unit : r) { - continue; - } + // Only traverse through <xyz>.socket units + const std::string& unitName = + std::get<NET_PROTO_UNIT_NAME>(unit); - // find "@" into unitsName - if (size_t atCharPos = unitName.rfind('@'); - atCharPos != std::string::npos) - { - lastCharPos = atCharPos; - } - - // unitsName without "@eth(x).socket", only <xyz> - // unitsName without ".socket", only <xyz> - std::string unitNameStr = unitName.substr(0, lastCharPos); + // find "." into unitsName + size_t lastCharPos = unitName.rfind('.'); + if (lastCharPos == std::string::npos) + { + continue; + } - for (const auto& kv : protocolToDBus) - { - // We are interested in services, which starts with - // mapped service name - if (unitNameStr != kv.second) + // is unitsName end with ".socket" + std::string unitNameEnd = unitName.substr(lastCharPos); + if (unitNameEnd != ".socket") { continue; } - const std::string& socketPath = - std::get<NET_PROTO_UNIT_OBJ_PATH>(unit); - const std::string& unitState = - std::get<NET_PROTO_UNIT_SUB_STATE>(unit); + // find "@" into unitsName + if (size_t atCharPos = unitName.rfind('@'); + atCharPos != std::string::npos) + { + lastCharPos = atCharPos; + } - bool isProtocolEnabled = ((unitState == "running") || - (unitState == "listening")); + // unitsName without "@eth(x).socket", only <xyz> + // unitsName without ".socket", only <xyz> + std::string unitNameStr = unitName.substr(0, lastCharPos); - // Some protocols may have multiple services associated with - // them (for example IPMI). Look to see if we've already added - // an entry for the current protocol. - auto find = std::ranges::find_if( - socketData, - [&kv](const std::tuple<std::string, std::string, bool>& i) { - return std::get<1>(i) == kv.first; - }); - if (find != socketData.end()) + for (const auto& kv : protocolToDBus) { - // It only takes one enabled systemd service to consider a - // protocol enabled so if the current entry already has it - // enabled (or the new one is disabled) then just continue, - // otherwise remove the current one and add this new one. - if (std::get<2>(*find) || !isProtocolEnabled) + // We are interested in services, which starts with + // mapped service name + if (unitNameStr != kv.second) { - // Already registered as enabled or current one is not - // enabled, nothing to do - BMCWEB_LOG_DEBUG( - "protocolName: {}, already true or current one is false: {}", - kv.first, isProtocolEnabled); - break; + continue; + } + + const std::string& socketPath = + std::get<NET_PROTO_UNIT_OBJ_PATH>(unit); + const std::string& unitState = + std::get<NET_PROTO_UNIT_SUB_STATE>(unit); + + bool isProtocolEnabled = ((unitState == "running") || + (unitState == "listening")); + + // Some protocols may have multiple services associated with + // them (for example IPMI). Look to see if we've already + // added an entry for the current protocol. + auto find = std::ranges::find_if( + socketData, + [&kv](const std::tuple<std::string, std::string, bool>& + i) { return std::get<1>(i) == kv.first; }); + if (find != socketData.end()) + { + // It only takes one enabled systemd service to consider + // a protocol enabled so if the current entry already + // has it enabled (or the new one is disabled) then just + // continue, otherwise remove the current one and add + // this new one. + if (std::get<2>(*find) || !isProtocolEnabled) + { + // Already registered as enabled or current one is + // not enabled, nothing to do + BMCWEB_LOG_DEBUG( + "protocolName: {}, already true or current one is false: {}", + kv.first, isProtocolEnabled); + break; + } + // Remove existing entry and replace with new one + // (below) + socketData.erase(find); } - // Remove existing entry and replace with new one (below) - socketData.erase(find); - } - socketData.emplace_back(socketPath, std::string(kv.first), - isProtocolEnabled); - // We found service, return from inner loop. - break; + socketData.emplace_back(socketPath, std::string(kv.first), + isProtocolEnabled); + // We found service, return from inner loop. + break; + } } - } - callback(ec, socketData); - }, + callback(ec, socketData); + }, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "ListUnits"); } @@ -213,46 +215,46 @@ void getPortNumber(const std::string& socketPath, CallbackFunc&& callback) [callback = std::forward<CallbackFunc>(callback)]( const boost::system::error_code& ec, const std::vector<std::tuple<std::string, std::string>>& resp) { - if (ec) - { - BMCWEB_LOG_ERROR("{}", ec); - callback(ec, 0); - return; - } - if (resp.empty()) - { - // Network Protocol Listen Response Elements is empty - boost::system::error_code ec1 = - boost::system::errc::make_error_code( - boost::system::errc::bad_message); - // return error code - callback(ec1, 0); - BMCWEB_LOG_ERROR("{}", ec1); - return; - } - const std::string& listenStream = - std::get<NET_PROTO_LISTEN_STREAM>(resp[0]); - const char* pa = &listenStream[listenStream.rfind(':') + 1]; - int port{0}; - if (auto [p, ec2] = std::from_chars(pa, nullptr, port); - ec2 != std::errc()) - { - // there is only two possibility invalid_argument and - // result_out_of_range - boost::system::error_code ec3 = - boost::system::errc::make_error_code( - boost::system::errc::invalid_argument); - if (ec2 == std::errc::result_out_of_range) + if (ec) + { + BMCWEB_LOG_ERROR("{}", ec); + callback(ec, 0); + return; + } + if (resp.empty()) { - ec3 = boost::system::errc::make_error_code( - boost::system::errc::result_out_of_range); + // Network Protocol Listen Response Elements is empty + boost::system::error_code ec1 = + boost::system::errc::make_error_code( + boost::system::errc::bad_message); + // return error code + callback(ec1, 0); + BMCWEB_LOG_ERROR("{}", ec1); + return; + } + const std::string& listenStream = + std::get<NET_PROTO_LISTEN_STREAM>(resp[0]); + const char* pa = &listenStream[listenStream.rfind(':') + 1]; + int port{0}; + if (auto [p, ec2] = std::from_chars(pa, nullptr, port); + ec2 != std::errc()) + { + // there is only two possibility invalid_argument and + // result_out_of_range + boost::system::error_code ec3 = + boost::system::errc::make_error_code( + boost::system::errc::invalid_argument); + if (ec2 == std::errc::result_out_of_range) + { + ec3 = boost::system::errc::make_error_code( + boost::system::errc::result_out_of_range); + } + // return error code + callback(ec3, 0); + BMCWEB_LOG_ERROR("{}", ec3); } - // return error code - callback(ec3, 0); - BMCWEB_LOG_ERROR("{}", ec3); - } - callback(ec, port); - }); + callback(ec, port); + }); } } // namespace redfish diff --git a/redfish-core/lib/redfish_v1.hpp b/redfish-core/lib/redfish_v1.hpp index bbda45bcdd..418f66c431 100644 --- a/redfish-core/lib/redfish_v1.hpp +++ b/redfish-core/lib/redfish_v1.hpp @@ -6,7 +6,6 @@ #include "http_response.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" -#include "schemas.hpp" #include "utility.hpp" #include <boost/url/format.hpp> @@ -86,15 +85,32 @@ inline void json["Name"] = "JsonSchemaFile Collection"; json["Description"] = "Collection of JsonSchemaFiles"; nlohmann::json::array_t members; - for (std::string_view schema : schemas) + + std::error_code ec; + std::filesystem::directory_iterator dirList( + "/usr/share/www/redfish/v1/JsonSchemas", ec); + if (ec) { + messages::internalError(asyncResp->res); + return; + } + for (const std::filesystem::path& file : dirList) + { + std::string filename = file.filename(); + std::vector<std::string> split; + bmcweb::split(split, filename, '.'); + if (split.empty()) + { + continue; + } nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}", - schema); + member["@odata.id"] = + boost::urls::format("/redfish/v1/JsonSchemas/{}", split[0]); members.emplace_back(std::move(member)); } + + json["Members@odata.count"] = members.size(); json["Members"] = std::move(members); - json["Members@odata.count"] = schemas.size(); } inline void jsonSchemaGet(App& app, const crow::Request& req, @@ -106,40 +122,100 @@ inline void jsonSchemaGet(App& app, const crow::Request& req, return; } - if (std::ranges::find(schemas, schema) == schemas.end()) + std::error_code ec; + std::filesystem::directory_iterator dirList( + "/usr/share/www/redfish/v1/JsonSchemas", ec); + if (ec) { messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); return; } + for (const std::filesystem::path& file : dirList) + { + std::string filename = file.filename(); + std::vector<std::string> split; + bmcweb::split(split, filename, '.'); + if (split.empty()) + { + continue; + } + BMCWEB_LOG_DEBUG("Checking {}", split[0]); + if (split[0] != schema) + { + continue; + } + + nlohmann::json& json = asyncResp->res.jsonValue; + json["@odata.id"] = + boost::urls::format("/redfish/v1/JsonSchemas/{}", schema); + json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile"; + json["Name"] = schema + " Schema File"; + json["Description"] = schema + " Schema File Location"; + json["Id"] = schema; + std::string schemaName = std::format("#{}.{}", schema, schema); + json["Schema"] = std::move(schemaName); + constexpr std::array<std::string_view, 1> languages{"en"}; + json["Languages"] = languages; + json["Languages@odata.count"] = languages.size(); + + nlohmann::json::array_t locationArray; + nlohmann::json::object_t locationEntry; + locationEntry["Language"] = "en"; + + locationEntry["PublicationUri"] = boost::urls::format( + "http://redfish.dmtf.org/schemas/v1/{}", filename); + locationEntry["Uri"] = boost::urls::format( + "/redfish/v1/JsonSchemas/{}/{}", schema, filename); + + locationArray.emplace_back(locationEntry); + + json["Location"] = std::move(locationArray); + json["Location@odata.count"] = 1; + return; + } + messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); +} - nlohmann::json& json = asyncResp->res.jsonValue; - json["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}", - schema); - json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile"; - json["Name"] = schema + " Schema File"; - json["Description"] = schema + " Schema File Location"; - json["Id"] = schema; - std::string schemaName = "#"; - schemaName += schema; - schemaName += "."; - schemaName += schema; - json["Schema"] = std::move(schemaName); - constexpr std::array<std::string_view, 1> languages{"en"}; - json["Languages"] = languages; - json["Languages@odata.count"] = languages.size(); - - nlohmann::json::array_t locationArray; - nlohmann::json::object_t locationEntry; - locationEntry["Language"] = "en"; - locationEntry["PublicationUri"] = "http://redfish.dmtf.org/schemas/v1/" + - schema + ".json"; - locationEntry["Uri"] = boost::urls::format( - "/redfish/v1/JsonSchemas/{}/{}", schema, std::string(schema) + ".json"); - - locationArray.emplace_back(locationEntry); - - json["Location"] = std::move(locationArray); - json["Location@odata.count"] = 1; +inline void + jsonSchemaGetFile(const crow::Request& /*req*/, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& schema, const std::string& schemaFile) +{ + // Sanity check the filename + if (schemaFile.find_first_not_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.") != + std::string::npos) + { + messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); + return; + } + // Schema path should look like /redfish/v1/JsonSchemas/Foo/Foo.x.json + // Make sure the two paths match. + if (!schemaFile.starts_with(schema)) + { + messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); + return; + } + std::filesystem::path filepath("/usr/share/www/redfish/v1/JsonSchemas"); + filepath /= schemaFile; + if (filepath.is_relative()) + { + messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); + return; + } + + crow::OpenCode ec = asyncResp->res.openFile(filepath); + if (ec == crow::OpenCode::FileDoesNotExist) + { + messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema); + return; + } + if (ec == crow::OpenCode::InternalError) + { + BMCWEB_LOG_DEBUG("failed to read file"); + messages::internalError(asyncResp->res); + return; + } } inline void requestRoutesRedfish(App& app) @@ -148,6 +224,10 @@ inline void requestRoutesRedfish(App& app) .methods(boost::beast::http::verb::get)( std::bind_front(redfishGet, std::ref(app))); + BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/<str>") + .privileges(redfish::privileges::getJsonSchemaFile) + .methods(boost::beast::http::verb::get)(jsonSchemaGetFile); + BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/") .privileges(redfish::privileges::getJsonSchemaFileCollection) .methods(boost::beast::http::verb::get)( diff --git a/redfish-core/lib/roles.hpp b/redfish-core/lib/roles.hpp index a0f4f34754..c17a2256be 100644 --- a/redfish-core/lib/roles.hpp +++ b/redfish-core/lib/roles.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -85,31 +85,33 @@ inline void requestRoutesRoles(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& roleId) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - std::optional<nlohmann::json::array_t> privArray = - getAssignedPrivFromRole(roleId); - if (!privArray) - { - messages::resourceNotFound(asyncResp->res, "Role", roleId); - - return; - } - - asyncResp->res.jsonValue["@odata.type"] = "#Role.v1_2_2.Role"; - asyncResp->res.jsonValue["Name"] = "User Role"; - asyncResp->res.jsonValue["Description"] = roleId + " User Role"; - asyncResp->res.jsonValue["OemPrivileges"] = nlohmann::json::array(); - asyncResp->res.jsonValue["IsPredefined"] = true; - asyncResp->res.jsonValue["Id"] = roleId; - asyncResp->res.jsonValue["RoleId"] = roleId; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/AccountService/Roles/{}", roleId); - asyncResp->res.jsonValue["AssignedPrivileges"] = std::move(*privArray); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + std::optional<nlohmann::json::array_t> privArray = + getAssignedPrivFromRole(roleId); + if (!privArray) + { + messages::resourceNotFound(asyncResp->res, "Role", roleId); + + return; + } + + asyncResp->res.jsonValue["@odata.type"] = "#Role.v1_2_2.Role"; + asyncResp->res.jsonValue["Name"] = "User Role"; + asyncResp->res.jsonValue["Description"] = roleId + " User Role"; + asyncResp->res.jsonValue["OemPrivileges"] = + nlohmann::json::array(); + asyncResp->res.jsonValue["IsPredefined"] = true; + asyncResp->res.jsonValue["Id"] = roleId; + asyncResp->res.jsonValue["RoleId"] = roleId; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/AccountService/Roles/{}", roleId); + asyncResp->res.jsonValue["AssignedPrivileges"] = + std::move(*privArray); + }); } inline void requestRoutesRoleCollection(App& app) @@ -119,46 +121,49 @@ inline void requestRoutesRoleCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/AccountService/Roles"; - asyncResp->res.jsonValue["@odata.type"] = - "#RoleCollection.RoleCollection"; - asyncResp->res.jsonValue["Name"] = "Roles Collection"; - asyncResp->res.jsonValue["Description"] = "BMC User Roles"; - - sdbusplus::asio::getProperty<std::vector<std::string>>( - *crow::connections::systemBus, "xyz.openbmc_project.User.Manager", - "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager", - "AllPrivileges", - [asyncResp](const boost::system::error_code& ec, - const std::vector<std::string>& privList) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; - memberArray = nlohmann::json::array(); - for (const std::string& priv : privList) - { - std::string role = getRoleFromPrivileges(priv); - if (!role.empty()) + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/AccountService/Roles/{}", role); - memberArray.emplace_back(std::move(member)); + return; } - } - asyncResp->res.jsonValue["Members@odata.count"] = - memberArray.size(); - }); - }); + + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/AccountService/Roles"; + asyncResp->res.jsonValue["@odata.type"] = + "#RoleCollection.RoleCollection"; + asyncResp->res.jsonValue["Name"] = "Roles Collection"; + asyncResp->res.jsonValue["Description"] = "BMC User Roles"; + + sdbusplus::asio::getProperty<std::vector<std::string>>( + *crow::connections::systemBus, + "xyz.openbmc_project.User.Manager", + "/xyz/openbmc_project/user", + "xyz.openbmc_project.User.Manager", "AllPrivileges", + [asyncResp](const boost::system::error_code& ec, + const std::vector<std::string>& privList) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + nlohmann::json& memberArray = + asyncResp->res.jsonValue["Members"]; + memberArray = nlohmann::json::array(); + for (const std::string& priv : privList) + { + std::string role = getRoleFromPrivileges(priv); + if (!role.empty()) + { + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/AccountService/Roles/{}", + role); + memberArray.emplace_back(std::move(member)); + } + } + asyncResp->res.jsonValue["Members@odata.count"] = + memberArray.size(); + }); + }); } } // namespace redfish diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp index 8be861ba65..f344220b01 100644 --- a/redfish-core/lib/sensors.hpp +++ b/redfish-core/lib/sensors.hpp @@ -1,31 +1,32 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_singleton.hpp" #include "dbus_utility.hpp" +#include "generated/enums/redundancy.hpp" #include "generated/enums/resource.hpp" -#include "generated/enums/sensor.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "str_utility.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" #include "utils/query_param.hpp" +#include "utils/sensor_utils.hpp" #include <boost/system/error_code.hpp> #include <boost/url/format.hpp> @@ -49,12 +50,6 @@ namespace redfish namespace sensors { -namespace node -{ -static constexpr std::string_view power = "Power"; -static constexpr std::string_view sensors = "Sensors"; -static constexpr std::string_view thermal = "Thermal"; -} // namespace node // clang-format off namespace dbus @@ -98,99 +93,22 @@ constexpr auto thermalPaths = std::to_array<std::string_view>({ } // namespace dbus // clang-format on +constexpr std::string_view powerNodeStr = sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::powerNode); +constexpr std::string_view sensorsNodeStr = + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::sensorsNode); +constexpr std::string_view thermalNodeStr = + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::thermalNode); + using sensorPair = std::pair<std::string_view, std::span<const std::string_view>>; static constexpr std::array<sensorPair, 3> paths = { - {{node::power, dbus::powerPaths}, - {node::sensors, dbus::sensorPaths}, - {node::thermal, dbus::thermalPaths}}}; - -inline sensor::ReadingType toReadingType(std::string_view sensorType) -{ - if (sensorType == "voltage") - { - return sensor::ReadingType::Voltage; - } - if (sensorType == "power") - { - return sensor::ReadingType::Power; - } - if (sensorType == "current") - { - return sensor::ReadingType::Current; - } - if (sensorType == "fan_tach") - { - return sensor::ReadingType::Rotational; - } - if (sensorType == "temperature") - { - return sensor::ReadingType::Temperature; - } - if (sensorType == "fan_pwm" || sensorType == "utilization") - { - return sensor::ReadingType::Percent; - } - if (sensorType == "humidity") - { - return sensor::ReadingType::Humidity; - } - if (sensorType == "altitude") - { - return sensor::ReadingType::Altitude; - } - if (sensorType == "airflow") - { - return sensor::ReadingType::AirFlow; - } - if (sensorType == "energy") - { - return sensor::ReadingType::EnergyJoules; - } - return sensor::ReadingType::Invalid; -} + {{sensors::powerNodeStr, dbus::powerPaths}, + {sensors::sensorsNodeStr, dbus::sensorPaths}, + {sensors::thermalNodeStr, dbus::thermalPaths}}}; -inline std::string_view toReadingUnits(std::string_view sensorType) -{ - if (sensorType == "voltage") - { - return "V"; - } - if (sensorType == "power") - { - return "W"; - } - if (sensorType == "current") - { - return "A"; - } - if (sensorType == "fan_tach") - { - return "RPM"; - } - if (sensorType == "temperature") - { - return "Cel"; - } - if (sensorType == "fan_pwm" || sensorType == "utilization" || - sensorType == "humidity") - { - return "%"; - } - if (sensorType == "altitude") - { - return "m"; - } - if (sensorType == "airflow") - { - return "cft_i/min"; - } - if (sensorType == "energy") - { - return "J"; - } - return ""; -} } // namespace sensors /** @@ -206,18 +124,17 @@ class SensorsAsyncResp struct SensorData { - const std::string name; + std::string name; std::string uri; - const std::string dbusPath; + std::string dbusPath; }; SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn, const std::string& chassisIdIn, std::span<const std::string_view> typesIn, std::string_view subNode) : - asyncResp(asyncRespIn), - chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode), - efficientExpand(false) + asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn), + chassisSubNode(subNode), efficientExpand(false) {} // Store extra data about sensor mapping and return it in callback @@ -226,9 +143,9 @@ class SensorsAsyncResp std::span<const std::string_view> typesIn, std::string_view subNode, DataCompleteCb&& creationComplete) : - asyncResp(asyncRespIn), - chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode), - efficientExpand(false), metadata{std::vector<SensorData>()}, + asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn), + chassisSubNode(subNode), efficientExpand(false), + metadata{std::vector<SensorData>()}, dataComplete{std::move(creationComplete)} {} @@ -237,9 +154,8 @@ class SensorsAsyncResp const std::string& chassisIdIn, std::span<const std::string_view> typesIn, const std::string_view& subNode, bool efficientExpandIn) : - asyncResp(asyncRespIn), - chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode), - efficientExpand(efficientExpandIn) + asyncResp(asyncRespIn), chassisId(chassisIdIn), types(typesIn), + chassisSubNode(subNode), efficientExpand(efficientExpandIn) {} ~SensorsAsyncResp() @@ -307,48 +223,7 @@ class SensorsAsyncResp DataCompleteCb dataComplete; }; -/** - * Possible states for physical inventory leds - */ -enum class LedState -{ - OFF, - ON, - BLINK, - UNKNOWN -}; - -/** - * D-Bus inventory item associated with one or more sensors. - */ -class InventoryItem -{ - public: - explicit InventoryItem(const std::string& objPath) : objectPath(objPath) - { - // Set inventory item name to last node of object path - sdbusplus::message::object_path path(objectPath); - name = path.filename(); - if (name.empty()) - { - BMCWEB_LOG_ERROR("Failed to find '/' in {}", objectPath); - } - } - - std::string objectPath; - std::string name; - bool isPresent = true; - bool isFunctional = true; - bool isPowerSupply = false; - int powerSupplyEfficiencyPercent = -1; - std::string manufacturer; - std::string model; - std::string partNumber; - std::string serialNumber; - std::set<std::string> sensors; - std::string ledObjectPath; - LedState ledState = LedState::UNKNOWN; -}; +using InventoryItem = sensor_utils::InventoryItem; /** * @brief Get objects with connection necessary for sensors @@ -373,50 +248,51 @@ void getObjectsWithConnection( [callback = std::forward<Callback>(callback), sensorsAsyncResp, sensorNames](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - // Response handler for parsing objects subtree - BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler enter"); - if (ec) - { - messages::internalError(sensorsAsyncResp->asyncResp->res); - BMCWEB_LOG_ERROR( - "getObjectsWithConnection resp_handler: Dbus error {}", ec); - return; - } + // Response handler for parsing objects subtree + BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler enter"); + if (ec) + { + messages::internalError(sensorsAsyncResp->asyncResp->res); + BMCWEB_LOG_ERROR( + "getObjectsWithConnection resp_handler: Dbus error {}", ec); + return; + } - BMCWEB_LOG_DEBUG("Found {} subtrees", subtree.size()); + BMCWEB_LOG_DEBUG("Found {} subtrees", subtree.size()); - // Make unique list of connections only for requested sensor types and - // found in the chassis - std::set<std::string> connections; - std::set<std::pair<std::string, std::string>> objectsWithConnection; + // Make unique list of connections only for requested sensor types + // and found in the chassis + std::set<std::string> connections; + std::set<std::pair<std::string, std::string>> objectsWithConnection; - 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) - { - if (sensorNames->find(object.first) != sensorNames->end()) + for (const std::pair<std::string, + std::vector<std::pair< + std::string, std::vector<std::string>>>>& + object : subtree) { - for (const std::pair<std::string, std::vector<std::string>>& - objData : object.second) + if (sensorNames->find(object.first) != sensorNames->end()) { - BMCWEB_LOG_DEBUG("Adding connection: {}", objData.first); - connections.insert(objData.first); - objectsWithConnection.insert( - std::make_pair(object.first, objData.first)); + for (const std::pair<std::string, std::vector<std::string>>& + objData : object.second) + { + BMCWEB_LOG_DEBUG("Adding connection: {}", + objData.first); + connections.insert(objData.first); + objectsWithConnection.insert( + std::make_pair(object.first, objData.first)); + } } } - } - BMCWEB_LOG_DEBUG("Found {} connections", connections.size()); - callback(std::move(connections), std::move(objectsWithConnection)); - BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler exit"); - }); + BMCWEB_LOG_DEBUG("Found {} connections", connections.size()); + callback(std::move(connections), std::move(objectsWithConnection)); + BMCWEB_LOG_DEBUG("getObjectsWithConnection resp_handler exit"); + }); BMCWEB_LOG_DEBUG("getObjectsWithConnection exit"); } @@ -458,7 +334,7 @@ inline void reduceSensorList( if ((allSensors == nullptr) || (activeSensors == nullptr)) { messages::resourceNotFound(res, chassisSubNode, - chassisSubNode == sensors::node::thermal + chassisSubNode == sensors::thermalNodeStr ? "Temperatures" : "Voltages"); @@ -490,17 +366,17 @@ inline void reduceSensorList( inline void populateChassisNode(nlohmann::json& jsonValue, std::string_view chassisSubNode) { - if (chassisSubNode == sensors::node::power) + if (chassisSubNode == sensors::powerNodeStr) { jsonValue["@odata.type"] = "#Power.v1_5_2.Power"; } - else if (chassisSubNode == sensors::node::thermal) + else if (chassisSubNode == sensors::thermalNodeStr) { jsonValue["@odata.type"] = "#Thermal.v1_4_0.Thermal"; jsonValue["Fans"] = nlohmann::json::array(); jsonValue["Temperatures"] = nlohmann::json::array(); } - else if (chassisSubNode == sensors::node::sensors) + else if (chassisSubNode == sensors::sensorsNodeStr) { jsonValue["@odata.type"] = "#SensorCollection.SensorCollection"; jsonValue["Description"] = "Collection of Sensors for this Chassis"; @@ -508,7 +384,7 @@ inline void populateChassisNode(nlohmann::json& jsonValue, jsonValue["Members@odata.count"] = 0; } - if (chassisSubNode != sensors::node::sensors) + if (chassisSubNode != sensors::sensorsNodeStr) { jsonValue["Id"] = chassisSubNode; } @@ -536,456 +412,72 @@ void getChassis(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "/xyz/openbmc_project/inventory", 0, interfaces, [callback = std::forward<Callback>(callback), asyncResp, chassisIdStr{std::string(chassisId)}, - chassisSubNode{std::string(chassisSubNode)}, sensorTypes]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& chassisPaths) { - BMCWEB_LOG_DEBUG("getChassis respHandler enter"); - if (ec) - { - BMCWEB_LOG_ERROR("getChassis respHandler DBUS error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - const std::string* chassisPath = nullptr; - for (const std::string& chassis : chassisPaths) - { - sdbusplus::message::object_path path(chassis); - std::string chassisName = path.filename(); - if (chassisName.empty()) - { - BMCWEB_LOG_ERROR("Failed to find '/' in {}", chassis); - continue; - } - if (chassisName == chassisIdStr) + chassisSubNode{std::string(chassisSubNode)}, + sensorTypes](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& + chassisPaths) mutable { + BMCWEB_LOG_DEBUG("getChassis respHandler enter"); + if (ec) { - chassisPath = &chassis; - break; + BMCWEB_LOG_ERROR("getChassis respHandler DBUS error: {}", ec); + messages::internalError(asyncResp->res); + return; } - } - if (chassisPath == nullptr) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisIdStr); - return; - } - populateChassisNode(asyncResp->res.jsonValue, chassisSubNode); - - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Chassis/{}/{}", chassisIdStr, chassisSubNode); - - // Get the list of all sensors for this Chassis element - std::string sensorPath = *chassisPath + "/all_sensors"; - dbus::utility::getAssociationEndPoints( - sensorPath, - [asyncResp, chassisSubNode, sensorTypes, - callback = std::forward<const Callback>(callback)]( - const boost::system::error_code& ec2, - const dbus::utility::MapperEndPoints& nodeSensorList) { - if (ec2) + const std::string* chassisPath = nullptr; + for (const std::string& chassis : chassisPaths) { - if (ec2.value() != EBADR) + sdbusplus::message::object_path path(chassis); + std::string chassisName = path.filename(); + if (chassisName.empty()) { - messages::internalError(asyncResp->res); - return; + BMCWEB_LOG_ERROR("Failed to find '/' in {}", chassis); + continue; } - } - const std::shared_ptr<std::set<std::string>> culledSensorList = - std::make_shared<std::set<std::string>>(); - reduceSensorList(asyncResp->res, chassisSubNode, sensorTypes, - &nodeSensorList, culledSensorList); - BMCWEB_LOG_DEBUG("Finishing with {}", culledSensorList->size()); - callback(culledSensorList); - }); - }); - BMCWEB_LOG_DEBUG("getChassis exit"); -} - -/** - * @brief Returns the Redfish State value for the specified inventory item. - * @param inventoryItem D-Bus inventory item associated with a sensor. - * @param sensorAvailable Boolean representing if D-Bus sensor is marked as - * available. - * @return State value for inventory item. - */ -inline resource::State getState(const InventoryItem* inventoryItem, - const bool sensorAvailable) -{ - if ((inventoryItem != nullptr) && !(inventoryItem->isPresent)) - { - return resource::State::Absent; - } - - if (!sensorAvailable) - { - return resource::State::UnavailableOffline; - } - - return resource::State::Enabled; -} - -/** - * @brief Returns the Redfish Health value for the specified sensor. - * @param sensorJson Sensor JSON object. - * @param valuesDict Map of all sensor DBus values. - * @param inventoryItem D-Bus inventory item associated with the sensor. Will - * be nullptr if no associated inventory item was found. - * @return Health value for sensor. - */ -inline std::string getHealth(nlohmann::json& sensorJson, - const dbus::utility::DBusPropertiesMap& valuesDict, - const InventoryItem* inventoryItem) -{ - // Get current health value (if any) in the sensor JSON object. Some JSON - // objects contain multiple sensors (such as PowerSupplies). We want to set - // the overall health to be the most severe of any of the sensors. - std::string currentHealth; - auto statusIt = sensorJson.find("Status"); - if (statusIt != sensorJson.end()) - { - auto healthIt = statusIt->find("Health"); - if (healthIt != statusIt->end()) - { - std::string* health = healthIt->get_ptr<std::string*>(); - if (health != nullptr) - { - currentHealth = *health; - } - } - } - - // If current health in JSON object is already Critical, return that. This - // should override the sensor health, which might be less severe. - if (currentHealth == "Critical") - { - return "Critical"; - } - - const bool* criticalAlarmHigh = nullptr; - const bool* criticalAlarmLow = nullptr; - const bool* warningAlarmHigh = nullptr; - const bool* warningAlarmLow = nullptr; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), valuesDict, "CriticalAlarmHigh", - criticalAlarmHigh, "CriticalAlarmLow", criticalAlarmLow, - "WarningAlarmHigh", warningAlarmHigh, "WarningAlarmLow", - warningAlarmLow); - - if (success) - { - // Check if sensor has critical threshold alarm - if ((criticalAlarmHigh != nullptr && *criticalAlarmHigh) || - (criticalAlarmLow != nullptr && *criticalAlarmLow)) - { - return "Critical"; - } - } - - // Check if associated inventory item is not functional - if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional)) - { - return "Critical"; - } - - // If current health in JSON object is already Warning, return that. This - // should override the sensor status, which might be less severe. - if (currentHealth == "Warning") - { - return "Warning"; - } - - if (success) - { - // Check if sensor has warning threshold alarm - if ((warningAlarmHigh != nullptr && *warningAlarmHigh) || - (warningAlarmLow != nullptr && *warningAlarmLow)) - { - return "Warning"; - } - } - - return "OK"; -} - -inline void setLedState(nlohmann::json& sensorJson, - const InventoryItem* inventoryItem) -{ - if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty()) - { - switch (inventoryItem->ledState) - { - case LedState::OFF: - sensorJson["IndicatorLED"] = "Off"; - break; - case LedState::ON: - sensorJson["IndicatorLED"] = "Lit"; - break; - case LedState::BLINK: - sensorJson["IndicatorLED"] = "Blinking"; - break; - default: - break; - } - } -} - -/** - * @brief Builds a json sensor representation of a sensor. - * @param sensorName The name of the sensor to be built - * @param sensorType The type (temperature, fan_tach, etc) of the sensor to - * build - * @param chassisSubNode The subnode (thermal, sensor, etc) of the sensor - * @param propertiesDict A dictionary of the properties to build the sensor - * from. - * @param sensorJson The json object to fill - * @param inventoryItem D-Bus inventory item associated with the sensor. Will - * be nullptr if no associated inventory item was found. - */ -inline void objectPropertiesToJson( - std::string_view sensorName, std::string_view sensorType, - std::string_view chassisSubNode, - const dbus::utility::DBusPropertiesMap& propertiesDict, - nlohmann::json& sensorJson, InventoryItem* inventoryItem) -{ - if (chassisSubNode == sensors::node::sensors) - { - std::string subNodeEscaped(sensorType); - auto remove = std::ranges::remove(subNodeEscaped, '_'); - subNodeEscaped.erase(std::ranges::begin(remove), subNodeEscaped.end()); - - // For sensors in SensorCollection we set Id instead of MemberId, - // including power sensors. - subNodeEscaped += '_'; - subNodeEscaped += sensorName; - sensorJson["Id"] = std::move(subNodeEscaped); - - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - else if (sensorType != "power") - { - // Set MemberId and Name for non-power sensors. For PowerSupplies and - // PowerControl, those properties have more general values because - // multiple sensors can be stored in the same JSON object. - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - - const bool* checkAvailable = nullptr; - bool available = true; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", - checkAvailable); - if (!success) - { - messages::internalError(); - } - if (checkAvailable != nullptr) - { - available = *checkAvailable; - } - - sensorJson["Status"]["State"] = getState(inventoryItem, available); - sensorJson["Status"]["Health"] = getHealth(sensorJson, propertiesDict, - inventoryItem); - - // 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; - - nlohmann::json::json_pointer unit("/Reading"); - if (chassisSubNode == sensors::node::sensors) - { - sensorJson["@odata.type"] = "#Sensor.v1_2_0.Sensor"; - - sensor::ReadingType readingType = sensors::toReadingType(sensorType); - if (readingType == sensor::ReadingType::Invalid) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", - sensorType); - } - else - { - sensorJson["ReadingType"] = readingType; - } - - std::string_view readingUnits = sensors::toReadingUnits(sensorType); - if (readingUnits.empty()) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", - sensorType); - } - else - { - sensorJson["ReadingUnits"] = readingUnits; - } - } - else if (sensorType == "temperature") - { - unit = "/ReadingCelsius"_json_pointer; - sensorJson["@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"_json_pointer; - sensorJson["ReadingUnits"] = "RPM"; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - setLedState(sensorJson, inventoryItem); - forceToInt = true; - } - else if (sensorType == "fan_pwm") - { - unit = "/Reading"_json_pointer; - sensorJson["ReadingUnits"] = "Percent"; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - setLedState(sensorJson, inventoryItem); - forceToInt = true; - } - else if (sensorType == "voltage") - { - unit = "/ReadingVolts"_json_pointer; - sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; - } - else if (sensorType == "power") - { - std::string lower; - std::ranges::transform(sensorName, std::back_inserter(lower), - bmcweb::asciiToLower); - if (lower == "total_power") - { - sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; - // Put multiple "sensors" into a single PowerControl, so have - // generic names for MemberId and Name. Follows Redfish mockup. - sensorJson["MemberId"] = "0"; - sensorJson["Name"] = "Chassis Power Control"; - unit = "/PowerConsumedWatts"_json_pointer; - } - else if (lower.find("input") != std::string::npos) - { - unit = "/PowerInputWatts"_json_pointer; - } - else - { - unit = "/PowerOutputWatts"_json_pointer; - } - } - 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*, nlohmann::json::json_pointer>> - properties; - properties.reserve(7); - - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); - - if (chassisSubNode == sensors::node::sensors) - { - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", - "/Thresholds/UpperCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", - "/Thresholds/LowerCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", - "/Thresholds/UpperCritical/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", - "/Thresholds/LowerCritical/Reading"_json_pointer); - } - else if (sensorType != "power") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningHigh", - "/UpperThresholdNonCritical"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", - "WarningLow", - "/LowerThresholdNonCritical"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalHigh", - "/UpperThresholdCritical"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", - "CriticalLow", - "/LowerThresholdCritical"_json_pointer); - } - - // TODO Need to get UpperThresholdFatal and LowerThresholdFatal - - if (chassisSubNode == sensors::node::sensors) - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "/ReadingRangeMin"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "/ReadingRangeMax"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", - "Accuracy", "/Accuracy"_json_pointer); - } - else if (sensorType == "temperature") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "/MinReadingRangeTemp"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "/MaxReadingRangeTemp"_json_pointer); - } - else if (sensorType != "power") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", - "/MinReadingRange"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", - "/MaxReadingRange"_json_pointer); - } - - for (const std::tuple<const char*, const char*, - nlohmann::json::json_pointer>& p : properties) - { - for (const auto& [valueName, valueVariant] : propertiesDict) - { - if (valueName != std::get<1>(p)) - { - continue; - } - - // The property we want to set may be nested json, so use - // a json_pointer for easy indexing into the json structure. - const nlohmann::json::json_pointer& key = std::get<2>(p); - - const double* doubleValue = std::get_if<double>(&valueVariant); - if (doubleValue == nullptr) - { - BMCWEB_LOG_ERROR("Got value interface that wasn't double"); - continue; - } - if (!std::isfinite(*doubleValue)) - { - if (valueName == "Value") + if (chassisName == chassisIdStr) { - // Readings are allowed to be NAN for unavailable; coerce - // them to null in the json response. - sensorJson[key] = nullptr; - continue; + chassisPath = &chassis; + break; } - BMCWEB_LOG_WARNING("Sensor value for {} was unexpectedly {}", - valueName, *doubleValue); - continue; - } - if (forceToInt) - { - sensorJson[key] = static_cast<int64_t>(*doubleValue); } - else + if (chassisPath == nullptr) { - sensorJson[key] = *doubleValue; + messages::resourceNotFound(asyncResp->res, "Chassis", + chassisIdStr); + return; } - } - } + populateChassisNode(asyncResp->res.jsonValue, chassisSubNode); + + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}/{}", chassisIdStr, chassisSubNode); + + // Get the list of all sensors for this Chassis element + std::string sensorPath = *chassisPath + "/all_sensors"; + dbus::utility::getAssociationEndPoints( + sensorPath, [asyncResp, chassisSubNode, sensorTypes, + callback = std::forward<Callback>(callback)]( + const boost::system::error_code& ec2, + const dbus::utility::MapperEndPoints& + nodeSensorList) mutable { + if (ec2) + { + if (ec2.value() != EBADR) + { + messages::internalError(asyncResp->res); + return; + } + } + const std::shared_ptr<std::set<std::string>> + culledSensorList = + std::make_shared<std::set<std::string>>(); + reduceSensorList(asyncResp->res, chassisSubNode, + sensorTypes, &nodeSensorList, + culledSensorList); + BMCWEB_LOG_DEBUG("Finishing with {}", + culledSensorList->size()); + callback(culledSensorList); + }); + }); + BMCWEB_LOG_DEBUG("getChassis exit"); } /** @@ -1002,14 +494,15 @@ inline void objectPropertiesToJson( */ inline void objectInterfacesToJson( const std::string& sensorName, const std::string& sensorType, - const std::string& chassisSubNode, + const sensor_utils::ChassisSubNode chassisSubNode, const dbus::utility::DBusInterfacesMap& interfacesDict, nlohmann::json& sensorJson, InventoryItem* inventoryItem) { for (const auto& [interface, valuesDict] : interfacesDict) { - objectPropertiesToJson(sensorName, sensorType, chassisSubNode, - valuesDict, sensorJson, inventoryItem); + sensor_utils::objectPropertiesToJson( + sensorName, sensorType, chassisSubNode, valuesDict, sensorJson, + inventoryItem); } BMCWEB_LOG_DEBUG("Added sensor {}", sensorName); } @@ -1024,167 +517,184 @@ inline void populateFanRedundancy( [sensorsAsyncResp]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& resp) { - if (ec) - { - return; // don't have to have this interface - } - for (const std::pair<std::string, dbus::utility::MapperServiceMap>& - pathPair : resp) - { - const std::string& path = pathPair.first; - const dbus::utility::MapperServiceMap& objDict = pathPair.second; - if (objDict.empty()) + if (ec) { - continue; // this should be impossible + return; // don't have to have this interface } - - const std::string& owner = objDict.begin()->first; - dbus::utility::getAssociationEndPoints( - path + "/chassis", - [path, owner, sensorsAsyncResp]( - const boost::system::error_code& ec2, - const dbus::utility::MapperEndPoints& endpoints) { - if (ec2) + for (const std::pair<std::string, dbus::utility::MapperServiceMap>& + pathPair : resp) + { + const std::string& path = pathPair.first; + const dbus::utility::MapperServiceMap& objDict = + pathPair.second; + if (objDict.empty()) { - return; // if they don't have an association we - // can't tell what chassis is + continue; // this should be impossible } - auto found = std::ranges::find_if( - endpoints, [sensorsAsyncResp](const std::string& entry) { - return entry.find(sensorsAsyncResp->chassisId) != - std::string::npos; - }); - if (found == endpoints.end()) - { - return; - } - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, owner, path, - "xyz.openbmc_project.Control.FanRedundancy", - [path, sensorsAsyncResp]( - const boost::system::error_code& ec3, - const dbus::utility::DBusPropertiesMap& ret) { - if (ec3) - { - return; // don't have to have this - // interface - } + const std::string& owner = objDict.begin()->first; + dbus::utility::getAssociationEndPoints( + path + "/chassis", + [path, owner, sensorsAsyncResp]( + const boost::system::error_code& ec2, + const dbus::utility::MapperEndPoints& endpoints) { + if (ec2) + { + return; // if they don't have an association we + // can't tell what chassis is + } + auto found = std::ranges::find_if( + endpoints, + [sensorsAsyncResp](const std::string& entry) { + return entry.find( + sensorsAsyncResp->chassisId) != + std::string::npos; + }); + + if (found == endpoints.end()) + { + return; + } + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, owner, path, + "xyz.openbmc_project.Control.FanRedundancy", + [path, sensorsAsyncResp]( + const boost::system::error_code& ec3, + const dbus::utility::DBusPropertiesMap& ret) { + if (ec3) + { + return; // don't have to have this + // interface + } - const uint8_t* allowedFailures = nullptr; - const std::vector<std::string>* collection = nullptr; - const std::string* status = nullptr; + const uint8_t* allowedFailures = nullptr; + const std::vector<std::string>* collection = + nullptr; + const std::string* status = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), ret, - "AllowedFailures", allowedFailures, "Collection", - collection, "Status", status); + const bool success = + sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), ret, + "AllowedFailures", allowedFailures, + "Collection", collection, "Status", + status); - if (!success) - { - messages::internalError( - sensorsAsyncResp->asyncResp->res); - return; - } + if (!success) + { + messages::internalError( + sensorsAsyncResp->asyncResp->res); + return; + } - if (allowedFailures == nullptr || collection == nullptr || - status == nullptr) - { - BMCWEB_LOG_ERROR("Invalid redundancy interface"); - messages::internalError( - sensorsAsyncResp->asyncResp->res); - return; - } + if (allowedFailures == nullptr || + collection == nullptr || status == nullptr) + { + BMCWEB_LOG_ERROR( + "Invalid redundancy interface"); + messages::internalError( + sensorsAsyncResp->asyncResp->res); + return; + } - sdbusplus::message::object_path objectPath(path); - std::string name = objectPath.filename(); - if (name.empty()) - { - // this should be impossible - messages::internalError( - sensorsAsyncResp->asyncResp->res); - return; - } - std::ranges::replace(name, '_', ' '); + sdbusplus::message::object_path objectPath( + path); + std::string name = objectPath.filename(); + if (name.empty()) + { + // this should be impossible + messages::internalError( + sensorsAsyncResp->asyncResp->res); + return; + } + std::ranges::replace(name, '_', ' '); - std::string health; + std::string health; - if (status->ends_with("Full")) - { - health = "OK"; - } - else if (status->ends_with("Degraded")) - { - health = "Warning"; - } - else - { - health = "Critical"; - } - nlohmann::json::array_t redfishCollection; - const auto& fanRedfish = - sensorsAsyncResp->asyncResp->res.jsonValue["Fans"]; - for (const std::string& item : *collection) - { - sdbusplus::message::object_path itemPath(item); - std::string itemName = itemPath.filename(); - if (itemName.empty()) - { - continue; - } - /* - todo(ed): merge patch that fixes the names - std::replace(itemName.begin(), - itemName.end(), '_', ' ');*/ - auto schemaItem = std::ranges::find_if( - fanRedfish, [itemName](const nlohmann::json& fan) { - return fan["Name"] == itemName; - }); - if (schemaItem != fanRedfish.end()) - { - nlohmann::json::object_t collectionId; - collectionId["@odata.id"] = - (*schemaItem)["@odata.id"]; - redfishCollection.emplace_back( - std::move(collectionId)); - } - else - { - BMCWEB_LOG_ERROR("failed to find fan in schema"); - messages::internalError( - sensorsAsyncResp->asyncResp->res); - return; - } - } + if (status->ends_with("Full")) + { + health = "OK"; + } + else if (status->ends_with("Degraded")) + { + health = "Warning"; + } + else + { + health = "Critical"; + } + nlohmann::json::array_t redfishCollection; + const auto& fanRedfish = + sensorsAsyncResp->asyncResp->res + .jsonValue["Fans"]; + for (const std::string& item : *collection) + { + sdbusplus::message::object_path itemPath( + item); + std::string itemName = itemPath.filename(); + if (itemName.empty()) + { + continue; + } + /* + todo(ed): merge patch that fixes the names + std::replace(itemName.begin(), + itemName.end(), '_', ' ');*/ + auto schemaItem = std::ranges::find_if( + fanRedfish, + [itemName](const nlohmann::json& fan) { + return fan["Name"] == itemName; + }); + if (schemaItem != fanRedfish.end()) + { + nlohmann::json::object_t collectionId; + collectionId["@odata.id"] = + (*schemaItem)["@odata.id"]; + redfishCollection.emplace_back( + std::move(collectionId)); + } + else + { + BMCWEB_LOG_ERROR( + "failed to find fan in schema"); + messages::internalError( + sensorsAsyncResp->asyncResp->res); + return; + } + } - size_t minNumNeeded = collection->empty() - ? 0 - : collection->size() - - *allowedFailures; - nlohmann::json& jResp = sensorsAsyncResp->asyncResp->res - .jsonValue["Redundancy"]; - - nlohmann::json::object_t redundancy; - boost::urls::url url = - boost::urls::format("/redfish/v1/Chassis/{}/{}", - sensorsAsyncResp->chassisId, - sensorsAsyncResp->chassisSubNode); - url.set_fragment(("/Redundancy"_json_pointer / jResp.size()) - .to_string()); - redundancy["@odata.id"] = std::move(url); - redundancy["@odata.type"] = "#Redundancy.v1_3_2.Redundancy"; - redundancy["MinNumNeeded"] = minNumNeeded; - redundancy["Mode"] = "N+m"; - redundancy["Name"] = name; - redundancy["RedundancySet"] = redfishCollection; - redundancy["Status"]["Health"] = health; - redundancy["Status"]["State"] = "Enabled"; - - jResp.emplace_back(std::move(redundancy)); - }); - }); - } - }); + size_t minNumNeeded = + collection->empty() + ? 0 + : collection->size() - *allowedFailures; + nlohmann::json& jResp = + sensorsAsyncResp->asyncResp->res + .jsonValue["Redundancy"]; + + nlohmann::json::object_t redundancy; + boost::urls::url url = boost::urls::format( + "/redfish/v1/Chassis/{}/{}", + sensorsAsyncResp->chassisId, + sensorsAsyncResp->chassisSubNode); + url.set_fragment( + ("/Redundancy"_json_pointer / jResp.size()) + .to_string()); + redundancy["@odata.id"] = std::move(url); + redundancy["@odata.type"] = + "#Redundancy.v1_3_2.Redundancy"; + redundancy["MinNumNeeded"] = minNumNeeded; + redundancy["Mode"] = + redundancy::RedundancyType::NPlusM; + redundancy["Name"] = name; + redundancy["RedundancySet"] = redfishCollection; + redundancy["Status"]["Health"] = health; + redundancy["Status"]["State"] = + resource::State::Enabled; + + jResp.emplace_back(std::move(redundancy)); + }); + }); + } + }); } inline void @@ -1192,37 +702,41 @@ inline void { nlohmann::json& response = sensorsAsyncResp->asyncResp->res.jsonValue; std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"}; - if (sensorsAsyncResp->chassisSubNode == sensors::node::power) + if (sensorsAsyncResp->chassisSubNode == sensors::powerNodeStr) { sensorHeaders = {"Voltages", "PowerSupplies"}; } for (const std::string& sensorGroup : sensorHeaders) { nlohmann::json::iterator entry = response.find(sensorGroup); - if (entry != response.end()) + if (entry == response.end()) { - std::sort(entry->begin(), entry->end(), - [](const nlohmann::json& c1, const nlohmann::json& c2) { - return c1["Name"] < c2["Name"]; - }); + continue; + } + nlohmann::json::array_t* arr = + entry->get_ptr<nlohmann::json::array_t*>(); + if (arr == nullptr) + { + continue; + } + json_util::sortJsonArrayByKey(*arr, "Name"); - // add the index counts to the end of each entry - size_t count = 0; - for (nlohmann::json& sensorJson : *entry) + // add the index counts to the end of each entry + size_t count = 0; + for (nlohmann::json& sensorJson : *entry) + { + nlohmann::json::iterator odata = sensorJson.find("@odata.id"); + if (odata == sensorJson.end()) { - nlohmann::json::iterator odata = sensorJson.find("@odata.id"); - if (odata == sensorJson.end()) - { - continue; - } - std::string* value = odata->get_ptr<std::string*>(); - if (value != nullptr) - { - *value += "/" + std::to_string(count); - sensorJson["MemberId"] = std::to_string(count); - count++; - sensorsAsyncResp->updateUri(sensorJson["Name"], *value); - } + continue; + } + std::string* value = odata->get_ptr<std::string*>(); + if (value != nullptr) + { + *value += "/" + std::to_string(count); + sensorJson["MemberId"] = std::to_string(count); + count++; + sensorsAsyncResp->updateUri(sensorJson["Name"], *value); } } } @@ -1274,9 +788,8 @@ inline InventoryItem* findInventoryItemForSensor( * @param ledObjPath D-Bus object path of led. * @return Inventory item within vector, or nullptr if no match found. */ -inline InventoryItem* - findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems, - const std::string& ledObjPath) +inline InventoryItem* findInventoryItemForLed( + std::vector<InventoryItem>& inventoryItems, const std::string& ledObjPath) { for (InventoryItem& inventoryItem : inventoryItems) { @@ -1307,8 +820,8 @@ inline void addInventoryItem( const std::string& invItemObjPath, const std::string& sensorObjPath) { // Look for inventory item in vector - InventoryItem* inventoryItem = findInventoryItem(inventoryItems, - invItemObjPath); + InventoryItem* inventoryItem = + findInventoryItem(inventoryItems, invItemObjPath); // If inventory item doesn't exist in vector, add it if (inventoryItem == nullptr) @@ -1456,7 +969,7 @@ inline void storeInventoryItemData( * in recursive calls to this function. */ template <typename Callback> -static void getInventoryItemsData( +void getInventoryItemsData( std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, std::shared_ptr<std::vector<InventoryItem>> inventoryItems, std::shared_ptr<std::set<std::string>> invConnections, Callback&& callback, @@ -1486,39 +999,41 @@ static void getInventoryItemsData( [sensorsAsyncResp, inventoryItems, invConnections, callback = std::forward<Callback>(callback), invConnectionsIndex]( const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& resp) { - BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler enter"); - if (ec) - { - BMCWEB_LOG_ERROR( - "getInventoryItemsData respHandler DBus error {}", ec); - messages::internalError(sensorsAsyncResp->asyncResp->res); - return; - } - - // Loop through returned object paths - for (const auto& objDictEntry : resp) - { - const std::string& objPath = - static_cast<const std::string&>(objDictEntry.first); + const dbus::utility::ManagedObjectType& resp) mutable { + BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler enter"); + if (ec) + { + BMCWEB_LOG_ERROR( + "getInventoryItemsData respHandler DBus error {}", ec); + messages::internalError(sensorsAsyncResp->asyncResp->res); + return; + } - // If this object path is one of the specified inventory items - InventoryItem* inventoryItem = findInventoryItem(inventoryItems, - objPath); - if (inventoryItem != nullptr) + // Loop through returned object paths + for (const auto& objDictEntry : resp) { - // Store inventory data in InventoryItem - storeInventoryItemData(*inventoryItem, objDictEntry.second); + const std::string& objPath = + static_cast<const std::string&>(objDictEntry.first); + + // If this object path is one of the specified inventory + // items + InventoryItem* inventoryItem = + findInventoryItem(inventoryItems, objPath); + if (inventoryItem != nullptr) + { + // Store inventory data in InventoryItem + storeInventoryItemData(*inventoryItem, + objDictEntry.second); + } } - } - // Recurse to get inventory item data from next connection - getInventoryItemsData(sensorsAsyncResp, inventoryItems, - invConnections, std::move(callback), - invConnectionsIndex + 1); + // Recurse to get inventory item data from next connection + getInventoryItemsData(sensorsAsyncResp, inventoryItems, + invConnections, std::move(callback), + invConnectionsIndex + 1); - BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler exit"); - }); + BMCWEB_LOG_DEBUG("getInventoryItemsData respHandler exit"); + }); } BMCWEB_LOG_DEBUG("getInventoryItemsData exit"); @@ -1543,7 +1058,7 @@ static void getInventoryItemsData( * @param callback Callback to invoke when connections have been obtained. */ template <typename Callback> -static void getInventoryItemsConnections( +void getInventoryItemsConnections( const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems, Callback&& callback) @@ -1563,44 +1078,45 @@ static void getInventoryItemsConnections( [callback = std::forward<Callback>(callback), sensorsAsyncResp, inventoryItems]( const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - // Response handler for parsing output from GetSubTree - BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler enter"); - if (ec) - { - messages::internalError(sensorsAsyncResp->asyncResp->res); - BMCWEB_LOG_ERROR( - "getInventoryItemsConnections respHandler DBus error {}", ec); - return; - } + const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { + // Response handler for parsing output from GetSubTree + BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler enter"); + if (ec) + { + messages::internalError(sensorsAsyncResp->asyncResp->res); + BMCWEB_LOG_ERROR( + "getInventoryItemsConnections respHandler DBus error {}", + ec); + return; + } - // Make unique list of connections for desired inventory items - std::shared_ptr<std::set<std::string>> invConnections = - std::make_shared<std::set<std::string>>(); + // Make unique list of connections for desired inventory items + std::shared_ptr<std::set<std::string>> invConnections = + std::make_shared<std::set<std::string>>(); - // Loop through objects from GetSubTree - for (const std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>& - object : subtree) - { - // Check if object path is one of the specified inventory items - const std::string& objPath = object.first; - if (findInventoryItem(inventoryItems, objPath) != nullptr) + // Loop through objects from GetSubTree + for (const std::pair<std::string, + std::vector<std::pair< + std::string, std::vector<std::string>>>>& + object : subtree) { - // Store all connections to inventory item - for (const std::pair<std::string, std::vector<std::string>>& - objData : object.second) + // Check if object path is one of the specified inventory items + const std::string& objPath = object.first; + if (findInventoryItem(inventoryItems, objPath) != nullptr) { - const std::string& invConnection = objData.first; - invConnections->insert(invConnection); + // Store all connections to inventory item + for (const std::pair<std::string, std::vector<std::string>>& + objData : object.second) + { + const std::string& invConnection = objData.first; + invConnections->insert(invConnection); + } } } - } - callback(invConnections); - BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler exit"); - }); + callback(invConnections); + BMCWEB_LOG_DEBUG("getInventoryItemsConnections respHandler exit"); + }); BMCWEB_LOG_DEBUG("getInventoryItemsConnections exit"); } @@ -1625,7 +1141,7 @@ static void getInventoryItemsConnections( * @param callback Callback to invoke when inventory items have been obtained. */ template <typename Callback> -static void getInventoryItemAssociations( +void getInventoryItemAssociations( const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, const std::shared_ptr<std::set<std::string>>& sensorNames, Callback&& callback) @@ -1638,113 +1154,120 @@ static void getInventoryItemAssociations( "xyz.openbmc_project.ObjectMapper", path, [callback = std::forward<Callback>(callback), sensorsAsyncResp, sensorNames](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& resp) { - BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler enter"); - if (ec) - { - BMCWEB_LOG_ERROR( - "getInventoryItemAssociations respHandler DBus error {}", ec); - messages::internalError(sensorsAsyncResp->asyncResp->res); - return; - } - - // Create vector to hold list of inventory items - std::shared_ptr<std::vector<InventoryItem>> inventoryItems = - std::make_shared<std::vector<InventoryItem>>(); + const dbus::utility::ManagedObjectType& resp) mutable { + BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler enter"); + if (ec) + { + BMCWEB_LOG_ERROR( + "getInventoryItemAssociations respHandler DBus error {}", + ec); + messages::internalError(sensorsAsyncResp->asyncResp->res); + return; + } - // Loop through returned object paths - std::string sensorAssocPath; - sensorAssocPath.reserve(128); // avoid memory allocations - for (const auto& objDictEntry : resp) - { - const std::string& objPath = - static_cast<const std::string&>(objDictEntry.first); + // Create vector to hold list of inventory items + std::shared_ptr<std::vector<InventoryItem>> inventoryItems = + std::make_shared<std::vector<InventoryItem>>(); - // If path is inventory association for one of the specified sensors - for (const std::string& sensorName : *sensorNames) + // Loop through returned object paths + std::string sensorAssocPath; + sensorAssocPath.reserve(128); // avoid memory allocations + for (const auto& objDictEntry : resp) { - sensorAssocPath = sensorName; - sensorAssocPath += "/inventory"; - if (objPath == sensorAssocPath) + const std::string& objPath = + static_cast<const std::string&>(objDictEntry.first); + + // If path is inventory association for one of the specified + // sensors + for (const std::string& sensorName : *sensorNames) { - // Get Association interface for object path - for (const auto& [interface, values] : objDictEntry.second) + sensorAssocPath = sensorName; + sensorAssocPath += "/inventory"; + if (objPath == sensorAssocPath) { - if (interface == "xyz.openbmc_project.Association") + // Get Association interface for object path + for (const auto& [interface, values] : + objDictEntry.second) { - for (const auto& [valueName, value] : values) + if (interface == "xyz.openbmc_project.Association") { - if (valueName == "endpoints") + for (const auto& [valueName, value] : values) { - const std::vector<std::string>* endpoints = - std::get_if<std::vector<std::string>>( - &value); - if ((endpoints != nullptr) && - !endpoints->empty()) + if (valueName == "endpoints") { - // Add inventory item to vector - const std::string& invItemPath = - endpoints->front(); - addInventoryItem(inventoryItems, - invItemPath, - sensorName); + const std::vector<std::string>* + endpoints = std::get_if< + std::vector<std::string>>( + &value); + if ((endpoints != nullptr) && + !endpoints->empty()) + { + // Add inventory item to vector + const std::string& invItemPath = + endpoints->front(); + addInventoryItem(inventoryItems, + invItemPath, + sensorName); + } } } } } + break; } - break; } } - } - - // Now loop through the returned object paths again, this time to - // find the leds associated with the inventory items we just found - std::string inventoryAssocPath; - inventoryAssocPath.reserve(128); // avoid memory allocations - for (const auto& objDictEntry : resp) - { - const std::string& objPath = - static_cast<const std::string&>(objDictEntry.first); - for (InventoryItem& inventoryItem : *inventoryItems) + // Now loop through the returned object paths again, this time to + // find the leds associated with the inventory items we just found + std::string inventoryAssocPath; + inventoryAssocPath.reserve(128); // avoid memory allocations + for (const auto& objDictEntry : resp) { - inventoryAssocPath = inventoryItem.objectPath; - inventoryAssocPath += "/leds"; - if (objPath == inventoryAssocPath) + const std::string& objPath = + static_cast<const std::string&>(objDictEntry.first); + + for (InventoryItem& inventoryItem : *inventoryItems) { - for (const auto& [interface, values] : objDictEntry.second) + inventoryAssocPath = inventoryItem.objectPath; + inventoryAssocPath += "/leds"; + if (objPath == inventoryAssocPath) { - if (interface == "xyz.openbmc_project.Association") + for (const auto& [interface, values] : + objDictEntry.second) { - for (const auto& [valueName, value] : values) + if (interface == "xyz.openbmc_project.Association") { - if (valueName == "endpoints") + for (const auto& [valueName, value] : values) { - const std::vector<std::string>* endpoints = - std::get_if<std::vector<std::string>>( - &value); - if ((endpoints != nullptr) && - !endpoints->empty()) + if (valueName == "endpoints") { - // Add inventory item to vector - // Store LED path in inventory item - const std::string& ledPath = - endpoints->front(); - inventoryItem.ledObjectPath = ledPath; + const std::vector<std::string>* + endpoints = std::get_if< + std::vector<std::string>>( + &value); + if ((endpoints != nullptr) && + !endpoints->empty()) + { + // Add inventory item to vector + // Store LED path in inventory item + const std::string& ledPath = + endpoints->front(); + inventoryItem.ledObjectPath = + ledPath; + } } } } } - } - break; + break; + } } } - } - callback(inventoryItems); - BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler exit"); - }); + callback(inventoryItems); + BMCWEB_LOG_DEBUG("getInventoryItemAssociations respHandler exit"); + }); BMCWEB_LOG_DEBUG("getInventoryItemAssociations exit"); } @@ -1805,49 +1328,51 @@ void getInventoryLedData( // Response handler for Get State property auto respHandler = [sensorsAsyncResp, inventoryItems, ledConnections, ledPath, - callback = std::forward<Callback>(callback), ledConnectionsIndex]( - const boost::system::error_code& ec, const std::string& state) { - BMCWEB_LOG_DEBUG("getInventoryLedData respHandler enter"); - if (ec) - { - BMCWEB_LOG_ERROR( - "getInventoryLedData respHandler DBus error {}", ec); - messages::internalError(sensorsAsyncResp->asyncResp->res); - return; - } - - BMCWEB_LOG_DEBUG("Led state: {}", state); - // Find inventory item with this LED object path - InventoryItem* inventoryItem = - findInventoryItemForLed(*inventoryItems, ledPath); - if (inventoryItem != nullptr) - { - // Store LED state in InventoryItem - if (state.ends_with("On")) - { - inventoryItem->ledState = LedState::ON; - } - else if (state.ends_with("Blink")) - { - inventoryItem->ledState = LedState::BLINK; - } - else if (state.ends_with("Off")) + callback = std::forward<Callback>(callback), + ledConnectionsIndex](const boost::system::error_code& ec, + const std::string& state) mutable { + BMCWEB_LOG_DEBUG("getInventoryLedData respHandler enter"); + if (ec) { - inventoryItem->ledState = LedState::OFF; + BMCWEB_LOG_ERROR( + "getInventoryLedData respHandler DBus error {}", ec); + messages::internalError(sensorsAsyncResp->asyncResp->res); + return; } - else + + BMCWEB_LOG_DEBUG("Led state: {}", state); + // Find inventory item with this LED object path + InventoryItem* inventoryItem = + findInventoryItemForLed(*inventoryItems, ledPath); + if (inventoryItem != nullptr) { - inventoryItem->ledState = LedState::UNKNOWN; + // Store LED state in InventoryItem + if (state.ends_with("On")) + { + inventoryItem->ledState = sensor_utils::LedState::ON; + } + else if (state.ends_with("Blink")) + { + inventoryItem->ledState = sensor_utils::LedState::BLINK; + } + else if (state.ends_with("Off")) + { + inventoryItem->ledState = sensor_utils::LedState::OFF; + } + else + { + inventoryItem->ledState = + sensor_utils::LedState::UNKNOWN; + } } - } - // Recurse to get LED data from next connection - getInventoryLedData(sensorsAsyncResp, inventoryItems, - ledConnections, std::move(callback), - ledConnectionsIndex + 1); + // Recurse to get LED data from next connection + getInventoryLedData(sensorsAsyncResp, inventoryItems, + ledConnections, std::move(callback), + ledConnectionsIndex + 1); - BMCWEB_LOG_DEBUG("getInventoryLedData respHandler exit"); - }; + BMCWEB_LOG_DEBUG("getInventoryLedData respHandler exit"); + }; // Get the State property for the current LED sdbusplus::asio::getProperty<std::string>( @@ -1899,42 +1424,46 @@ void getInventoryLeds( [callback = std::forward<Callback>(callback), sensorsAsyncResp, inventoryItems]( const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - // Response handler for parsing output from GetSubTree - BMCWEB_LOG_DEBUG("getInventoryLeds respHandler enter"); - if (ec) - { - messages::internalError(sensorsAsyncResp->asyncResp->res); - BMCWEB_LOG_ERROR("getInventoryLeds respHandler DBus error {}", ec); - return; - } + const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { + // Response handler for parsing output from GetSubTree + BMCWEB_LOG_DEBUG("getInventoryLeds respHandler enter"); + if (ec) + { + messages::internalError(sensorsAsyncResp->asyncResp->res); + BMCWEB_LOG_ERROR("getInventoryLeds respHandler DBus error {}", + ec); + return; + } - // Build map of LED object paths to connections - std::shared_ptr<std::map<std::string, std::string>> ledConnections = - std::make_shared<std::map<std::string, std::string>>(); + // Build map of LED object paths to connections + std::shared_ptr<std::map<std::string, std::string>> ledConnections = + std::make_shared<std::map<std::string, std::string>>(); - // Loop through objects from GetSubTree - for (const std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>& - object : subtree) - { - // Check if object path is LED for one of the specified inventory - // items - const std::string& ledPath = object.first; - if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr) + // Loop through objects from GetSubTree + for (const std::pair<std::string, + std::vector<std::pair< + std::string, std::vector<std::string>>>>& + object : subtree) { - // Add mapping from ledPath to connection - const std::string& connection = object.second.begin()->first; - (*ledConnections)[ledPath] = connection; - BMCWEB_LOG_DEBUG("Added mapping {} -> {}", ledPath, connection); + // Check if object path is LED for one of the specified + // inventory items + const std::string& ledPath = object.first; + if (findInventoryItemForLed(*inventoryItems, ledPath) != + nullptr) + { + // Add mapping from ledPath to connection + const std::string& connection = + object.second.begin()->first; + (*ledConnections)[ledPath] = connection; + BMCWEB_LOG_DEBUG("Added mapping {} -> {}", ledPath, + connection); + } } - } - getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections, - std::move(callback)); - BMCWEB_LOG_DEBUG("getInventoryLeds respHandler exit"); - }); + getInventoryLedData(sensorsAsyncResp, inventoryItems, + ledConnections, std::move(callback)); + BMCWEB_LOG_DEBUG("getInventoryLeds respHandler exit"); + }); BMCWEB_LOG_DEBUG("getInventoryLeds exit"); } @@ -1988,7 +1517,7 @@ void getPowerSupplyAttributesData( auto respHandler = [sensorsAsyncResp, inventoryItems, callback = std::forward<Callback>(callback)]( const boost::system::error_code& ec, - const uint32_t value) { + uint32_t value) mutable { BMCWEB_LOG_DEBUG("getPowerSupplyAttributesData respHandler enter"); if (ec) { @@ -2055,7 +1584,7 @@ void getPowerSupplyAttributes( BMCWEB_LOG_DEBUG("getPowerSupplyAttributes enter"); // Only need the power supply attributes when the Power Schema - if (sensorsAsyncResp->chassisSubNode != sensors::node::power) + if (sensorsAsyncResp->chassisSubNode != sensors::powerNodeStr) { BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit since not Power"); callback(inventoryItems); @@ -2071,54 +1600,54 @@ void getPowerSupplyAttributes( [callback = std::forward<Callback>(callback), sensorsAsyncResp, inventoryItems]( const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) { - // Response handler for parsing output from GetSubTree - BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler enter"); - if (ec) - { - messages::internalError(sensorsAsyncResp->asyncResp->res); - BMCWEB_LOG_ERROR( - "getPowerSupplyAttributes respHandler DBus error {}", ec); - return; - } - if (subtree.empty()) - { - BMCWEB_LOG_DEBUG("Can't find Power Supply Attributes!"); - callback(inventoryItems); - return; - } + const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { + // Response handler for parsing output from GetSubTree + BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler enter"); + if (ec) + { + messages::internalError(sensorsAsyncResp->asyncResp->res); + BMCWEB_LOG_ERROR( + "getPowerSupplyAttributes respHandler DBus error {}", ec); + return; + } + if (subtree.empty()) + { + BMCWEB_LOG_DEBUG("Can't find Power Supply Attributes!"); + callback(inventoryItems); + return; + } - // Currently we only support 1 power supply attribute, use this for - // all the power supplies. Build map of object path to connection. - // Assume just 1 connection and 1 path for now. - std::map<std::string, std::string> psAttributesConnections; + // Currently we only support 1 power supply attribute, use this for + // all the power supplies. Build map of object path to connection. + // Assume just 1 connection and 1 path for now. + std::map<std::string, std::string> psAttributesConnections; - if (subtree[0].first.empty() || subtree[0].second.empty()) - { - BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!"); - callback(inventoryItems); - return; - } + if (subtree[0].first.empty() || subtree[0].second.empty()) + { + BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!"); + callback(inventoryItems); + return; + } - const std::string& psAttributesPath = subtree[0].first; - const std::string& connection = subtree[0].second.begin()->first; + const std::string& psAttributesPath = subtree[0].first; + const std::string& connection = subtree[0].second.begin()->first; - if (connection.empty()) - { - BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!"); - callback(inventoryItems); - return; - } + if (connection.empty()) + { + BMCWEB_LOG_DEBUG("Power Supply Attributes mapper error!"); + callback(inventoryItems); + return; + } - psAttributesConnections[psAttributesPath] = connection; - BMCWEB_LOG_DEBUG("Added mapping {} -> {}", psAttributesPath, - connection); + psAttributesConnections[psAttributesPath] = connection; + BMCWEB_LOG_DEBUG("Added mapping {} -> {}", psAttributesPath, + connection); - getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems, - psAttributesConnections, - std::move(callback)); - BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler exit"); - }); + getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems, + psAttributesConnections, + std::move(callback)); + BMCWEB_LOG_DEBUG("getPowerSupplyAttributes respHandler exit"); + }); BMCWEB_LOG_DEBUG("getPowerSupplyAttributes exit"); } @@ -2145,7 +1674,7 @@ void getPowerSupplyAttributes( * @param callback Callback to invoke when inventory items have been obtained. */ template <typename Callback> -static void +inline void getInventoryItems(std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, const std::shared_ptr<std::set<std::string>> sensorNames, Callback&& callback) @@ -2153,45 +1682,55 @@ static void BMCWEB_LOG_DEBUG("getInventoryItems enter"); auto getInventoryItemAssociationsCb = [sensorsAsyncResp, callback = std::forward<Callback>(callback)]( - std::shared_ptr<std::vector<InventoryItem>> inventoryItems) { - BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb enter"); - auto getInventoryItemsConnectionsCb = - [sensorsAsyncResp, inventoryItems, - callback = std::forward<const Callback>(callback)]( - std::shared_ptr<std::set<std::string>> invConnections) { - BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb enter"); - auto getInventoryItemsDataCb = [sensorsAsyncResp, inventoryItems, - callback{std::move(callback)}]() { - BMCWEB_LOG_DEBUG("getInventoryItemsDataCb enter"); - - auto getInventoryLedsCb = [sensorsAsyncResp, inventoryItems, - callback{std::move(callback)}]() { - BMCWEB_LOG_DEBUG("getInventoryLedsCb enter"); - // Find Power Supply Attributes and get the data - getPowerSupplyAttributes(sensorsAsyncResp, inventoryItems, - std::move(callback)); - BMCWEB_LOG_DEBUG("getInventoryLedsCb exit"); + std::shared_ptr<std::vector<InventoryItem>> + inventoryItems) mutable { + BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb enter"); + auto getInventoryItemsConnectionsCb = + [sensorsAsyncResp, inventoryItems, + callback = std::forward<Callback>(callback)]( + std::shared_ptr<std::set<std::string>> + invConnections) mutable { + BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb enter"); + auto getInventoryItemsDataCb = + [sensorsAsyncResp, inventoryItems, + callback = + std::forward<Callback>(callback)]() mutable { + BMCWEB_LOG_DEBUG("getInventoryItemsDataCb enter"); + + auto getInventoryLedsCb = + [sensorsAsyncResp, inventoryItems, + callback = std::forward<Callback>( + callback)]() mutable { + BMCWEB_LOG_DEBUG( + "getInventoryLedsCb enter"); + // Find Power Supply Attributes and get the + // data + getPowerSupplyAttributes( + sensorsAsyncResp, inventoryItems, + std::move(callback)); + BMCWEB_LOG_DEBUG("getInventoryLedsCb exit"); + }; + + // Find led connections and get the data + getInventoryLeds(sensorsAsyncResp, inventoryItems, + std::move(getInventoryLedsCb)); + BMCWEB_LOG_DEBUG("getInventoryItemsDataCb exit"); + }; + + // Get inventory item data from connections + getInventoryItemsData(sensorsAsyncResp, inventoryItems, + invConnections, + std::move(getInventoryItemsDataCb)); + BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb exit"); }; - // Find led connections and get the data - getInventoryLeds(sensorsAsyncResp, inventoryItems, - std::move(getInventoryLedsCb)); - BMCWEB_LOG_DEBUG("getInventoryItemsDataCb exit"); - }; - - // Get inventory item data from connections - getInventoryItemsData(sensorsAsyncResp, inventoryItems, - invConnections, - std::move(getInventoryItemsDataCb)); - BMCWEB_LOG_DEBUG("getInventoryItemsConnectionsCb exit"); + // Get connections that provide inventory item data + getInventoryItemsConnections( + sensorsAsyncResp, inventoryItems, + std::move(getInventoryItemsConnectionsCb)); + BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb exit"); }; - // Get connections that provide inventory item data - getInventoryItemsConnections(sensorsAsyncResp, inventoryItems, - std::move(getInventoryItemsConnectionsCb)); - BMCWEB_LOG_DEBUG("getInventoryItemAssociationsCb exit"); - }; - // Get associations from sensors to inventory items getInventoryItemAssociations(sensorsAsyncResp, sensorNames, std::move(getInventoryItemAssociationsCb)); @@ -2243,8 +1782,8 @@ inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray, // Add new PowerSupply object to JSON array powerSupplyArray.push_back({}); nlohmann::json& powerSupply = powerSupplyArray.back(); - boost::urls::url url = boost::urls::format("/redfish/v1/Chassis/{}/Power", - chassisId); + boost::urls::url url = + boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId); url.set_fragment(("/PowerSupplies"_json_pointer).to_string()); powerSupply["@odata.id"] = std::move(url); std::string escaped; @@ -2255,7 +1794,7 @@ inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray, powerSupply["Model"] = inventoryItem.model; powerSupply["PartNumber"] = inventoryItem.partNumber; powerSupply["SerialNumber"] = inventoryItem.serialNumber; - setLedState(powerSupply, &inventoryItem); + sensor_utils::setLedState(powerSupply, &inventoryItem); if (inventoryItem.powerSupplyEfficiencyPercent >= 0) { @@ -2263,7 +1802,8 @@ inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray, inventoryItem.powerSupplyEfficiencyPercent; } - powerSupply["Status"]["State"] = getState(&inventoryItem, true); + powerSupply["Status"]["State"] = + sensor_utils::getState(&inventoryItem, true); const char* health = inventoryItem.isFunctional ? "OK" : "Critical"; powerSupply["Status"]["Health"] = health; @@ -2312,215 +1852,212 @@ inline void getSensorData( [sensorsAsyncResp, sensorNames, inventoryItems](const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& resp) { - BMCWEB_LOG_DEBUG("getManagedObjectsCb enter"); - if (ec) - { - BMCWEB_LOG_ERROR("getManagedObjectsCb DBUS error: {}", ec); - messages::internalError(sensorsAsyncResp->asyncResp->res); - 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); - // NOLINTNEXTLINE - bmcweb::split(split, objPath, '/'); - if (split.size() < 6) + BMCWEB_LOG_DEBUG("getManagedObjectsCb enter"); + if (ec) { - BMCWEB_LOG_ERROR("Got path that isn't long enough {}", - objPath); - continue; + BMCWEB_LOG_ERROR("getManagedObjectsCb DBUS error: {}", ec); + messages::internalError(sensorsAsyncResp->asyncResp->res); + return; } - // These indexes aren't intuitive, as split puts an empty - // string at the beginning - const std::string& sensorType = split[4]; - const std::string& sensorName = split[5]; - BMCWEB_LOG_DEBUG("sensorName {} sensorType {}", sensorName, - sensorType); - if (sensorNames->find(objPath) == sensorNames->end()) + auto chassisSubNode = sensor_utils::chassisSubNodeFromString( + sensorsAsyncResp->chassisSubNode); + // Go through all objects and update response with sensor data + for (const auto& objDictEntry : resp) { - BMCWEB_LOG_DEBUG("{} not in sensor list ", sensorName); - continue; - } - - // Find inventory item (if any) associated with sensor - InventoryItem* inventoryItem = - findInventoryItemForSensor(inventoryItems, objPath); - - const std::string& sensorSchema = - sensorsAsyncResp->chassisSubNode; - - nlohmann::json* sensorJson = nullptr; + const std::string& objPath = + static_cast<const std::string&>(objDictEntry.first); + BMCWEB_LOG_DEBUG("getManagedObjectsCb parsing object {}", + objPath); - if (sensorSchema == sensors::node::sensors && - !sensorsAsyncResp->efficientExpand) - { - std::string sensorTypeEscaped(sensorType); - auto remove = std::ranges::remove(sensorTypeEscaped, '_'); - - sensorTypeEscaped.erase(std::ranges::begin(remove), - sensorTypeEscaped.end()); - std::string sensorId(sensorTypeEscaped); - sensorId += "_"; - sensorId += sensorName; - - sensorsAsyncResp->asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Chassis/{}/{}/{}", - sensorsAsyncResp->chassisId, - sensorsAsyncResp->chassisSubNode, - sensorId); - sensorJson = &(sensorsAsyncResp->asyncResp->res.jsonValue); - } - else - { - std::string fieldName; - if (sensorsAsyncResp->efficientExpand) + std::vector<std::string> split; + // Reserve space for + // /xyz/openbmc_project/sensors/<name>/<subname> + split.reserve(6); + // NOLINTNEXTLINE + bmcweb::split(split, objPath, '/'); + if (split.size() < 6) { - fieldName = "Members"; - } - else if (sensorType == "temperature") - { - fieldName = "Temperatures"; + BMCWEB_LOG_ERROR("Got path that isn't long enough {}", + objPath); + continue; } - else if (sensorType == "fan" || sensorType == "fan_tach" || - sensorType == "fan_pwm") + // These indexes aren't intuitive, as split puts an empty + // string at the beginning + const std::string& sensorType = split[4]; + const std::string& sensorName = split[5]; + BMCWEB_LOG_DEBUG("sensorName {} sensorType {}", sensorName, + sensorType); + if (sensorNames->find(objPath) == sensorNames->end()) { - fieldName = "Fans"; + BMCWEB_LOG_DEBUG("{} not in sensor list ", sensorName); + continue; } - else if (sensorType == "voltage") + + // Find inventory item (if any) associated with sensor + InventoryItem* inventoryItem = + findInventoryItemForSensor(inventoryItems, objPath); + + const std::string& sensorSchema = + sensorsAsyncResp->chassisSubNode; + + nlohmann::json* sensorJson = nullptr; + + if (sensorSchema == sensors::sensorsNodeStr && + !sensorsAsyncResp->efficientExpand) { - fieldName = "Voltages"; + std::string sensorId = + redfish::sensor_utils::getSensorId(sensorName, + sensorType); + + sensorsAsyncResp->asyncResp->res + .jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}/{}/{}", + sensorsAsyncResp->chassisId, + sensorsAsyncResp->chassisSubNode, sensorId); + sensorJson = + &(sensorsAsyncResp->asyncResp->res.jsonValue); } - else if (sensorType == "power") + else { - if (sensorName == "total_power") + std::string fieldName; + if (sensorsAsyncResp->efficientExpand) + { + fieldName = "Members"; + } + else if (sensorType == "temperature") { - fieldName = "PowerControl"; + fieldName = "Temperatures"; } - else if ((inventoryItem != nullptr) && - (inventoryItem->isPowerSupply)) + else if (sensorType == "fan" || + sensorType == "fan_tach" || + sensorType == "fan_pwm") { - fieldName = "PowerSupplies"; + fieldName = "Fans"; + } + else if (sensorType == "voltage") + { + fieldName = "Voltages"; + } + else if (sensorType == "power") + { + if (sensorName == "total_power") + { + fieldName = "PowerControl"; + } + else if ((inventoryItem != nullptr) && + (inventoryItem->isPowerSupply)) + { + fieldName = "PowerSupplies"; + } + else + { + // Other power sensors are in SensorCollection + continue; + } } else { - // Other power sensors are in SensorCollection + BMCWEB_LOG_ERROR( + "Unsure how to handle sensorType {}", + sensorType); continue; } - } - else - { - BMCWEB_LOG_ERROR("Unsure how to handle sensorType {}", - sensorType); - continue; - } - nlohmann::json& tempArray = - sensorsAsyncResp->asyncResp->res.jsonValue[fieldName]; - if (fieldName == "PowerControl") - { - if (tempArray.empty()) + nlohmann::json& tempArray = + sensorsAsyncResp->asyncResp->res + .jsonValue[fieldName]; + if (fieldName == "PowerControl") + { + if (tempArray.empty()) + { + // Put multiple "sensors" into a single + // PowerControl. Follows MemberId naming and + // naming in power.hpp. + nlohmann::json::object_t power; + boost::urls::url url = boost::urls::format( + "/redfish/v1/Chassis/{}/{}", + sensorsAsyncResp->chassisId, + sensorsAsyncResp->chassisSubNode); + url.set_fragment( + (""_json_pointer / fieldName / "0") + .to_string()); + power["@odata.id"] = std::move(url); + tempArray.emplace_back(std::move(power)); + } + sensorJson = &(tempArray.back()); + } + else if (fieldName == "PowerSupplies") + { + if (inventoryItem != nullptr) + { + sensorJson = &(getPowerSupply( + tempArray, *inventoryItem, + sensorsAsyncResp->chassisId)); + } + } + else if (fieldName == "Members") + { + std::string sensorId = + redfish::sensor_utils::getSensorId(sensorName, + sensorType); + + nlohmann::json::object_t member; + member["@odata.id"] = boost::urls::format( + "/redfish/v1/Chassis/{}/{}/{}", + sensorsAsyncResp->chassisId, + sensorsAsyncResp->chassisSubNode, sensorId); + tempArray.emplace_back(std::move(member)); + sensorJson = &(tempArray.back()); + } + else { - // Put multiple "sensors" into a single - // PowerControl. Follows MemberId naming and - // naming in power.hpp. - nlohmann::json::object_t power; + nlohmann::json::object_t member; boost::urls::url url = boost::urls::format( "/redfish/v1/Chassis/{}/{}", sensorsAsyncResp->chassisId, sensorsAsyncResp->chassisSubNode); - url.set_fragment((""_json_pointer / fieldName / "0") - .to_string()); - power["@odata.id"] = std::move(url); - tempArray.emplace_back(std::move(power)); + url.set_fragment( + (""_json_pointer / fieldName).to_string()); + member["@odata.id"] = std::move(url); + tempArray.emplace_back(std::move(member)); + sensorJson = &(tempArray.back()); } - sensorJson = &(tempArray.back()); } - else if (fieldName == "PowerSupplies") + + if (sensorJson != nullptr) { - if (inventoryItem != nullptr) - { - sensorJson = - &(getPowerSupply(tempArray, *inventoryItem, - sensorsAsyncResp->chassisId)); - } + objectInterfacesToJson( + sensorName, sensorType, chassisSubNode, + objDictEntry.second, *sensorJson, inventoryItem); + + std::string path = "/xyz/openbmc_project/sensors/"; + path += sensorType; + path += "/"; + path += sensorName; + sensorsAsyncResp->addMetadata(*sensorJson, path); } - else if (fieldName == "Members") + } + if (sensorsAsyncResp.use_count() == 1) + { + sortJSONResponse(sensorsAsyncResp); + if (chassisSubNode == + sensor_utils::ChassisSubNode::sensorsNode && + sensorsAsyncResp->efficientExpand) { - std::string sensorTypeEscaped(sensorType); - auto remove = std::ranges::remove(sensorTypeEscaped, - '_'); - sensorTypeEscaped.erase(std::ranges::begin(remove), - sensorTypeEscaped.end()); - std::string sensorId(sensorTypeEscaped); - sensorId += "_"; - sensorId += sensorName; - - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/Chassis/{}/{}/{}", - sensorsAsyncResp->chassisId, - sensorsAsyncResp->chassisSubNode, sensorId); - tempArray.emplace_back(std::move(member)); - sensorJson = &(tempArray.back()); + sensorsAsyncResp->asyncResp->res + .jsonValue["Members@odata.count"] = + sensorsAsyncResp->asyncResp->res + .jsonValue["Members"] + .size(); } - else + else if (chassisSubNode == + sensor_utils::ChassisSubNode::thermalNode) { - nlohmann::json::object_t member; - boost::urls::url url = boost::urls::format( - "/redfish/v1/Chassis/{}/{}", - sensorsAsyncResp->chassisId, - sensorsAsyncResp->chassisSubNode); - url.set_fragment( - (""_json_pointer / fieldName).to_string()); - member["@odata.id"] = std::move(url); - tempArray.emplace_back(std::move(member)); - sensorJson = &(tempArray.back()); + populateFanRedundancy(sensorsAsyncResp); } } - - if (sensorJson != nullptr) - { - objectInterfacesToJson(sensorName, sensorType, - sensorsAsyncResp->chassisSubNode, - objDictEntry.second, *sensorJson, - inventoryItem); - - std::string path = "/xyz/openbmc_project/sensors/"; - path += sensorType; - path += "/"; - path += sensorName; - sensorsAsyncResp->addMetadata(*sensorJson, path); - } - } - if (sensorsAsyncResp.use_count() == 1) - { - sortJSONResponse(sensorsAsyncResp); - if (sensorsAsyncResp->chassisSubNode == - sensors::node::sensors && - sensorsAsyncResp->efficientExpand) - { - sensorsAsyncResp->asyncResp->res - .jsonValue["Members@odata.count"] = - sensorsAsyncResp->asyncResp->res.jsonValue["Members"] - .size(); - } - else if (sensorsAsyncResp->chassisSubNode == - sensors::node::thermal) - { - populateFanRedundancy(sensorsAsyncResp); - } - } - BMCWEB_LOG_DEBUG("getManagedObjectsCb exit"); - }); + BMCWEB_LOG_DEBUG("getManagedObjectsCb exit"); + }); } BMCWEB_LOG_DEBUG("getSensorData exit"); } @@ -2533,15 +2070,15 @@ inline void const std::set<std::string>& connections) { BMCWEB_LOG_DEBUG("getConnectionCb enter"); auto getInventoryItemsCb = - [sensorsAsyncResp, sensorNames, - connections](const std::shared_ptr<std::vector<InventoryItem>>& - inventoryItems) { - BMCWEB_LOG_DEBUG("getInventoryItemsCb enter"); - // Get sensor data and store results in JSON - getSensorData(sensorsAsyncResp, sensorNames, connections, - inventoryItems); - BMCWEB_LOG_DEBUG("getInventoryItemsCb exit"); - }; + [sensorsAsyncResp, sensorNames, connections]( + const std::shared_ptr<std::vector<InventoryItem>>& + inventoryItems) mutable { + BMCWEB_LOG_DEBUG("getInventoryItemsCb enter"); + // Get sensor data and store results in JSON + getSensorData(sensorsAsyncResp, sensorNames, connections, + inventoryItems); + BMCWEB_LOG_DEBUG("getInventoryItemsCb exit"); + }; // Get inventory items associated with sensors getInventoryItems(sensorsAsyncResp, sensorNames, @@ -2566,12 +2103,12 @@ inline void auto getChassisCb = [sensorsAsyncResp]( const std::shared_ptr<std::set<std::string>>& sensorNames) { - BMCWEB_LOG_DEBUG("getChassisCb enter"); - processSensorList(sensorsAsyncResp, sensorNames); - BMCWEB_LOG_DEBUG("getChassisCb exit"); - }; + BMCWEB_LOG_DEBUG("getChassisCb enter"); + processSensorList(sensorsAsyncResp, sensorNames); + BMCWEB_LOG_DEBUG("getChassisCb exit"); + }; // SensorCollection doesn't contain the Redundancy property - if (sensorsAsyncResp->chassisSubNode != sensors::node::sensors) + if (sensorsAsyncResp->chassisSubNode != sensors::sensorsNodeStr) { sensorsAsyncResp->asyncResp->res.jsonValue["Redundancy"] = nlohmann::json::array(); @@ -2592,10 +2129,9 @@ inline void * @param sensorsModified The list of sensors that were found as a result of * repeated calls to this function */ -inline bool - findSensorNameUsingSensorPath(std::string_view sensorName, - const std::set<std::string>& sensorsList, - std::set<std::string>& sensorsModified) +inline bool findSensorNameUsingSensorPath( + std::string_view sensorName, const std::set<std::string>& sensorsList, + std::set<std::string>& sensorsModified) { for (const auto& chassisSensor : sensorsList) { @@ -2614,24 +2150,6 @@ inline bool return false; } -inline std::pair<std::string, std::string> - splitSensorNameAndType(std::string_view sensorId) -{ - size_t index = sensorId.find('_'); - if (index == std::string::npos) - { - return std::make_pair<std::string, std::string>("", ""); - } - std::string sensorType{sensorId.substr(0, index)}; - std::string sensorName{sensorId.substr(index + 1)}; - // fan_pwm and fan_tach need special handling - if (sensorType == "fantach" || sensorType == "fanpwm") - { - sensorType.insert(3, 1, '_'); - } - return std::make_pair(sensorType, sensorName); -} - /** * @brief Entry point for overriding sensor values of given sensor * @@ -2678,10 +2196,11 @@ inline void setSensorsOverride( } } - auto getChassisSensorListCb = - [sensorAsyncResp, overrideMap, - propertyValueNameStr = std::string(propertyValueName)]( - const std::shared_ptr<std::set<std::string>>& sensorsList) { + auto getChassisSensorListCb = [sensorAsyncResp, overrideMap, + propertyValueNameStr = + std::string(propertyValueName)]( + const std::shared_ptr< + std::set<std::string>>& sensorsList) { // Match sensor names in the PATCH request to those managed by the // chassis node const std::shared_ptr<std::set<std::string>> sensorNames = @@ -2690,7 +2209,7 @@ inline void setSensorsOverride( { const auto& sensor = item.first; std::pair<std::string, std::string> sensorNameType = - splitSensorNameAndType(sensor); + redfish::sensor_utils::splitSensorNameAndType(sensor); if (!findSensorNameUsingSensorPath(sensorNameType.second, *sensorsList, *sensorNames)) { @@ -2701,22 +2220,24 @@ inline void setSensorsOverride( } } // Get the connection to which the memberId belongs - auto getObjectsWithConnectionCb = - [sensorAsyncResp, overrideMap, propertyValueNameStr]( - const std::set<std::string>& /*connections*/, - const std::set<std::pair<std::string, std::string>>& - objectsWithConnection) { + auto getObjectsWithConnectionCb = [sensorAsyncResp, overrideMap, + propertyValueNameStr]( + const std::set< + std::string>& /*connections*/, + const std::set<std::pair< + std::string, std::string>>& + objectsWithConnection) { if (objectsWithConnection.size() != overrideMap.size()) { BMCWEB_LOG_INFO( "Unable to find all objects with proper connection {} requested {}", objectsWithConnection.size(), overrideMap.size()); - messages::resourceNotFound(sensorAsyncResp->asyncResp->res, - sensorAsyncResp->chassisSubNode == - sensors::node::thermal - ? "Temperatures" - : "Voltages", - "Count"); + messages::resourceNotFound( + sensorAsyncResp->asyncResp->res, + sensorAsyncResp->chassisSubNode == sensors::thermalNodeStr + ? "Temperatures" + : "Voltages", + "Count"); return; } for (const auto& item : objectsWithConnection) @@ -2728,11 +2249,8 @@ inline void setSensorsOverride( messages::internalError(sensorAsyncResp->asyncResp->res); return; } - std::string id = path.parent_path().filename(); - auto remove = std::ranges::remove(id, '_'); - id.erase(std::ranges::begin(remove), id.end()); - id += "_"; - id += sensorName; + std::string id = redfish::sensor_utils::getSensorId( + sensorName, path.parent_path().filename()); const auto& iterator = overrideMap.find(id); if (iterator == overrideMap.end()) @@ -2767,13 +2285,13 @@ inline void setSensorsOverride( * it to caller in a callback. * * @param chassis Chassis for which retrieval should be performed - * @param node Node (group) of sensors. See sensors::node for supported values + * @param node Node (group) of sensors. See sensor_utils::node for supported + * values * @param mapComplete Callback to be called with retrieval result */ template <typename Callback> -inline void retrieveUriToDbusMap(const std::string& chassis, - const std::string& node, - Callback&& mapComplete) +inline void retrieveUriToDbusMap( + const std::string& chassis, const std::string& node, Callback&& mapComplete) { decltype(sensors::paths)::const_iterator pathIt = std::find_if(sensors::paths.cbegin(), sensors::paths.cend(), @@ -2787,12 +2305,12 @@ inline void retrieveUriToDbusMap(const std::string& chassis, } auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); - auto callback = [asyncResp, - mapCompleteCb = std::forward<Callback>(mapComplete)]( - const boost::beast::http::status status, - const std::map<std::string, std::string>& uriToDbus) { - mapCompleteCb(status, uriToDbus); - }; + auto callback = + [asyncResp, mapCompleteCb = std::forward<Callback>(mapComplete)]( + const boost::beast::http::status status, + const std::map<std::string, std::string>& uriToDbus) { + mapCompleteCb(status, uriToDbus); + }; auto resp = std::make_shared<SensorsAsyncResp>( asyncResp, chassis, pathIt->second, node, std::move(callback)); @@ -2823,15 +2341,9 @@ inline void getChassisCallback( return; } std::string type = path.parent_path().filename(); - // fan_tach has an underscore in it, so remove it to "normalize" the - // type in the URI - auto remove = std::ranges::remove(type, '_'); - type.erase(std::ranges::begin(remove), type.end()); + std::string id = redfish::sensor_utils::getSensorId(sensorName, type); nlohmann::json::object_t member; - std::string id = type; - id += "_"; - id += sensorName; member["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/{}/{}", chassisId, chassisSubNode, id); @@ -2862,7 +2374,7 @@ inline void handleSensorCollectionGet( // we perform efficient expand. auto sensorsAsyncResp = std::make_shared<SensorsAsyncResp>( asyncResp, chassisId, sensors::dbus::sensorPaths, - sensors::node::sensors, + sensors::sensorsNodeStr, /*efficientExpand=*/true); getChassisData(sensorsAsyncResp); @@ -2873,9 +2385,9 @@ inline void handleSensorCollectionGet( // We get all sensors as hyperlinkes in the chassis (this // implies we reply on the default query parameters handler) - getChassis(asyncResp, chassisId, sensors::node::sensors, dbus::sensorPaths, + getChassis(asyncResp, chassisId, sensors::sensorsNodeStr, dbus::sensorPaths, std::bind_front(sensors::getChassisCallback, asyncResp, - chassisId, sensors::node::sensors)); + chassisId, sensors::sensorsNodeStr)); } inline void @@ -2898,18 +2410,19 @@ inline void [asyncResp, sensorPath](const boost::system::error_code& ec, const ::dbus::utility::DBusPropertiesMap& valuesDict) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - sdbusplus::message::object_path path(sensorPath); - std::string name = path.filename(); - path = path.parent_path(); - std::string type = path.filename(); - objectPropertiesToJson(name, type, sensors::node::sensors, valuesDict, - asyncResp->res.jsonValue, nullptr); - }); + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + sdbusplus::message::object_path path(sensorPath); + std::string name = path.filename(); + path = path.parent_path(); + std::string type = path.filename(); + sensor_utils::objectPropertiesToJson( + name, type, sensor_utils::ChassisSubNode::sensorsNode, + valuesDict, asyncResp->res.jsonValue, nullptr); + }); } inline void handleSensorGet(App& app, const crow::Request& req, @@ -2922,7 +2435,7 @@ inline void handleSensorGet(App& app, const crow::Request& req, return; } std::pair<std::string, std::string> nameType = - splitSensorNameAndType(sensorId); + redfish::sensor_utils::splitSensorNameAndType(sensorId); if (nameType.first.empty() || nameType.second.empty()) { messages::resourceNotFound(asyncResp->res, sensorId, "Sensor"); @@ -2945,23 +2458,23 @@ inline void handleSensorGet(App& app, const crow::Request& req, [asyncResp, sensorId, sensorPath](const boost::system::error_code& ec, const ::dbus::utility::MapperGetObject& subtree) { - BMCWEB_LOG_DEBUG("respHandler1 enter"); - if (ec == boost::system::errc::io_error) - { - BMCWEB_LOG_WARNING("Sensor not found from getSensorPaths"); - messages::resourceNotFound(asyncResp->res, sensorId, "Sensor"); - return; - } - if (ec) - { - messages::internalError(asyncResp->res); - BMCWEB_LOG_ERROR( - "Sensor getSensorPaths resp_handler: Dbus error {}", ec); - return; - } - getSensorFromDbus(asyncResp, sensorPath, subtree); - BMCWEB_LOG_DEBUG("respHandler1 exit"); - }); + BMCWEB_LOG_DEBUG("respHandler1 enter"); + if (ec == boost::system::errc::io_error) + { + BMCWEB_LOG_WARNING("Sensor not found from getSensorPaths"); + messages::resourceNotFound(asyncResp->res, sensorId, "Sensor"); + return; + } + if (ec) + { + messages::internalError(asyncResp->res); + BMCWEB_LOG_ERROR( + "Sensor getSensorPaths resp_handler: Dbus error {}", ec); + return; + } + getSensorFromDbus(asyncResp, sensorPath, subtree); + BMCWEB_LOG_DEBUG("respHandler1 exit"); + }); } } // namespace sensors diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp index 57b967c427..ca640749ed 100644 --- a/redfish-core/lib/service_root.hpp +++ b/redfish-core/lib/service_root.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once diff --git a/redfish-core/lib/storage.hpp b/redfish-core/lib/storage.hpp index e0ad737752..e670de72e3 100644 --- a/redfish-core/lib/storage.hpp +++ b/redfish-core/lib/storage.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2019 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2019 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -21,6 +21,7 @@ #include "dbus_utility.hpp" #include "generated/enums/drive.hpp" #include "generated/enums/protocol.hpp" +#include "generated/enums/resource.hpp" #include "human_sort.hpp" #include "query.hpp" #include "redfish_util.hpp" @@ -162,9 +163,9 @@ inline void afterSystemsStorageGetSubtree( subtree, [&storageId](const std::pair<std::string, dbus::utility::MapperServiceMap>& object) { - return sdbusplus::message::object_path(object.first).filename() == - storageId; - }); + return sdbusplus::message::object_path(object.first).filename() == + storageId; + }); if (storage == subtree.end()) { messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage", @@ -178,7 +179,7 @@ inline void afterSystemsStorageGetSubtree( BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId); asyncResp->res.jsonValue["Name"] = "Storage"; asyncResp->res.jsonValue["Id"] = storageId; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; getDrives(asyncResp); asyncResp->res.jsonValue["Controllers"]["@odata.id"] = @@ -186,11 +187,10 @@ inline void afterSystemsStorageGetSubtree( BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId); } -inline void - handleSystemsStorageGet(App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& systemName, - const std::string& storageId) +inline void handleSystemsStorageGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& storageId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -227,9 +227,9 @@ inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, subtree, [&storageId](const std::pair<std::string, dbus::utility::MapperServiceMap>& object) { - return sdbusplus::message::object_path(object.first).filename() == - storageId; - }); + return sdbusplus::message::object_path(object.first).filename() == + storageId; + }); if (storage == subtree.end()) { messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage", @@ -242,7 +242,7 @@ inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, boost::urls::format("/redfish/v1/Storage/{}", storageId); asyncResp->res.jsonValue["Name"] = "Storage"; asyncResp->res.jsonValue["Id"] = storageId; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; // Storage subsystem to Storage link. nlohmann::json::array_t storageServices; @@ -298,48 +298,48 @@ inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::vector< std::pair<std::string, dbus::utility::DbusVariantType>>& propertiesList) { - if (ec) - { - // this interface isn't necessary - return; - } + if (ec) + { + // this interface isn't necessary + return; + } - const std::string* partNumber = nullptr; - const std::string* serialNumber = nullptr; - const std::string* manufacturer = nullptr; - const std::string* model = nullptr; + const std::string* partNumber = nullptr; + const std::string* serialNumber = nullptr; + const std::string* manufacturer = nullptr; + const std::string* model = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", - partNumber, "SerialNumber", serialNumber, "Manufacturer", - manufacturer, "Model", model); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", + partNumber, "SerialNumber", serialNumber, "Manufacturer", + manufacturer, "Model", model); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (partNumber != nullptr) - { - asyncResp->res.jsonValue["PartNumber"] = *partNumber; - } + if (partNumber != nullptr) + { + asyncResp->res.jsonValue["PartNumber"] = *partNumber; + } - if (serialNumber != nullptr) - { - asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; - } + if (serialNumber != nullptr) + { + asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; + } - if (manufacturer != nullptr) - { - asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; - } + if (manufacturer != nullptr) + { + asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; + } - if (model != nullptr) - { - asyncResp->res.jsonValue["Model"] = *model; - } - }); + if (model != nullptr) + { + asyncResp->res.jsonValue["Model"] = *model; + } + }); } inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -349,20 +349,21 @@ inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present", - [asyncResp, path](const boost::system::error_code& ec, - const bool isPresent) { - // this interface isn't necessary, only check it if - // we get a good return - if (ec) - { - return; - } + [asyncResp, + path](const boost::system::error_code& ec, const bool isPresent) { + // this interface isn't necessary, only check it if + // we get a good return + if (ec) + { + return; + } - if (!isPresent) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + if (!isPresent) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); } inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -373,22 +374,23 @@ inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, *crow::connections::systemBus, connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding", [asyncResp](const boost::system::error_code& ec, const bool updating) { - // this interface isn't necessary, only check it - // if we get a good return - if (ec) - { - return; - } + // this interface isn't necessary, only check it + // if we get a good return + if (ec) + { + return; + } - // updating and disabled in the backend shouldn't be - // able to be set at the same time, so we don't need - // to check for the race condition of these two - // calls - if (updating) - { - asyncResp->res.jsonValue["Status"]["State"] = "Updating"; - } - }); + // updating and disabled in the backend shouldn't be + // able to be set at the same time, so we don't need + // to check for the race condition of these two + // calls + if (updating) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Updating; + } + }); } inline std::optional<drive::MediaType> convertDriveType(std::string_view type) @@ -437,10 +439,9 @@ inline std::optional<protocol::Protocol> return protocol::Protocol::Invalid; } -inline void - getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& connectionName, - const std::string& path) +inline void getDriveItemProperties( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& connectionName, const std::string& path) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, connectionName, path, @@ -449,160 +450,162 @@ inline void const std::vector< std::pair<std::string, dbus::utility::DbusVariantType>>& propertiesList) { - if (ec) - { - // this interface isn't required - return; - } - const std::string* encryptionStatus = nullptr; - const bool* isLocked = nullptr; - for (const std::pair<std::string, dbus::utility::DbusVariantType>& - property : propertiesList) - { - const std::string& propertyName = property.first; - if (propertyName == "Type") + if (ec) { - const std::string* value = - std::get_if<std::string>(&property.second); - if (value == nullptr) - { - // illegal property - BMCWEB_LOG_ERROR("Illegal property: Type"); - messages::internalError(asyncResp->res); - return; - } - - std::optional<drive::MediaType> mediaType = - convertDriveType(*value); - if (!mediaType) - { - BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}", - *value); - continue; - } - if (*mediaType == drive::MediaType::Invalid) - { - messages::internalError(asyncResp->res); - return; - } - - asyncResp->res.jsonValue["MediaType"] = *mediaType; + // this interface isn't required + return; } - else if (propertyName == "Capacity") + const std::string* encryptionStatus = nullptr; + const bool* isLocked = nullptr; + for (const std::pair<std::string, dbus::utility::DbusVariantType>& + property : propertiesList) { - const uint64_t* capacity = - std::get_if<uint64_t>(&property.second); - if (capacity == nullptr) + const std::string& propertyName = property.first; + if (propertyName == "Type") { - BMCWEB_LOG_ERROR("Illegal property: Capacity"); - messages::internalError(asyncResp->res); - return; + const std::string* value = + std::get_if<std::string>(&property.second); + if (value == nullptr) + { + // illegal property + BMCWEB_LOG_ERROR("Illegal property: Type"); + messages::internalError(asyncResp->res); + return; + } + + std::optional<drive::MediaType> mediaType = + convertDriveType(*value); + if (!mediaType) + { + BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}", + *value); + continue; + } + if (*mediaType == drive::MediaType::Invalid) + { + messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.jsonValue["MediaType"] = *mediaType; } - if (*capacity == 0) + else if (propertyName == "Capacity") { - // drive capacity not known - continue; + const uint64_t* capacity = + std::get_if<uint64_t>(&property.second); + if (capacity == nullptr) + { + BMCWEB_LOG_ERROR("Illegal property: Capacity"); + messages::internalError(asyncResp->res); + return; + } + if (*capacity == 0) + { + // drive capacity not known + continue; + } + + asyncResp->res.jsonValue["CapacityBytes"] = *capacity; } - - asyncResp->res.jsonValue["CapacityBytes"] = *capacity; - } - else if (propertyName == "Protocol") - { - const std::string* value = - std::get_if<std::string>(&property.second); - if (value == nullptr) + else if (propertyName == "Protocol") { - BMCWEB_LOG_ERROR("Illegal property: Protocol"); - messages::internalError(asyncResp->res); - return; + const std::string* value = + std::get_if<std::string>(&property.second); + if (value == nullptr) + { + BMCWEB_LOG_ERROR("Illegal property: Protocol"); + messages::internalError(asyncResp->res); + return; + } + + std::optional<protocol::Protocol> proto = + convertDriveProtocol(*value); + if (!proto) + { + BMCWEB_LOG_WARNING( + "Unknown DrivePrototype Interface: {}", *value); + continue; + } + if (*proto == protocol::Protocol::Invalid) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["Protocol"] = *proto; } - - std::optional<protocol::Protocol> proto = - convertDriveProtocol(*value); - if (!proto) + else if (propertyName == "PredictedMediaLifeLeftPercent") { - BMCWEB_LOG_WARNING("Unknown DrivePrototype Interface: {}", - *value); - continue; - } - if (*proto == protocol::Protocol::Invalid) - { - messages::internalError(asyncResp->res); - return; + const uint8_t* lifeLeft = + std::get_if<uint8_t>(&property.second); + if (lifeLeft == nullptr) + { + BMCWEB_LOG_ERROR( + "Illegal property: PredictedMediaLifeLeftPercent"); + messages::internalError(asyncResp->res); + return; + } + // 255 means reading the value is not supported + if (*lifeLeft != 255) + { + asyncResp->res + .jsonValue["PredictedMediaLifeLeftPercent"] = + *lifeLeft; + } } - asyncResp->res.jsonValue["Protocol"] = *proto; - } - else if (propertyName == "PredictedMediaLifeLeftPercent") - { - const uint8_t* lifeLeft = - std::get_if<uint8_t>(&property.second); - if (lifeLeft == nullptr) + else if (propertyName == "EncryptionStatus") { - BMCWEB_LOG_ERROR( - "Illegal property: PredictedMediaLifeLeftPercent"); - messages::internalError(asyncResp->res); - return; + encryptionStatus = + std::get_if<std::string>(&property.second); + if (encryptionStatus == nullptr) + { + BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus"); + messages::internalError(asyncResp->res); + return; + } } - // 255 means reading the value is not supported - if (*lifeLeft != 255) + else if (propertyName == "Locked") { - asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] = - *lifeLeft; + isLocked = std::get_if<bool>(&property.second); + if (isLocked == nullptr) + { + BMCWEB_LOG_ERROR("Illegal property: Locked"); + messages::internalError(asyncResp->res); + return; + } } } - else if (propertyName == "EncryptionStatus") + + if (encryptionStatus == nullptr || isLocked == nullptr || + *encryptionStatus == + "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown") { - encryptionStatus = std::get_if<std::string>(&property.second); - if (encryptionStatus == nullptr) - { - BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus"); - messages::internalError(asyncResp->res); - return; - } + return; } - else if (propertyName == "Locked") + if (*encryptionStatus != + "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted") { - isLocked = std::get_if<bool>(&property.second); - if (isLocked == nullptr) - { - BMCWEB_LOG_ERROR("Illegal property: Locked"); - messages::internalError(asyncResp->res); - return; - } + //"The drive is not currently encrypted." + asyncResp->res.jsonValue["EncryptionStatus"] = + drive::EncryptionStatus::Unencrypted; + return; } - } - - if (encryptionStatus == nullptr || isLocked == nullptr || - *encryptionStatus == - "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown") - { - return; - } - if (*encryptionStatus != - "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted") - { - //"The drive is not currently encrypted." - asyncResp->res.jsonValue["EncryptionStatus"] = - drive::EncryptionStatus::Unencrypted; - return; - } - if (*isLocked) - { - //"The drive is currently encrypted and the data is not - // accessible to the user." + if (*isLocked) + { + //"The drive is currently encrypted and the data is not + // accessible to the user." + asyncResp->res.jsonValue["EncryptionStatus"] = + drive::EncryptionStatus::Locked; + return; + } + // if not locked + // "The drive is currently encrypted but the data is accessible + // to the user in unencrypted form." asyncResp->res.jsonValue["EncryptionStatus"] = - drive::EncryptionStatus::Locked; - return; - } - // if not locked - // "The drive is currently encrypted but the data is accessible - // to the user in unencrypted form." - asyncResp->res.jsonValue["EncryptionStatus"] = - drive::EncryptionStatus::Unlocked; - }); + drive::EncryptionStatus::Unlocked; + }); } -static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& connectionName, const std::string& path, const std::vector<std::string>& interfaces) @@ -644,9 +647,9 @@ inline void afterGetSubtreeSystemsStorageDrive( subtree, [&driveId](const std::pair<std::string, dbus::utility::MapperServiceMap>& object) { - return sdbusplus::message::object_path(object.first).filename() == - driveId; - }); + return sdbusplus::message::object_path(object.first).filename() == + driveId; + }); if (drive == subtree.end()) { @@ -672,15 +675,15 @@ inline void afterGetSubtreeSystemsStorageDrive( return; } - getMainChassisId(asyncResp, - [](const std::string& chassisId, - const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { - aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = - boost::urls::format("/redfish/v1/Chassis/{}", chassisId); - }); + getMainChassisId( + asyncResp, [](const std::string& chassisId, + const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { + aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}", chassisId); + }); // default it to Enabled - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; addAllDriveInfo(asyncResp, connectionNames[0].first, path, connectionNames[0].second); @@ -768,35 +771,36 @@ inline void afterChassisDriveCollectionSubtreeGet( path + "/drive", [asyncResp, chassisId](const boost::system::error_code& ec3, const dbus::utility::MapperEndPoints& resp) { - if (ec3) - { - BMCWEB_LOG_ERROR("Error in chassis Drive association "); - } - nlohmann::json& members = asyncResp->res.jsonValue["Members"]; - // important if array is empty - members = nlohmann::json::array(); + if (ec3) + { + BMCWEB_LOG_ERROR("Error in chassis Drive association "); + } + nlohmann::json& members = asyncResp->res.jsonValue["Members"]; + // important if array is empty + members = nlohmann::json::array(); - std::vector<std::string> leafNames; - for (const auto& drive : resp) - { - sdbusplus::message::object_path drivePath(drive); - leafNames.push_back(drivePath.filename()); - } + std::vector<std::string> leafNames; + for (const auto& drive : resp) + { + sdbusplus::message::object_path drivePath(drive); + leafNames.push_back(drivePath.filename()); + } - std::ranges::sort(leafNames, AlphanumLess<std::string>()); + std::ranges::sort(leafNames, AlphanumLess<std::string>()); - for (const auto& leafName : leafNames) - { - nlohmann::json::object_t member; - member["@odata.id"] = boost::urls::format( - "/redfish/v1/Chassis/{}/Drives/{}", chassisId, leafName); - members.emplace_back(std::move(member)); - // navigation links will be registered in next patch set - } - asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); - }); // end association lambda + for (const auto& leafName : leafNames) + { + nlohmann::json::object_t member; + member["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}", + chassisId, leafName); + members.emplace_back(std::move(member)); + // navigation links will be registered in next patch set + } + asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); + }); // end association lambda - } // end Iterate over all retrieved ObjectPaths + } // end Iterate over all retrieved ObjectPaths } /** * Chassis drives, this URL will show all the DriveCollection @@ -865,7 +869,7 @@ inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, asyncResp->res.jsonValue["Name"] = driveName; asyncResp->res.jsonValue["Id"] = driveName; // default it to Enabled - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; nlohmann::json::object_t linkChassisNav; linkChassisNav["@odata.id"] = @@ -877,11 +881,10 @@ inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, } } -inline void - matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& driveName, - const std::vector<std::string>& resp) +inline void matchAndFillDrive( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& driveName, + const std::vector<std::string>& resp) { for (const std::string& drivePath : resp) { @@ -899,16 +902,15 @@ inline void [asyncResp, chassisId, driveName]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - buildDrive(asyncResp, chassisId, driveName, ec, subtree); - }); + buildDrive(asyncResp, chassisId, driveName, ec, subtree); + }); } } -inline void - handleChassisDriveGet(crow::App& app, const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& chassisId, - const std::string& driveName) +inline void handleChassisDriveGet( + crow::App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& chassisId, const std::string& driveName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { @@ -924,41 +926,42 @@ inline void [asyncResp, chassisId, driveName](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - - // Iterate over all retrieved ObjectPaths. - for (const auto& [path, connectionNames] : subtree) - { - sdbusplus::message::object_path objPath(path); - if (objPath.filename() != chassisId) + if (ec) { - continue; + messages::internalError(asyncResp->res); + return; } - if (connectionNames.empty()) + // Iterate over all retrieved ObjectPaths. + for (const auto& [path, connectionNames] : subtree) { - BMCWEB_LOG_ERROR("Got 0 Connection names"); - continue; - } + sdbusplus::message::object_path objPath(path); + if (objPath.filename() != chassisId) + { + continue; + } - dbus::utility::getAssociationEndPoints( - path + "/drive", - [asyncResp, chassisId, - driveName](const boost::system::error_code& ec3, - const dbus::utility::MapperEndPoints& resp) { - if (ec3) + if (connectionNames.empty()) { - return; // no drives = no failures + BMCWEB_LOG_ERROR("Got 0 Connection names"); + continue; } - matchAndFillDrive(asyncResp, chassisId, driveName, resp); - }); - break; - } - }); + + dbus::utility::getAssociationEndPoints( + path + "/drive", + [asyncResp, chassisId, + driveName](const boost::system::error_code& ec3, + const dbus::utility::MapperEndPoints& resp) { + if (ec3) + { + return; // no drives = no failures + } + matchAndFillDrive(asyncResp, chassisId, driveName, + resp); + }); + break; + } + }); } /** @@ -1031,24 +1034,25 @@ inline void populateStorageController( BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId); asyncResp->res.jsonValue["Name"] = controllerId; asyncResp->res.jsonValue["Id"] = controllerId; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; sdbusplus::asio::getProperty<bool>( *crow::connections::systemBus, connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp](const boost::system::error_code& ec, bool isPresent) { - // this interface isn't necessary, only check it - // if we get a good return - if (ec) - { - BMCWEB_LOG_DEBUG("Failed to get Present property"); - return; - } - if (!isPresent) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - } - }); + // this interface isn't necessary, only check it + // if we get a good return + if (ec) + { + BMCWEB_LOG_DEBUG("Failed to get Present property"); + return; + } + if (!isPresent) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + } + }); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, connectionName, path, @@ -1057,8 +1061,8 @@ inline void populateStorageController( const std::vector< std::pair<std::string, dbus::utility::DbusVariantType>>& propertiesList) { - getStorageControllerAsset(asyncResp, ec, propertiesList); - }); + getStorageControllerAsset(asyncResp, ec, propertiesList); + }); } inline void getStorageControllerHandler( @@ -1166,8 +1170,8 @@ inline void handleSystemsStorageControllerCollectionGet( [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& controllerList) { - populateStorageControllerCollection(asyncResp, ec, controllerList); - }); + populateStorageControllerCollection(asyncResp, ec, controllerList); + }); } inline void handleSystemsStorageControllerGet( @@ -1194,8 +1198,8 @@ inline void handleSystemsStorageControllerGet( [asyncResp, controllerId](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - getStorageControllerHandler(asyncResp, controllerId, ec, subtree); - }); + getStorageControllerHandler(asyncResp, controllerId, ec, subtree); + }); } inline void requestRoutesStorageControllerCollection(App& app) diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index 5d7dba1c36..d9a8d17455 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -20,7 +20,9 @@ #include "app.hpp" #include "dbus_singleton.hpp" #include "dbus_utility.hpp" +#include "generated/enums/action_info.hpp" #include "generated/enums/computer_system.hpp" +#include "generated/enums/open_bmc_computer_system.hpp" #include "generated/enums/resource.hpp" #include "hypervisor_system.hpp" #include "led.hpp" @@ -65,9 +67,8 @@ const static std::array<std::pair<std::string_view, std::string_view>, 2> * * @return None. */ -inline void - updateDimmProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - bool isDimmFunctional) +inline void updateDimmProperties( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, bool isDimmFunctional) { BMCWEB_LOG_DEBUG("Dimm Functional: {}", isDimmFunctional); @@ -124,9 +125,8 @@ inline void modifyCpuFunctionalState( * * @return None. */ -inline void - modifyCpuPresenceState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - bool isCpuPresent) +inline void modifyCpuPresenceState( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, bool isCpuPresent) { BMCWEB_LOG_DEBUG("Cpu Present: {}", isCpuPresent); @@ -216,14 +216,14 @@ inline void [asyncResp, service, path](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { - if (ec2) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec2); - messages::internalError(asyncResp->res); - return; - } - getProcessorProperties(asyncResp, properties); - }); + if (ec2) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec2); + messages::internalError(asyncResp->res); + return; + } + getProcessorProperties(asyncResp, properties); + }); } /* @@ -295,14 +295,14 @@ inline void [asyncResp, service, path](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { - if (ec2) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec2); - messages::internalError(asyncResp->res); - return; - } - processMemoryProperties(asyncResp, properties); - }); + if (ec2) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec2); + messages::internalError(asyncResp->res); + return; + } + processMemoryProperties(asyncResp, properties); + }); } inline void afterGetUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, @@ -403,10 +403,9 @@ inline void "BiosVersion", false); } -inline void - afterGetAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const boost::system::error_code& ec, - const std::string& value) +inline void afterGetAssetTag( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const boost::system::error_code& ec, const std::string& value) { if (ec) { @@ -473,8 +472,8 @@ inline void afterSystemGetSubTree( [asyncResp](const boost::system::error_code& ec3, const dbus::utility::DBusPropertiesMap& properties) { - afterGetUUID(asyncResp, ec3, properties); - }); + afterGetUUID(asyncResp, ec3, properties); + }); } else if (interfaceName == "xyz.openbmc_project.Inventory.Item.System") @@ -485,8 +484,8 @@ inline void afterSystemGetSubTree( [asyncResp](const boost::system::error_code& ec3, const dbus::utility::DBusPropertiesMap& properties) { - afterGetInventory(asyncResp, ec3, properties); - }); + afterGetInventory(asyncResp, ec3, properties); + }); sdbusplus::asio::getProperty<std::string>( *crow::connections::systemBus, connection.first, path, @@ -539,58 +538,71 @@ inline void getHostState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) "CurrentHostState", [asyncResp](const boost::system::error_code& ec, const std::string& hostState) { - if (ec) - { - if (ec == boost::system::errc::host_unreachable) + if (ec) { - // Service not available, no error, just don't return - // host state info - BMCWEB_LOG_DEBUG("Service not available {}", ec); + if (ec == boost::system::errc::host_unreachable) + { + // Service not available, no error, just don't return + // host state info + BMCWEB_LOG_DEBUG("Service not available {}", ec); + return; + } + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - BMCWEB_LOG_DEBUG("Host state: {}", hostState); - // Verify Host State - if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - } - else if (hostState == - "xyz.openbmc_project.State.Host.HostState.Quiesced") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; - } - else if (hostState == - "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") - { - asyncResp->res.jsonValue["PowerState"] = "On"; - asyncResp->res.jsonValue["Status"]["State"] = "InTest"; - } - else if ( - hostState == - "xyz.openbmc_project.State.Host.HostState.TransitioningToRunning") - { - asyncResp->res.jsonValue["PowerState"] = "PoweringOn"; - asyncResp->res.jsonValue["Status"]["State"] = "Starting"; - } - else if (hostState == - "xyz.openbmc_project.State.Host.HostState.TransitioningToOff") - { - asyncResp->res.jsonValue["PowerState"] = "PoweringOff"; - asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; - } - else - { - asyncResp->res.jsonValue["PowerState"] = "Off"; - asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; - } - }); + BMCWEB_LOG_DEBUG("Host state: {}", hostState); + // Verify Host State + if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + } + else if (hostState == + "xyz.openbmc_project.State.Host.HostState.Quiesced") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Quiesced; + } + else if (hostState == + "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::On; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::InTest; + } + else if ( + hostState == + "xyz.openbmc_project.State.Host.HostState.TransitioningToRunning") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOn; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Starting; + } + else if ( + hostState == + "xyz.openbmc_project.State.Host.HostState.TransitioningToOff") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOff; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Disabled; + } + else + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::Off; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Disabled; + } + }); } /** @@ -763,10 +775,9 @@ inline std::string dbusToRfBootProgress(const std::string& dbusBootProgress) * * @return Integer error code. */ -inline int - assignBootParameters(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::string& rfSource, std::string& bootSource, - std::string& bootMode) +inline int assignBootParameters( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& rfSource, std::string& bootSource, std::string& bootMode) { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; @@ -828,18 +839,18 @@ inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) "xyz.openbmc_project.State.Boot.Progress", "BootProgress", [asyncResp](const boost::system::error_code& ec, const std::string& bootProgressStr) { - if (ec) - { - // BootProgress is an optional object so just do nothing if - // not found - return; - } + if (ec) + { + // BootProgress is an optional object so just do nothing if + // not found + return; + } - BMCWEB_LOG_DEBUG("Boot Progress: {}", bootProgressStr); + BMCWEB_LOG_DEBUG("Boot Progress: {}", bootProgressStr); - asyncResp->res.jsonValue["BootProgress"]["LastState"] = - dbusToRfBootProgress(bootProgressStr); - }); + asyncResp->res.jsonValue["BootProgress"]["LastState"] = + dbusToRfBootProgress(bootProgressStr); + }); } /** @@ -858,22 +869,22 @@ inline void getBootProgressLastStateTime( "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", [asyncResp](const boost::system::error_code& ec, const uint64_t lastStateTime) { - if (ec) - { - BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); + return; + } - // BootProgressLastUpdate is the last time the BootProgress property - // was updated. The time is the Epoch time, number of microseconds - // since 1 Jan 1970 00::00::00 UTC." - // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/ - // yaml/xyz/openbmc_project/State/Boot/Progress.interface.yaml#L11 + // BootProgressLastUpdate is the last time the BootProgress property + // was updated. The time is the Epoch time, number of microseconds + // since 1 Jan 1970 00::00::00 UTC." + // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/ + // yaml/xyz/openbmc_project/State/Boot/Progress.interface.yaml#L11 - // Convert to ISO 8601 standard - asyncResp->res.jsonValue["BootProgress"]["LastStateTime"] = - redfish::time_utils::getDateTimeUintUs(lastStateTime); - }); + // Convert to ISO 8601 standard + asyncResp->res.jsonValue["BootProgress"]["LastStateTime"] = + redfish::time_utils::getDateTimeUintUs(lastStateTime); + }); } /** @@ -893,28 +904,28 @@ inline void "xyz.openbmc_project.Control.Boot.Type", "BootType", [asyncResp](const boost::system::error_code& ec, const std::string& bootType) { - if (ec) - { - // not an error, don't have to have the interface - return; - } + if (ec) + { + // not an error, don't have to have the interface + return; + } - BMCWEB_LOG_DEBUG("Boot type: {}", bootType); + BMCWEB_LOG_DEBUG("Boot type: {}", bootType); - asyncResp->res - .jsonValue["Boot"] - ["BootSourceOverrideMode@Redfish.AllowableValues"] = - nlohmann::json::array_t({"Legacy", "UEFI"}); + asyncResp->res + .jsonValue["Boot"] + ["BootSourceOverrideMode@Redfish.AllowableValues"] = + nlohmann::json::array_t({"Legacy", "UEFI"}); - auto rfType = dbusToRfBootType(bootType); - if (rfType.empty()) - { - messages::internalError(asyncResp->res); - return; - } + auto rfType = dbusToRfBootType(bootType); + if (rfType.empty()) + { + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; - }); + asyncResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; + }); } /** @@ -934,39 +945,39 @@ inline void "xyz.openbmc_project.Control.Boot.Mode", "BootMode", [asyncResp](const boost::system::error_code& ec, const std::string& bootModeStr) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } - BMCWEB_LOG_DEBUG("Boot mode: {}", bootModeStr); - - nlohmann::json::array_t allowed; - allowed.emplace_back("None"); - allowed.emplace_back("Pxe"); - allowed.emplace_back("Hdd"); - allowed.emplace_back("Cd"); - allowed.emplace_back("Diags"); - allowed.emplace_back("BiosSetup"); - allowed.emplace_back("Usb"); - - asyncResp->res - .jsonValue["Boot"] - ["BootSourceOverrideTarget@Redfish.AllowableValues"] = - std::move(allowed); - if (bootModeStr != - "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") - { - auto rfMode = dbusToRfBootMode(bootModeStr); - if (!rfMode.empty()) + BMCWEB_LOG_DEBUG("Boot mode: {}", bootModeStr); + + nlohmann::json::array_t allowed; + allowed.emplace_back("None"); + allowed.emplace_back("Pxe"); + allowed.emplace_back("Hdd"); + allowed.emplace_back("Cd"); + allowed.emplace_back("Diags"); + allowed.emplace_back("BiosSetup"); + allowed.emplace_back("Usb"); + + asyncResp->res + .jsonValue["Boot"] + ["BootSourceOverrideTarget@Redfish.AllowableValues"] = + std::move(allowed); + if (bootModeStr != + "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") { - asyncResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = - rfMode; + auto rfMode = dbusToRfBootMode(bootModeStr); + if (!rfMode.empty()) + { + asyncResp->res + .jsonValue["Boot"]["BootSourceOverrideTarget"] = rfMode; + } } - } - }); + }); } /** @@ -986,30 +997,30 @@ inline void "xyz.openbmc_project.Control.Boot.Source", "BootSource", [asyncResp](const boost::system::error_code& ec, const std::string& bootSourceStr) { - if (ec) - { - if (ec.value() == boost::asio::error::host_unreachable) + if (ec) { + if (ec.value() == boost::asio::error::host_unreachable) + { + return; + } + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - BMCWEB_LOG_DEBUG("Boot source: {}", bootSourceStr); + BMCWEB_LOG_DEBUG("Boot source: {}", bootSourceStr); - auto rfSource = dbusToRfBootSource(bootSourceStr); - if (!rfSource.empty()) - { - asyncResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = - rfSource; - } + auto rfSource = dbusToRfBootSource(bootSourceStr); + if (!rfSource.empty()) + { + asyncResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = + rfSource; + } - // Get BootMode as BootSourceOverrideTarget is constructed - // from both BootSource and BootMode - getBootOverrideMode(asyncResp); - }); + // Get BootMode as BootSourceOverrideTarget is constructed + // from both BootSource and BootMode + getBootOverrideMode(asyncResp); + }); } /** @@ -1040,24 +1051,24 @@ inline void processBootOverrideEnable( "/xyz/openbmc_project/control/host0/boot/one_time", "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp](const boost::system::error_code& ec, bool oneTimeSetting) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } - if (oneTimeSetting) - { - asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = - "Once"; - } - else - { - asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = - "Continuous"; - } - }); + if (oneTimeSetting) + { + asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = + "Once"; + } + else + { + asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = + "Continuous"; + } + }); } /** @@ -1077,19 +1088,19 @@ inline void "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp](const boost::system::error_code& ec, const bool bootOverrideEnable) { - if (ec) - { - if (ec.value() == boost::asio::error::host_unreachable) + if (ec) { + if (ec.value() == boost::asio::error::host_unreachable) + { + return; + } + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); - return; - } - processBootOverrideEnable(asyncResp, bootOverrideEnable); - }); + processBootOverrideEnable(asyncResp, bootOverrideEnable); + }); } /** @@ -1132,20 +1143,20 @@ inline void "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", [asyncResp](const boost::system::error_code& ec, uint64_t lastResetTime) { - if (ec) - { - BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); + return; + } - // LastStateChangeTime is epoch time, in milliseconds - // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 - uint64_t lastResetTimeStamp = lastResetTime / 1000; + // LastStateChangeTime is epoch time, in milliseconds + // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 + uint64_t lastResetTimeStamp = lastResetTime / 1000; - // Convert to ISO 8601 standard - asyncResp->res.jsonValue["LastResetTime"] = - redfish::time_utils::getDateTimeUint(lastResetTimeStamp); - }); + // Convert to ISO 8601 standard + asyncResp->res.jsonValue["LastResetTime"] = + redfish::time_utils::getDateTimeUint(lastResetTimeStamp); + }); } /** @@ -1172,42 +1183,42 @@ inline void getAutomaticRebootAttempts( [asyncResp{asyncResp}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); + messages::internalError(asyncResp->res); + } + return; } - return; - } - const uint32_t* attemptsLeft = nullptr; - const uint32_t* retryAttempts = nullptr; + const uint32_t* attemptsLeft = nullptr; + const uint32_t* retryAttempts = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "AttemptsLeft", - attemptsLeft, "RetryAttempts", retryAttempts); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, + "AttemptsLeft", attemptsLeft, "RetryAttempts", retryAttempts); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (attemptsLeft != nullptr) - { - asyncResp->res - .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = - *attemptsLeft; - } + if (attemptsLeft != nullptr) + { + asyncResp->res + .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = + *attemptsLeft; + } - if (retryAttempts != nullptr) - { - asyncResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = - *retryAttempts; - } - }); + if (retryAttempts != nullptr) + { + asyncResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = + *retryAttempts; + } + }); } /** @@ -1228,39 +1239,40 @@ inline void "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", [asyncResp](const boost::system::error_code& ec, bool autoRebootEnabled) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); + messages::internalError(asyncResp->res); + } + return; } - return; - } - - BMCWEB_LOG_DEBUG("Auto Reboot: {}", autoRebootEnabled); - if (autoRebootEnabled) - { - asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = - "RetryAttempts"; - } - else - { - asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = - "Disabled"; - } - getAutomaticRebootAttempts(asyncResp); - // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, - // and RetryAttempts. OpenBMC only supports Disabled and - // RetryAttempts. - nlohmann::json::array_t allowed; - allowed.emplace_back("Disabled"); - allowed.emplace_back("RetryAttempts"); - asyncResp->res - .jsonValue["Boot"]["AutomaticRetryConfig@Redfish.AllowableValues"] = - std::move(allowed); - }); + BMCWEB_LOG_DEBUG("Auto Reboot: {}", autoRebootEnabled); + if (autoRebootEnabled) + { + asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = + "RetryAttempts"; + } + else + { + asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = + "Disabled"; + } + getAutomaticRebootAttempts(asyncResp); + + // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, + // and RetryAttempts. OpenBMC only supports Disabled and + // RetryAttempts. + nlohmann::json::array_t allowed; + allowed.emplace_back("Disabled"); + allowed.emplace_back("RetryAttempts"); + asyncResp->res + .jsonValue["Boot"] + ["AutomaticRetryConfig@Redfish.AllowableValues"] = + std::move(allowed); + }); } /** @@ -1327,21 +1339,21 @@ inline void "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", [asyncResp](const boost::system::error_code& ec, const std::string& policy) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - return; - } - computer_system::PowerRestorePolicyTypes restore = - redfishPowerRestorePolicyFromDbus(policy); - if (restore == computer_system::PowerRestorePolicyTypes::Invalid) - { - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + return; + } + computer_system::PowerRestorePolicyTypes restore = + redfishPowerRestorePolicyFromDbus(policy); + if (restore == computer_system::PowerRestorePolicyTypes::Invalid) + { + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["PowerRestorePolicy"] = restore; - }); + asyncResp->res.jsonValue["PowerRestorePolicy"] = restore; + }); } /** @@ -1361,25 +1373,27 @@ inline void "/xyz/openbmc_project/logging/settings", "xyz.openbmc_project.Logging.Settings", "QuiesceOnHwError", [asyncResp](const boost::system::error_code& ec, bool value) { - if (ec) - { - if (ec.value() != EBADR) + if (ec) { - BMCWEB_LOG_ERROR("DBUS response error {}", ec); - messages::internalError(asyncResp->res); + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + } + return; } - return; - } - if (value) - { - asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = "AnyFault"; - } - else - { - asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = "Never"; - } - }); + if (value) + { + asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = + computer_system::StopBootOnFault::AnyFault; + } + else + { + asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = + computer_system::StopBootOnFault::Never; + } + }); } /** @@ -1400,72 +1414,72 @@ inline void getTrustedModuleRequiredToBoot( "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error on TPM.Policy GetSubTree{}", - ec); - // This is an optional D-Bus object so just return if - // error occurs - return; - } - if (subtree.empty()) - { - // As noted above, this is an optional interface so just return - // if there is no instance found - return; - } - - /* When there is more than one TPMEnable object... */ - if (subtree.size() > 1) - { - BMCWEB_LOG_DEBUG( - "DBUS response has more than 1 TPM Enable object:{}", - subtree.size()); - // Throw an internal Error and return - messages::internalError(asyncResp->res); - return; - } - - // Make sure the Dbus response map has a service and objectPath - // field - if (subtree[0].first.empty() || subtree[0].second.size() != 1) - { - BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); - messages::internalError(asyncResp->res); - return; - } - - const std::string& path = subtree[0].first; - const std::string& serv = subtree[0].second.begin()->first; - - // Valid TPM Enable object found, now reading the current value - sdbusplus::asio::getProperty<bool>( - *crow::connections::systemBus, serv, path, - "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", - [asyncResp](const boost::system::error_code& ec2, - bool tpmRequired) { - if (ec2) + if (ec) { - BMCWEB_LOG_ERROR("D-BUS response error on TPM.Policy Get{}", - ec2); - messages::internalError(asyncResp->res); + BMCWEB_LOG_DEBUG( + "DBUS response error on TPM.Policy GetSubTree{}", ec); + // This is an optional D-Bus object so just return if + // error occurs + return; + } + if (subtree.empty()) + { + // As noted above, this is an optional interface so just return + // if there is no instance found return; } - if (tpmRequired) + /* When there is more than one TPMEnable object... */ + if (subtree.size() > 1) { - asyncResp->res - .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = - "Required"; + BMCWEB_LOG_DEBUG( + "DBUS response has more than 1 TPM Enable object:{}", + subtree.size()); + // Throw an internal Error and return + messages::internalError(asyncResp->res); + return; } - else + + // Make sure the Dbus response map has a service and objectPath + // field + if (subtree[0].first.empty() || subtree[0].second.size() != 1) { - asyncResp->res - .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = - "Disabled"; + BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); + messages::internalError(asyncResp->res); + return; } + + const std::string& path = subtree[0].first; + const std::string& serv = subtree[0].second.begin()->first; + + // Valid TPM Enable object found, now reading the current value + sdbusplus::asio::getProperty<bool>( + *crow::connections::systemBus, serv, path, + "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", + [asyncResp](const boost::system::error_code& ec2, + bool tpmRequired) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "D-BUS response error on TPM.Policy Get{}", ec2); + messages::internalError(asyncResp->res); + return; + } + + if (tpmRequired) + { + asyncResp->res + .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = + "Required"; + } + else + { + asyncResp->res + .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = + "Disabled"; + } + }); }); - }); } /** @@ -1488,55 +1502,56 @@ inline void setTrustedModuleRequiredToBoot( [asyncResp, tpmRequired](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error on TPM.Policy GetSubTree{}", - ec); - messages::internalError(asyncResp->res); - return; - } - if (subtree.empty()) - { - messages::propertyValueNotInList(asyncResp->res, "ComputerSystem", - "TrustedModuleRequiredToBoot"); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error on TPM.Policy GetSubTree{}", ec); + messages::internalError(asyncResp->res); + return; + } + if (subtree.empty()) + { + messages::propertyValueNotInList(asyncResp->res, + "ComputerSystem", + "TrustedModuleRequiredToBoot"); + return; + } - /* When there is more than one TPMEnable object... */ - if (subtree.size() > 1) - { - BMCWEB_LOG_DEBUG( - "DBUS response has more than 1 TPM Enable object:{}", - subtree.size()); - // Throw an internal Error and return - messages::internalError(asyncResp->res); - return; - } + /* When there is more than one TPMEnable object... */ + if (subtree.size() > 1) + { + BMCWEB_LOG_DEBUG( + "DBUS response has more than 1 TPM Enable object:{}", + subtree.size()); + // Throw an internal Error and return + messages::internalError(asyncResp->res); + return; + } - // Make sure the Dbus response map has a service and objectPath - // field - if (subtree[0].first.empty() || subtree[0].second.size() != 1) - { - BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); - messages::internalError(asyncResp->res); - return; - } + // Make sure the Dbus response map has a service and objectPath + // field + if (subtree[0].first.empty() || subtree[0].second.size() != 1) + { + BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); + messages::internalError(asyncResp->res); + return; + } - const std::string& path = subtree[0].first; - const std::string& serv = subtree[0].second.begin()->first; + const std::string& path = subtree[0].first; + const std::string& serv = subtree[0].second.begin()->first; - if (serv.empty()) - { - BMCWEB_LOG_DEBUG("TPM.Policy service mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (serv.empty()) + { + BMCWEB_LOG_DEBUG("TPM.Policy service mapper error!"); + messages::internalError(asyncResp->res); + return; + } - // Valid TPM Enable object found, now setting the value - setDbusProperty(asyncResp, "Boot/TrustedModuleRequiredToBoot", serv, - path, "xyz.openbmc_project.Control.TPM.Policy", - "TPMEnable", tpmRequired); - }); + // Valid TPM Enable object found, now setting the value + setDbusProperty(asyncResp, "Boot/TrustedModuleRequiredToBoot", serv, + path, "xyz.openbmc_project.Control.TPM.Policy", + "TPMEnable", tpmRequired); + }); } /** @@ -1755,47 +1770,47 @@ inline void setAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, [asyncResp, assetTag](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); - messages::internalError(asyncResp->res); - return; - } - if (subtree.empty()) - { - BMCWEB_LOG_DEBUG("Can't find system D-Bus object!"); - messages::internalError(asyncResp->res); - return; - } - // Assume only 1 system D-Bus object - // Throw an error if there is more than 1 - if (subtree.size() > 1) - { - BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus object!"); - messages::internalError(asyncResp->res); - return; - } - if (subtree[0].first.empty() || subtree[0].second.size() != 1) - { - BMCWEB_LOG_DEBUG("Asset Tag Set mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); + messages::internalError(asyncResp->res); + return; + } + if (subtree.empty()) + { + BMCWEB_LOG_DEBUG("Can't find system D-Bus object!"); + messages::internalError(asyncResp->res); + return; + } + // Assume only 1 system D-Bus object + // Throw an error if there is more than 1 + if (subtree.size() > 1) + { + BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus object!"); + messages::internalError(asyncResp->res); + return; + } + if (subtree[0].first.empty() || subtree[0].second.size() != 1) + { + BMCWEB_LOG_DEBUG("Asset Tag Set mapper error!"); + messages::internalError(asyncResp->res); + return; + } - const std::string& path = subtree[0].first; - const std::string& service = subtree[0].second.begin()->first; + const std::string& path = subtree[0].first; + const std::string& service = subtree[0].second.begin()->first; - if (service.empty()) - { - BMCWEB_LOG_DEBUG("Asset Tag Set service mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (service.empty()) + { + BMCWEB_LOG_DEBUG("Asset Tag Set service mapper error!"); + messages::internalError(asyncResp->res); + return; + } - setDbusProperty(asyncResp, "AssetTag", service, path, - "xyz.openbmc_project.Inventory.Decorator.AssetTag", - "AssetTag", assetTag); - }); + setDbusProperty(asyncResp, "AssetTag", service, path, + "xyz.openbmc_project.Inventory.Decorator.AssetTag", + "AssetTag", assetTag); + }); } /** @@ -1962,56 +1977,62 @@ inline void "/xyz/openbmc_project/pfr", "xyz.openbmc_project.PFR.Attributes", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - nlohmann::json& oemPFR = - asyncResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; - asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = - "#OemComputerSystem.OpenBmc"; - oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; + nlohmann::json& oemPFR = + asyncResp->res + .jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; + asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = + "#OpenBMCComputerSystem.v1_0_0.OpenBmc"; + oemPFR["@odata.type"] = + "#OpenBMCComputerSystem.FirmwareProvisioning"; - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - // not an error, don't have to have the interface - oemPFR["ProvisioningStatus"] = "NotProvisioned"; - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + // not an error, don't have to have the interface + oemPFR["ProvisioningStatus"] = open_bmc_computer_system:: + FirmwareProvisioningStatus::NotProvisioned; + return; + } - const bool* provState = nullptr; - const bool* lockState = nullptr; + const bool* provState = nullptr; + const bool* lockState = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "UfmProvisioned", - provState, "UfmLocked", lockState); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, + "UfmProvisioned", provState, "UfmLocked", lockState); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if ((provState == nullptr) || (lockState == nullptr)) - { - BMCWEB_LOG_DEBUG("Unable to get PFR attributes."); - messages::internalError(asyncResp->res); - return; - } + if ((provState == nullptr) || (lockState == nullptr)) + { + BMCWEB_LOG_DEBUG("Unable to get PFR attributes."); + messages::internalError(asyncResp->res); + return; + } - if (*provState) - { - if (*lockState) + if (*provState) { - oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; + if (*lockState) + { + oemPFR["ProvisioningStatus"] = open_bmc_computer_system:: + FirmwareProvisioningStatus::ProvisionedAndLocked; + } + else + { + oemPFR["ProvisioningStatus"] = open_bmc_computer_system:: + FirmwareProvisioningStatus::ProvisionedButNotLocked; + } } else { - oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; + oemPFR["ProvisioningStatus"] = open_bmc_computer_system:: + FirmwareProvisioningStatus::NotProvisioned; } - } - else - { - oemPFR["ProvisioningStatus"] = "NotProvisioned"; - } - }); + }); } /** @@ -2139,54 +2160,55 @@ inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error on Power.Mode GetSubTree {}", - ec); - // This is an optional D-Bus object so just return if - // error occurs - return; - } - if (subtree.empty()) - { - // As noted above, this is an optional interface so just return - // if there is no instance found - return; - } - if (subtree.size() > 1) - { - // More then one PowerMode object is not supported and is an - // error - BMCWEB_LOG_DEBUG( - "Found more than 1 system D-Bus Power.Mode objects: {}", - subtree.size()); - messages::internalError(asyncResp->res); - return; - } - if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) - { - BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); - messages::internalError(asyncResp->res); - return; - } - const std::string& path = subtree[0].first; - const std::string& service = subtree[0].second.begin()->first; - if (service.empty()) - { - BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_DEBUG( + "DBUS response error on Power.Mode GetSubTree {}", ec); + // This is an optional D-Bus object so just return if + // error occurs + return; + } + if (subtree.empty()) + { + // As noted above, this is an optional interface so just return + // if there is no instance found + return; + } + if (subtree.size() > 1) + { + // More then one PowerMode object is not supported and is an + // error + BMCWEB_LOG_DEBUG( + "Found more than 1 system D-Bus Power.Mode objects: {}", + subtree.size()); + messages::internalError(asyncResp->res); + return; + } + if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) + { + BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); + messages::internalError(asyncResp->res); + return; + } + const std::string& path = subtree[0].first; + const std::string& service = subtree[0].second.begin()->first; + if (service.empty()) + { + BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); + messages::internalError(asyncResp->res); + return; + } - // Valid Power Mode object found, now read the mode properties - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, service, path, - "xyz.openbmc_project.Control.Power.Mode", - [asyncResp](const boost::system::error_code& ec2, - const dbus::utility::DBusPropertiesMap& properties) { - afterGetPowerMode(asyncResp, ec2, properties); + // Valid Power Mode object found, now read the mode properties + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, service, path, + "xyz.openbmc_project.Control.Power.Mode", + [asyncResp]( + const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& properties) { + afterGetPowerMode(asyncResp, ec2, properties); + }); }); - }); } /** @@ -2268,53 +2290,53 @@ inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, [asyncResp, powerMode](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR("DBUS response error on Power.Mode GetSubTree {}", - ec); - // This is an optional D-Bus object, but user attempted to patch - messages::internalError(asyncResp->res); - return; - } - if (subtree.empty()) - { - // This is an optional D-Bus object, but user attempted to patch - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - "PowerMode"); - return; - } - if (subtree.size() > 1) - { - // More then one PowerMode object is not supported and is an - // error - BMCWEB_LOG_DEBUG( - "Found more than 1 system D-Bus Power.Mode objects: {}", - subtree.size()); - messages::internalError(asyncResp->res); - return; - } - if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) - { - BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); - messages::internalError(asyncResp->res); - return; - } - const std::string& path = subtree[0].first; - const std::string& service = subtree[0].second.begin()->first; - if (service.empty()) - { - BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error on Power.Mode GetSubTree {}", ec); + // This is an optional D-Bus object, but user attempted to patch + messages::internalError(asyncResp->res); + return; + } + if (subtree.empty()) + { + // This is an optional D-Bus object, but user attempted to patch + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + "PowerMode"); + return; + } + if (subtree.size() > 1) + { + // More then one PowerMode object is not supported and is an + // error + BMCWEB_LOG_DEBUG( + "Found more than 1 system D-Bus Power.Mode objects: {}", + subtree.size()); + messages::internalError(asyncResp->res); + return; + } + if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) + { + BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); + messages::internalError(asyncResp->res); + return; + } + const std::string& path = subtree[0].first; + const std::string& service = subtree[0].second.begin()->first; + if (service.empty()) + { + BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); + messages::internalError(asyncResp->res); + return; + } - BMCWEB_LOG_DEBUG("Setting power mode({}) -> {}", powerMode, path); + BMCWEB_LOG_DEBUG("Setting power mode({}) -> {}", powerMode, path); - // Set the Power Mode property - setDbusProperty(asyncResp, "PowerMode", service, path, - "xyz.openbmc_project.Control.Power.Mode", "PowerMode", - powerMode); - }); + // Set the Power Mode property + setDbusProperty(asyncResp, "PowerMode", service, path, + "xyz.openbmc_project.Control.Power.Mode", + "PowerMode", powerMode); + }); } /** @@ -2395,50 +2417,50 @@ inline void "xyz.openbmc_project.State.Watchdog", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { - if (ec) - { - // watchdog service is stopped - BMCWEB_LOG_DEBUG("DBUS response error {}", ec); - return; - } + if (ec) + { + // watchdog service is stopped + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + return; + } - BMCWEB_LOG_DEBUG("Got {} wdt prop.", properties.size()); + BMCWEB_LOG_DEBUG("Got {} wdt prop.", properties.size()); - nlohmann::json& hostWatchdogTimer = - asyncResp->res.jsonValue["HostWatchdogTimer"]; + nlohmann::json& hostWatchdogTimer = + asyncResp->res.jsonValue["HostWatchdogTimer"]; - // watchdog service is running/enabled - hostWatchdogTimer["Status"]["State"] = "Enabled"; + // watchdog service is running/enabled + hostWatchdogTimer["Status"]["State"] = resource::State::Enabled; - const bool* enabled = nullptr; - const std::string* expireAction = nullptr; + const bool* enabled = nullptr; + const std::string* expireAction = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, - "ExpireAction", expireAction); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), properties, "Enabled", + enabled, "ExpireAction", expireAction); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } - - if (enabled != nullptr) - { - hostWatchdogTimer["FunctionEnabled"] = *enabled; - } - - if (expireAction != nullptr) - { - std::string action = dbusToRfWatchdogAction(*expireAction); - if (action.empty()) + if (!success) { messages::internalError(asyncResp->res); return; } - hostWatchdogTimer["TimeoutAction"] = action; - } - }); + + if (enabled != nullptr) + { + hostWatchdogTimer["FunctionEnabled"] = *enabled; + } + + if (expireAction != nullptr) + { + std::string action = dbusToRfWatchdogAction(*expireAction); + if (action.empty()) + { + messages::internalError(asyncResp->res); + return; + } + hostWatchdogTimer["TimeoutAction"] = action; + } + }); } /** @@ -2574,67 +2596,69 @@ inline void "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR( - "DBUS response error on Power.IdlePowerSaver GetSubTree {}", - ec); - messages::internalError(asyncResp->res); - return; - } - if (subtree.empty()) - { - // This is an optional interface so just return - // if there is no instance found - BMCWEB_LOG_DEBUG("No instances found"); - return; - } - if (subtree.size() > 1) - { - // More then one PowerIdlePowerSaver object is not supported and - // is an error - BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus " - "Power.IdlePowerSaver objects: {}", - subtree.size()); - messages::internalError(asyncResp->res); - return; - } - if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) - { - BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); - messages::internalError(asyncResp->res); - return; - } - const std::string& path = subtree[0].first; - const std::string& service = subtree[0].second.begin()->first; - if (service.empty()) - { - BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); - messages::internalError(asyncResp->res); - return; - } - - // Valid IdlePowerSaver object found, now read the current values - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - [asyncResp](const boost::system::error_code& ec2, - const dbus::utility::DBusPropertiesMap& properties) { - if (ec2) + if (ec) { BMCWEB_LOG_ERROR( - "DBUS response error on IdlePowerSaver GetAll: {}", ec2); + "DBUS response error on Power.IdlePowerSaver GetSubTree {}", + ec); messages::internalError(asyncResp->res); return; } - - if (!parseIpsProperties(asyncResp, properties)) + if (subtree.empty()) { + // This is an optional interface so just return + // if there is no instance found + BMCWEB_LOG_DEBUG("No instances found"); + return; + } + if (subtree.size() > 1) + { + // More then one PowerIdlePowerSaver object is not supported and + // is an error + BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus " + "Power.IdlePowerSaver objects: {}", + subtree.size()); + messages::internalError(asyncResp->res); + return; + } + if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) + { + BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); + messages::internalError(asyncResp->res); + return; + } + const std::string& path = subtree[0].first; + const std::string& service = subtree[0].second.begin()->first; + if (service.empty()) + { + BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); messages::internalError(asyncResp->res); return; } + + // Valid IdlePowerSaver object found, now read the current values + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, service, path, + "xyz.openbmc_project.Control.Power.IdlePowerSaver", + [asyncResp]( + const boost::system::error_code& ec2, + const dbus::utility::DBusPropertiesMap& properties) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "DBUS response error on IdlePowerSaver GetAll: {}", + ec2); + messages::internalError(asyncResp->res); + return; + } + + if (!parseIpsProperties(asyncResp, properties)) + { + messages::internalError(asyncResp->res); + return; + } + }); }); - }); BMCWEB_LOG_DEBUG("EXIT: Get idle power saver parameters"); } @@ -2654,13 +2678,13 @@ inline void * * @return None. */ -inline void - setIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const std::optional<bool> ipsEnable, - const std::optional<uint8_t> ipsEnterUtil, - const std::optional<uint64_t> ipsEnterTime, - const std::optional<uint8_t> ipsExitUtil, - const std::optional<uint64_t> ipsExitTime) +inline void setIdlePowerSaver( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::optional<bool> ipsEnable, + const std::optional<uint8_t> ipsEnterUtil, + const std::optional<uint64_t> ipsEnterTime, + const std::optional<uint8_t> ipsExitUtil, + const std::optional<uint64_t> ipsExitTime) { BMCWEB_LOG_DEBUG("Set idle power saver properties"); @@ -2672,88 +2696,90 @@ inline void [asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - if (ec) - { - BMCWEB_LOG_ERROR( - "DBUS response error on Power.IdlePowerSaver GetSubTree {}", - ec); - messages::internalError(asyncResp->res); - return; - } - if (subtree.empty()) - { - // This is an optional D-Bus object, but user attempted to patch - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - "IdlePowerSaver"); - return; - } - if (subtree.size() > 1) - { - // More then one PowerIdlePowerSaver object is not supported and - // is an error - BMCWEB_LOG_DEBUG( - "Found more than 1 system D-Bus Power.IdlePowerSaver objects: {}", - subtree.size()); - messages::internalError(asyncResp->res); - return; - } - if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) - { - BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); - messages::internalError(asyncResp->res); - return; - } - const std::string& path = subtree[0].first; - const std::string& service = subtree[0].second.begin()->first; - if (service.empty()) - { - BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + BMCWEB_LOG_ERROR( + "DBUS response error on Power.IdlePowerSaver GetSubTree {}", + ec); + messages::internalError(asyncResp->res); + return; + } + if (subtree.empty()) + { + // This is an optional D-Bus object, but user attempted to patch + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + "IdlePowerSaver"); + return; + } + if (subtree.size() > 1) + { + // More then one PowerIdlePowerSaver object is not supported and + // is an error + BMCWEB_LOG_DEBUG( + "Found more than 1 system D-Bus Power.IdlePowerSaver objects: {}", + subtree.size()); + messages::internalError(asyncResp->res); + return; + } + if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) + { + BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); + messages::internalError(asyncResp->res); + return; + } + const std::string& path = subtree[0].first; + const std::string& service = subtree[0].second.begin()->first; + if (service.empty()) + { + BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); + messages::internalError(asyncResp->res); + return; + } - // Valid Power IdlePowerSaver object found, now set any values that - // need to be updated + // Valid Power IdlePowerSaver object found, now set any values that + // need to be updated - if (ipsEnable) - { - setDbusProperty(asyncResp, "IdlePowerSaver/Enabled", service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - "Enabled", *ipsEnable); - } - if (ipsEnterUtil) - { - setDbusProperty(asyncResp, "IdlePowerSaver/EnterUtilizationPercent", - service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - "EnterUtilizationPercent", *ipsEnterUtil); - } - if (ipsEnterTime) - { - // Convert from seconds into milliseconds for DBus - const uint64_t timeMilliseconds = *ipsEnterTime * 1000; - setDbusProperty(asyncResp, "IdlePowerSaver/EnterDwellTimeSeconds", - service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - "EnterDwellTime", timeMilliseconds); - } - if (ipsExitUtil) - { - setDbusProperty(asyncResp, "IdlePowerSaver/ExitUtilizationPercent", - service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - "ExitUtilizationPercent", *ipsExitUtil); - } - if (ipsExitTime) - { - // Convert from seconds into milliseconds for DBus - const uint64_t timeMilliseconds = *ipsExitTime * 1000; - setDbusProperty(asyncResp, "IdlePowerSaver/ExitDwellTimeSeconds", - service, path, - "xyz.openbmc_project.Control.Power.IdlePowerSaver", - "ExitDwellTime", timeMilliseconds); - } - }); + if (ipsEnable) + { + setDbusProperty( + asyncResp, "IdlePowerSaver/Enabled", service, path, + "xyz.openbmc_project.Control.Power.IdlePowerSaver", + "Enabled", *ipsEnable); + } + if (ipsEnterUtil) + { + setDbusProperty( + asyncResp, "IdlePowerSaver/EnterUtilizationPercent", + service, path, + "xyz.openbmc_project.Control.Power.IdlePowerSaver", + "EnterUtilizationPercent", *ipsEnterUtil); + } + if (ipsEnterTime) + { + // Convert from seconds into milliseconds for DBus + const uint64_t timeMilliseconds = *ipsEnterTime * 1000; + setDbusProperty( + asyncResp, "IdlePowerSaver/EnterDwellTimeSeconds", service, + path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", + "EnterDwellTime", timeMilliseconds); + } + if (ipsExitUtil) + { + setDbusProperty( + asyncResp, "IdlePowerSaver/ExitUtilizationPercent", service, + path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", + "ExitUtilizationPercent", *ipsExitUtil); + } + if (ipsExitTime) + { + // Convert from seconds into milliseconds for DBus + const uint64_t timeMilliseconds = *ipsExitTime * 1000; + setDbusProperty( + asyncResp, "IdlePowerSaver/ExitDwellTimeSeconds", service, + path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", + "ExitDwellTime", timeMilliseconds); + } + }); BMCWEB_LOG_DEBUG("EXIT: Set idle power saver parameters"); } @@ -2801,35 +2827,16 @@ inline void handleComputerSystemCollectionGet( system["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); ifaceArray.emplace_back(std::move(system)); - sdbusplus::asio::getProperty<std::string>( - *crow::connections::systemBus, "xyz.openbmc_project.Settings", - "/xyz/openbmc_project/network/hypervisor", - "xyz.openbmc_project.Network.SystemConfiguration", "HostName", - [asyncResp](const boost::system::error_code& ec2, - const std::string& /*hostName*/) { - if (ec2) - { - return; - } - auto val = asyncResp->res.jsonValue.find("Members@odata.count"); - if (val == asyncResp->res.jsonValue.end()) - { - BMCWEB_LOG_CRITICAL("Count wasn't found??"); - return; - } - uint64_t* count = val->get_ptr<uint64_t*>(); - if (count == nullptr) - { - BMCWEB_LOG_CRITICAL("Count wasn't found??"); - return; - } - *count = *count + 1; + + if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM) + { BMCWEB_LOG_DEBUG("Hypervisor is available"); - nlohmann::json& ifaceArray2 = asyncResp->res.jsonValue["Members"]; + asyncResp->res.jsonValue["Members@odata.count"] = 2; + nlohmann::json::object_t hypervisor; hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; - ifaceArray2.emplace_back(std::move(hypervisor)); - }); + ifaceArray.emplace_back(std::move(hypervisor)); + } } /** @@ -2845,14 +2852,14 @@ inline void doNMI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR(" Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - messages::success(asyncResp->res); - }, + if (ec) + { + BMCWEB_LOG_ERROR(" Bad D-Bus request error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + messages::success(asyncResp->res); + }, serviceName, objectPath, interfaceName, method); } @@ -2865,6 +2872,16 @@ inline void handleComputerSystemResetActionPost( { return; } + + if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM) + { + if (systemName == "hypervisor") + { + handleHypervisorSystemResetPost(req, asyncResp); + return; + } + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", @@ -3017,10 +3034,13 @@ inline void return; } - if (systemName == "hypervisor") + if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM) { - handleHypervisorSystemGet(asyncResp); - return; + if (systemName == "hypervisor") + { + handleHypervisorSystemGet(asyncResp); + return; + } } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) @@ -3036,7 +3056,8 @@ inline void "#ComputerSystem.v1_22_0.ComputerSystem"; asyncResp->res.jsonValue["Name"] = BMCWEB_REDFISH_SYSTEM_URI_NAME; asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_SYSTEM_URI_NAME; - asyncResp->res.jsonValue["SystemType"] = "Physical"; + asyncResp->res.jsonValue["SystemType"] = + computer_system::SystemType::Physical; asyncResp->res.jsonValue["Description"] = "Computer System"; asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = @@ -3073,8 +3094,8 @@ inline void manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; // Fill in SerialConsole info asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; @@ -3097,15 +3118,15 @@ inline void nlohmann::json::array_t({"KVMIP"}); } - getMainChassisId(asyncResp, - [](const std::string& chassisId, - const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { - nlohmann::json::array_t chassisArray; - nlohmann::json& chassis = chassisArray.emplace_back(); - chassis["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}", - chassisId); - aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); - }); + getMainChassisId( + asyncResp, [](const std::string& chassisId, + const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { + nlohmann::json::array_t chassisArray; + nlohmann::json& chassis = chassisArray.emplace_back(); + chassis["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}", chassisId); + aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); + }); getSystemLocationIndicatorActive(asyncResp); // TODO (Gunnar): Remove IndicatorLED after enough time has passed @@ -3179,30 +3200,30 @@ inline void handleComputerSystemPatch( std::optional<uint64_t> ipsExitTime; // clang-format off - if (!json_util::readJsonPatch( - req, asyncResp->res, - "IndicatorLED", indicatorLed, - "LocationIndicatorActive", locationIndicatorActive, - "AssetTag", assetTag, - "PowerRestorePolicy", powerRestorePolicy, - "PowerMode", powerMode, - "HostWatchdogTimer/FunctionEnabled", wdtEnable, - "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, - "Boot/BootSourceOverrideTarget", bootSource, - "Boot/BootSourceOverrideMode", bootType, - "Boot/BootSourceOverrideEnabled", bootEnable, - "Boot/AutomaticRetryConfig", bootAutomaticRetry, - "Boot/AutomaticRetryAttempts", bootAutomaticRetryAttempts, - "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, - "Boot/StopBootOnFault", stopBootOnFault, - "IdlePowerSaver/Enabled", ipsEnable, - "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, - "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, - "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, - "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) - { - return; - } + if (!json_util::readJsonPatch( + req, asyncResp->res, + "IndicatorLED", indicatorLed, + "LocationIndicatorActive", locationIndicatorActive, + "AssetTag", assetTag, + "PowerRestorePolicy", powerRestorePolicy, + "PowerMode", powerMode, + "HostWatchdogTimer/FunctionEnabled", wdtEnable, + "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, + "Boot/BootSourceOverrideTarget", bootSource, + "Boot/BootSourceOverrideMode", bootType, + "Boot/BootSourceOverrideEnabled", bootEnable, + "Boot/AutomaticRetryConfig", bootAutomaticRetry, + "Boot/AutomaticRetryAttempts", bootAutomaticRetryAttempts, + "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, + "Boot/StopBootOnFault", stopBootOnFault, + "IdlePowerSaver/Enabled", ipsEnable, + "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, + "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, + "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, + "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) + { + return; + } // clang-format on asyncResp->res.result(boost::beast::http::status::no_content); @@ -3373,7 +3394,7 @@ inline void afterGetAllowedHostTransitions( nlohmann::json::object_t parameter; parameter["Name"] = "ResetType"; parameter["Required"] = true; - parameter["DataType"] = "String"; + parameter["DataType"] = action_info::ParameterTypes::String; parameter["AllowableValues"] = std::move(allowableValues); nlohmann::json::array_t parameters; parameters.emplace_back(std::move(parameter)); @@ -3397,10 +3418,13 @@ inline void handleSystemCollectionResetActionGet( return; } - if (systemName == "hypervisor") + if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM) { - handleHypervisorResetActionGet(asyncResp); - return; + if (systemName == "hypervisor") + { + handleHypervisorResetActionGet(asyncResp); + return; + } } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) @@ -3428,8 +3452,9 @@ inline void handleSystemCollectionResetActionGet( "AllowedHostTransitions", [asyncResp](const boost::system::error_code& ec, const std::vector<std::string>& allowedHostTransitions) { - afterGetAllowedHostTransitions(asyncResp, ec, allowedHostTransitions); - }); + afterGetAllowedHostTransitions(asyncResp, ec, + allowedHostTransitions); + }); } /** * SystemResetActionInfo derived class for delivering Computer Systems diff --git a/redfish-core/lib/systems_logservices_hostlogger.hpp b/redfish-core/lib/systems_logservices_hostlogger.hpp new file mode 100644 index 0000000000..98be6f3ac2 --- /dev/null +++ b/redfish-core/lib/systems_logservices_hostlogger.hpp @@ -0,0 +1,241 @@ +#pragma once + +#include "app.hpp" +#include "generated/enums/log_entry.hpp" +#include "query.hpp" +#include "registries/openbmc_message_registry.hpp" +#include "registries/privilege_registry.hpp" +#include "utils/time_utils.hpp" + +#include <cstdint> +#include <memory> +#include <string_view> +#include <utility> +#include <vector> + +namespace redfish +{ + +inline void fillHostLoggerEntryJson(std::string_view logEntryID, + std::string_view msg, + nlohmann::json::object_t& logEntryJson) +{ + // Fill in the log entry with the gathered data. + logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + logEntryJson["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); + logEntryJson["Name"] = "Host Logger Entry"; + logEntryJson["Id"] = logEntryID; + logEntryJson["Message"] = msg; + logEntryJson["EntryType"] = log_entry::LogEntryType::Oem; + logEntryJson["Severity"] = log_entry::EventSeverity::OK; + logEntryJson["OemRecordFormat"] = "Host Logger Entry"; +} + +inline void handleSystemsLogServicesHostloggerGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["Name"] = "Host Logger Service"; + asyncResp->res.jsonValue["Description"] = "Host Logger Service"; + asyncResp->res.jsonValue["Id"] = "HostLogger"; + asyncResp->res.jsonValue["Entries"]["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); +} + +inline void handleSystemsLogServicesHostloggerEntriesGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + query_param::QueryCapabilities capabilities = { + .canDelegateTop = true, + .canDelegateSkip = true, + }; + query_param::Query delegatedQuery; + if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, + delegatedQuery, capabilities)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["Name"] = "HostLogger Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of HostLogger Entries"; + nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; + logEntryArray = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + + std::vector<std::filesystem::path> hostLoggerFiles; + if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) + { + BMCWEB_LOG_DEBUG("Failed to get host log file path"); + return; + } + // If we weren't provided top and skip limits, use the defaults. + size_t skip = delegatedQuery.skip.value_or(0); + size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); + size_t logCount = 0; + // This vector only store the entries we want to expose that + // control by skip and top. + std::vector<std::string> logEntries; + if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries, logCount)) + { + messages::internalError(asyncResp->res); + return; + } + // If vector is empty, that means skip value larger than total + // log count + if (logEntries.empty()) + { + asyncResp->res.jsonValue["Members@odata.count"] = logCount; + return; + } + if (!logEntries.empty()) + { + for (size_t i = 0; i < logEntries.size(); i++) + { + nlohmann::json::object_t hostLogEntry; + fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i], + hostLogEntry); + logEntryArray.emplace_back(std::move(hostLogEntry)); + } + + asyncResp->res.jsonValue["Members@odata.count"] = logCount; + if (skip + top < logCount) + { + asyncResp->res.jsonValue["Members@odata.nextLink"] = + std::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries?$skip=", + BMCWEB_REDFISH_SYSTEM_URI_NAME) + + std::to_string(skip + top); + } + } +} + +inline void handleSystemsLogServicesHostloggerEntriesEntryGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& param) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + std::string_view targetID = param; + + uint64_t idInt = 0; + + auto [ptr, ec] = std::from_chars(targetID.begin(), targetID.end(), idInt); + if (ec != std::errc{} || ptr != targetID.end()) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", param); + return; + } + + std::vector<std::filesystem::path> hostLoggerFiles; + if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) + { + BMCWEB_LOG_DEBUG("Failed to get host log file path"); + return; + } + + size_t logCount = 0; + size_t top = 1; + std::vector<std::string> logEntries; + // We can get specific entry by skip and top. For example, if we + // want to get nth entry, we can set skip = n-1 and top = 1 to + // get that entry + if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries, + logCount)) + { + messages::internalError(asyncResp->res); + return; + } + + if (!logEntries.empty()) + { + nlohmann::json::object_t hostLogEntry; + fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry); + asyncResp->res.jsonValue.update(hostLogEntry); + return; + } + + // Requested ID was not found + messages::resourceNotFound(asyncResp->res, "LogEntry", param); +} + +inline void requestRoutesSystemsLogServiceHostlogger(App& app) +{ + BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/") + .privileges(redfish::privileges::getLogService) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesHostloggerGet, std::ref(app))); + BMCWEB_ROUTE(app, + "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/") + .privileges(redfish::privileges::getLogEntryCollection) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesHostloggerEntriesGet, std::ref(app))); + + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/<str>/LogServices/HostLogger/Entries/<str>/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesHostloggerEntriesEntryGet, std::ref(app))); +} + +} // namespace redfish diff --git a/redfish-core/lib/systems_logservices_postcodes.hpp b/redfish-core/lib/systems_logservices_postcodes.hpp new file mode 100644 index 0000000000..90782bde51 --- /dev/null +++ b/redfish-core/lib/systems_logservices_postcodes.hpp @@ -0,0 +1,615 @@ +#pragma once + +#include "app.hpp" +#include "generated/enums/log_service.hpp" +#include "query.hpp" +#include "registries/openbmc_message_registry.hpp" +#include "registries/privilege_registry.hpp" +#include "utils/time_utils.hpp" + +#include <cstdint> +#include <memory> +#include <string_view> +#include <utility> +#include <vector> + +namespace redfish +{ + +inline void handleSystemsLogServicesPostCodesGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/PostCodes", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; + asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; + asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; + asyncResp->res.jsonValue["Id"] = "PostCodes"; + asyncResp->res.jsonValue["OverWritePolicy"] = + log_service::OverWritePolicy::WrapsWhenFull; + asyncResp->res.jsonValue["Entries"]["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + + std::pair<std::string, std::string> redfishDateTimeOffset = + redfish::time_utils::getDateTimeOffsetNow(); + asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; + asyncResp->res.jsonValue["DateTimeLocalOffset"] = + redfishDateTimeOffset.second; + + asyncResp->res + .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = std::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog", + BMCWEB_REDFISH_SYSTEM_URI_NAME); +} + +inline void handleSystemsLogServicesPostCodesPost( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); + + // Make call to post-code service to request clear all + crow::connections::systemBus->async_method_call( + [asyncResp](const boost::system::error_code& ec) { + if (ec) + { + // TODO Handle for specific error code + BMCWEB_LOG_ERROR("doClearPostCodes resp_handler got error {}", + ec); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + messages::internalError(asyncResp->res); + return; + } + messages::success(asyncResp->res); + }, + "xyz.openbmc_project.State.Boot.PostCode0", + "/xyz/openbmc_project/State/Boot/PostCode0", + "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); +} + +/** + * @brief Parse post code ID and get the current value and index value + * eg: postCodeID=B1-2, currentValue=1, index=2 + * + * @param[in] postCodeID Post Code ID + * @param[out] currentValue Current value + * @param[out] index Index value + * + * @return bool true if the parsing is successful, false the parsing fails + */ +inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue, + uint16_t& index) +{ + std::vector<std::string> split; + bmcweb::split(split, postCodeID, '-'); + if (split.size() != 2) + { + return false; + } + std::string_view postCodeNumber = split[0]; + if (postCodeNumber.size() < 2) + { + return false; + } + if (postCodeNumber[0] != 'B') + { + return false; + } + postCodeNumber.remove_prefix(1); + auto [ptrIndex, ecIndex] = + std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index); + if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc()) + { + return false; + } + + std::string_view postCodeIndex = split[1]; + + auto [ptrValue, ecValue] = std::from_chars( + postCodeIndex.begin(), postCodeIndex.end(), currentValue); + + return ptrValue == postCodeIndex.end() && ecValue == std::errc(); +} + +static bool fillPostCodeEntry( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const boost::container::flat_map< + uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode, + const uint16_t bootIndex, const uint64_t codeIndex = 0, + const uint64_t skip = 0, const uint64_t top = 0) +{ + // Get the Message from the MessageRegistry + const registries::Message* message = + registries::getMessage("OpenBMC.0.2.BIOSPOSTCode"); + if (message == nullptr) + { + BMCWEB_LOG_ERROR("Couldn't find known message?"); + return false; + } + uint64_t currentCodeIndex = 0; + uint64_t firstCodeTimeUs = 0; + for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& + code : postcode) + { + currentCodeIndex++; + std::string postcodeEntryID = + "B" + std::to_string(bootIndex) + "-" + + std::to_string(currentCodeIndex); // 1 based index in EntryID string + + uint64_t usecSinceEpoch = code.first; + uint64_t usTimeOffset = 0; + + if (1 == currentCodeIndex) + { // already incremented + firstCodeTimeUs = code.first; + } + else + { + usTimeOffset = code.first - firstCodeTimeUs; + } + + // skip if no specific codeIndex is specified and currentCodeIndex does + // not fall between top and skip + if ((codeIndex == 0) && + (currentCodeIndex <= skip || currentCodeIndex > top)) + { + continue; + } + + // skip if a specific codeIndex is specified and does not match the + // currentIndex + if ((codeIndex > 0) && (currentCodeIndex != codeIndex)) + { + // This is done for simplicity. 1st entry is needed to calculate + // time offset. To improve efficiency, one can get to the entry + // directly (possibly with flatmap's nth method) + continue; + } + + // currentCodeIndex is within top and skip or equal to specified code + // index + + // Get the Created time from the timestamp + std::string entryTimeStr; + entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch); + + // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex) + std::ostringstream hexCode; + hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex + << std::get<0>(code.second); + std::ostringstream timeOffsetStr; + // Set Fixed -Point Notation + timeOffsetStr << std::fixed; + // Set precision to 4 digits + timeOffsetStr << std::setprecision(4); + // Add double to stream + timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; + + std::string bootIndexStr = std::to_string(bootIndex); + std::string timeOffsetString = timeOffsetStr.str(); + std::string hexCodeStr = hexCode.str(); + + std::array<std::string_view, 3> messageArgs = { + bootIndexStr, timeOffsetString, hexCodeStr}; + + std::string msg = + redfish::registries::fillMessageArgs(messageArgs, message->message); + if (msg.empty()) + { + messages::internalError(asyncResp->res); + return false; + } + + // Get Severity template from message registry + std::string severity; + if (message != nullptr) + { + severity = message->messageSeverity; + } + + // Format entry + nlohmann::json::object_t bmcLogEntry; + bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; + bmcLogEntry["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID); + bmcLogEntry["Name"] = "POST Code Log Entry"; + bmcLogEntry["Id"] = postcodeEntryID; + bmcLogEntry["Message"] = std::move(msg); + bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode"; + bmcLogEntry["MessageArgs"] = messageArgs; + bmcLogEntry["EntryType"] = "Event"; + bmcLogEntry["Severity"] = std::move(severity); + bmcLogEntry["Created"] = entryTimeStr; + if (!std::get<std::vector<uint8_t>>(code.second).empty()) + { + bmcLogEntry["AdditionalDataURI"] = + std::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/", + BMCWEB_REDFISH_SYSTEM_URI_NAME) + + postcodeEntryID + "/attachment"; + } + + // codeIndex is only specified when querying single entry, return only + // that entry in this case + if (codeIndex != 0) + { + asyncResp->res.jsonValue.update(bmcLogEntry); + return true; + } + + nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"]; + logEntryArray.emplace_back(std::move(bmcLogEntry)); + } + + // Return value is always false when querying multiple entries + return false; +} + +inline void + getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& entryId) +{ + uint16_t bootIndex = 0; + uint64_t codeIndex = 0; + if (!parsePostCode(entryId, codeIndex, bootIndex)) + { + // Requested ID was not found + messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); + return; + } + + if (bootIndex == 0 || codeIndex == 0) + { + // 0 is an invalid index + messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); + return; + } + + crow::connections::systemBus->async_method_call( + [asyncResp, entryId, bootIndex, + codeIndex](const boost::system::error_code& ec, + const boost::container::flat_map< + uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& + postcode) { + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); + messages::internalError(asyncResp->res); + return; + } + + if (postcode.empty()) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); + return; + } + + if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex)) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); + return; + } + }, + "xyz.openbmc_project.State.Boot.PostCode0", + "/xyz/openbmc_project/State/Boot/PostCode0", + "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", + bootIndex); +} + +inline void + getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const uint16_t bootIndex, const uint16_t bootCount, + const uint64_t entryCount, size_t skip, size_t top) +{ + crow::connections::systemBus->async_method_call( + [asyncResp, bootIndex, bootCount, entryCount, skip, + top](const boost::system::error_code& ec, + const boost::container::flat_map< + uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& + postcode) { + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error"); + messages::internalError(asyncResp->res); + return; + } + + uint64_t endCount = entryCount; + if (!postcode.empty()) + { + endCount = entryCount + postcode.size(); + if (skip < endCount && (top + skip) > entryCount) + { + uint64_t thisBootSkip = + std::max(static_cast<uint64_t>(skip), entryCount) - + entryCount; + uint64_t thisBootTop = + std::min(static_cast<uint64_t>(top + skip), endCount) - + entryCount; + + fillPostCodeEntry(asyncResp, postcode, bootIndex, 0, + thisBootSkip, thisBootTop); + } + asyncResp->res.jsonValue["Members@odata.count"] = endCount; + } + + // continue to previous bootIndex + if (bootIndex < bootCount) + { + getPostCodeForBoot(asyncResp, + static_cast<uint16_t>(bootIndex + 1), + bootCount, endCount, skip, top); + } + else if (skip + top < endCount) + { + asyncResp->res.jsonValue["Members@odata.nextLink"] = + std::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=", + BMCWEB_REDFISH_SYSTEM_URI_NAME) + + std::to_string(skip + top); + } + }, + "xyz.openbmc_project.State.Boot.PostCode0", + "/xyz/openbmc_project/State/Boot/PostCode0", + "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", + bootIndex); +} + +inline void + getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + size_t skip, size_t top) +{ + uint64_t entryCount = 0; + sdbusplus::asio::getProperty<uint16_t>( + *crow::connections::systemBus, + "xyz.openbmc_project.State.Boot.PostCode0", + "/xyz/openbmc_project/State/Boot/PostCode0", + "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount", + [asyncResp, entryCount, skip, + top](const boost::system::error_code& ec, const uint16_t bootCount) { + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } + getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top); + }); +} + +inline void handleSystemsLogServicesPostCodesEntriesGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName) +{ + query_param::QueryCapabilities capabilities = { + .canDelegateTop = true, + .canDelegateSkip = true, + }; + query_param::Query delegatedQuery; + if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, + delegatedQuery, capabilities)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#LogEntryCollection.LogEntryCollection"; + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; + asyncResp->res.jsonValue["Description"] = + "Collection of POST Code Log Entries"; + asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + size_t skip = delegatedQuery.skip.value_or(0); + size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); + getCurrentBootNumber(asyncResp, skip, top); +} + +inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& postCodeID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (!http_helpers::isContentTypeAllowed( + req.getHeaderValue("Accept"), + http_helpers::ContentType::OctetStream, true)) + { + asyncResp->res.result(boost::beast::http::status::bad_request); + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + uint64_t currentValue = 0; + uint16_t index = 0; + if (!parsePostCode(postCodeID, currentValue, index)) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID); + return; + } + + crow::connections::systemBus->async_method_call( + [asyncResp, postCodeID, currentValue]( + const boost::system::error_code& ec, + const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>& + postcodes) { + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", + postCodeID); + return; + } + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error {}", ec); + messages::internalError(asyncResp->res); + return; + } + + size_t value = static_cast<size_t>(currentValue) - 1; + if (value == std::string::npos || postcodes.size() < currentValue) + { + BMCWEB_LOG_WARNING("Wrong currentValue value"); + messages::resourceNotFound(asyncResp->res, "LogEntry", + postCodeID); + return; + } + + const auto& [tID, c] = postcodes[value]; + if (c.empty()) + { + BMCWEB_LOG_WARNING("No found post code data"); + messages::resourceNotFound(asyncResp->res, "LogEntry", + postCodeID); + return; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const char* d = reinterpret_cast<const char*>(c.data()); + std::string_view strData(d, c.size()); + + asyncResp->res.addHeader(boost::beast::http::field::content_type, + "application/octet-stream"); + asyncResp->res.addHeader( + boost::beast::http::field::content_transfer_encoding, "Base64"); + asyncResp->res.write(crow::utility::base64encode(strData)); + }, + "xyz.openbmc_project.State.Boot.PostCode0", + "/xyz/openbmc_project/State/Boot/PostCode0", + "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index); +} + +inline void handleSystemsLogServicesPostCodesEntriesEntryGet( + App& app, const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& systemName, const std::string& targetID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Option currently returns no systems. TBD + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + + getPostCodeForEntry(asyncResp, targetID); +} + +inline void requestRoutesSystemsLogServicesPostCode(App& app) +{ + BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/") + .privileges(redfish::privileges::getLogService) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesPostCodesGet, std::ref(app))); + + BMCWEB_ROUTE( + app, + "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/") + // The following privilege is correct; we need "SubordinateOverrides" + // before we can automate it. + .privileges({{"ConfigureComponents"}}) + .methods(boost::beast::http::verb::post)(std::bind_front( + handleSystemsLogServicesPostCodesPost, std::ref(app))); + + BMCWEB_ROUTE(app, + "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/") + .privileges(redfish::privileges::getLogEntryCollection) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesPostCodesEntriesGet, std::ref(app))); + + BMCWEB_ROUTE( + app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesPostCodesEntriesEntryGet, std::ref(app))); + + BMCWEB_ROUTE( + app, + "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/") + .privileges(redfish::privileges::getLogEntry) + .methods(boost::beast::http::verb::get)(std::bind_front( + handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet, + std::ref(app))); +} +} // namespace redfish diff --git a/redfish-core/lib/task.hpp b/redfish-core/lib/task.hpp index a2959b8455..5f67cf0448 100644 --- a/redfish-core/lib/task.hpp +++ b/redfish-core/lib/task.hpp @@ -1,23 +1,25 @@ /* -// Copyright (c) 2020 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2020 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include "app.hpp" #include "dbus_utility.hpp" #include "event_service_manager.hpp" +#include "generated/enums/resource.hpp" +#include "generated/enums/task_service.hpp" #include "http/parsing.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -72,8 +74,8 @@ struct Payload continue; } std::string header; - header.reserve(field.name_string().size() + 2 + - field.value().size()); + header.reserve( + field.name_string().size() + 2 + field.value().size()); header += field.name_string(); header += ": "; header += field.value(); @@ -95,8 +97,7 @@ struct TaskData : std::enable_shared_from_this<TaskData> std::function<bool(boost::system::error_code, sdbusplus::message_t&, const std::shared_ptr<TaskData>&)>&& handler, const std::string& matchIn, size_t idx) : - callback(std::move(handler)), - matchStr(matchIn), index(idx), + callback(std::move(handler)), matchStr(matchIn), index(idx), startTime(std::chrono::system_clock::to_time_t( std::chrono::system_clock::now())), status("OK"), state("Running"), messages(nlohmann::json::array()), @@ -144,7 +145,8 @@ struct TaskData : std::enable_shared_from_this<TaskData> { res.result(boost::beast::http::status::accepted); std::string strIdx = std::to_string(index); - std::string uri = "/redfish/v1/TaskService/Tasks/" + strIdx; + boost::urls::url uri = + boost::urls::format("/redfish/v1/TaskService/Tasks/{}", strIdx); res.jsonValue["@odata.id"] = uri; res.jsonValue["@odata.type"] = "#Task.v1_4_3.Task"; @@ -152,8 +154,11 @@ struct TaskData : std::enable_shared_from_this<TaskData> res.jsonValue["TaskState"] = state; res.jsonValue["TaskStatus"] = status; + boost::urls::url taskMonitor = boost::urls::format( + "/redfish/v1/TaskService/TaskMonitors/{}", strIdx); + res.addHeader(boost::beast::http::field::location, - uri + "/Monitor"); + taskMonitor.buffer()); res.addHeader(boost::beast::http::field::retry_after, std::to_string(retryAfterSeconds)); } @@ -175,33 +180,30 @@ struct TaskData : std::enable_shared_from_this<TaskData> timer.expires_after(timeout); timer.async_wait( [self = shared_from_this()](boost::system::error_code ec) { - if (ec == boost::asio::error::operation_aborted) - { - return; // completed successfully - } - if (!ec) - { - // change ec to error as timer expired - ec = boost::asio::error::operation_aborted; - } - self->match.reset(); - sdbusplus::message_t msg; - self->finishTask(); - self->state = "Cancelled"; - self->status = "Warning"; - self->messages.emplace_back( - messages::taskAborted(std::to_string(self->index))); - // Send event :TaskAborted - self->sendTaskEvent(self->state, self->index); - self->callback(ec, msg, self); - }); + if (ec == boost::asio::error::operation_aborted) + { + return; // completed successfully + } + if (!ec) + { + // change ec to error as timer expired + ec = boost::asio::error::operation_aborted; + } + self->match.reset(); + sdbusplus::message_t msg; + self->finishTask(); + self->state = "Cancelled"; + self->status = "Warning"; + self->messages.emplace_back( + messages::taskAborted(std::to_string(self->index))); + // Send event :TaskAborted + self->sendTaskEvent(self->state, self->index); + self->callback(ec, msg, self); + }); } static void sendTaskEvent(std::string_view state, size_t index) { - std::string origin = "/redfish/v1/TaskService/Tasks/" + - std::to_string(index); - std::string resType = "Task"; // TaskState enums which should send out an event are: // "Starting" = taskResumed // "Running" = taskStarted @@ -213,59 +215,50 @@ struct TaskData : std::enable_shared_from_this<TaskData> // "Killed" = taskRemoved // "Exception" = taskCompletedWarning // "Cancelled" = taskCancelled + nlohmann::json event; + std::string indexStr = std::to_string(index); if (state == "Starting") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskResumed(std::to_string(index)), origin, - resType); + event = redfish::messages::taskResumed(indexStr); } else if (state == "Running") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskStarted(std::to_string(index)), origin, - resType); + event = redfish::messages::taskStarted(indexStr); } else if ((state == "Suspended") || (state == "Interrupted") || (state == "Pending")) { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskPaused(std::to_string(index)), origin, - resType); + event = redfish::messages::taskPaused(indexStr); } else if (state == "Stopping") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskAborted(std::to_string(index)), origin, - resType); + event = redfish::messages::taskAborted(indexStr); } else if (state == "Completed") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskCompletedOK(std::to_string(index)), - origin, resType); + event = redfish::messages::taskCompletedOK(indexStr); } else if (state == "Killed") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskRemoved(std::to_string(index)), origin, - resType); + event = redfish::messages::taskRemoved(indexStr); } else if (state == "Exception") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskCompletedWarning(std::to_string(index)), - origin, resType); + event = redfish::messages::taskCompletedWarning(indexStr); } else if (state == "Cancelled") { - redfish::EventServiceManager::getInstance().sendEvent( - redfish::messages::taskCancelled(std::to_string(index)), origin, - resType); + event = redfish::messages::taskCancelled(indexStr); } else { BMCWEB_LOG_INFO("sendTaskEvent: No events to send"); + return; } + boost::urls::url origin = + boost::urls::format("/redfish/v1/TaskService/Tasks/{}", index); + EventServiceManager::getInstance().sendEvent(event, origin.buffer(), + "Task"); } void startTimer(const std::chrono::seconds& timeout) @@ -278,25 +271,25 @@ struct TaskData : std::enable_shared_from_this<TaskData> static_cast<sdbusplus::bus_t&>(*crow::connections::systemBus), matchStr, [self = shared_from_this()](sdbusplus::message_t& message) { - boost::system::error_code ec; - - // callback to return True if callback is done, callback needs - // to update status itself if needed - if (self->callback(ec, message, self) == task::completed) - { - self->timer.cancel(); - self->finishTask(); - - // Send event - self->sendTaskEvent(self->state, self->index); - - // reset the match after the callback was successful - boost::asio::post( - crow::connections::systemBus->get_io_context(), - [self] { self->match.reset(); }); - return; - } - }); + boost::system::error_code ec; + + // callback to return True if callback is done, callback needs + // to update status itself if needed + if (self->callback(ec, message, self) == task::completed) + { + self->timer.cancel(); + self->finishTask(); + + // Send event + self->sendTaskEvent(self->state, self->index); + + // reset the match after the callback was successful + boost::asio::post( + crow::connections::systemBus->get_io_context(), + [self] { self->match.reset(); }); + return; + } + }); extendTimer(timeout); messages.emplace_back(messages::taskStarted(std::to_string(index))); @@ -325,43 +318,45 @@ struct TaskData : std::enable_shared_from_this<TaskData> inline void requestRoutesTaskMonitor(App& app) { - BMCWEB_ROUTE(app, "/redfish/v1/TaskService/Tasks/<str>/Monitor/") + BMCWEB_ROUTE(app, "/redfish/v1/TaskService/TaskMonitors/<str>/") .privileges(redfish::privileges::getTask) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& strParam) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - auto find = std::ranges::find_if( - task::tasks, - [&strParam](const std::shared_ptr<task::TaskData>& task) { - if (!task) - { - return false; - } - - // we compare against the string version as on failure - // strtoul returns 0 - return std::to_string(task->index) == strParam; - }); - - if (find == task::tasks.end()) - { - messages::resourceNotFound(asyncResp->res, "Task", strParam); - return; - } - std::shared_ptr<task::TaskData>& ptr = *find; - // monitor expires after 204 - if (ptr->gave204) - { - messages::resourceNotFound(asyncResp->res, "Task", strParam); - return; - } - ptr->populateResp(asyncResp->res); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + auto find = std::ranges::find_if( + task::tasks, + [&strParam](const std::shared_ptr<task::TaskData>& task) { + if (!task) + { + return false; + } + + // we compare against the string version as on failure + // strtoul returns 0 + return std::to_string(task->index) == strParam; + }); + + if (find == task::tasks.end()) + { + messages::resourceNotFound(asyncResp->res, "Task", + strParam); + return; + } + std::shared_ptr<task::TaskData>& ptr = *find; + // monitor expires after 204 + if (ptr->gave204) + { + messages::resourceNotFound(asyncResp->res, "Task", + strParam); + return; + } + ptr->populateResp(asyncResp->res); + }); } inline void requestRoutesTask(App& app) @@ -372,66 +367,75 @@ inline void requestRoutesTask(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& strParam) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - auto find = std::ranges::find_if( - task::tasks, - [&strParam](const std::shared_ptr<task::TaskData>& task) { - if (!task) - { - return false; - } - - // we compare against the string version as on failure - // strtoul returns 0 - return std::to_string(task->index) == strParam; - }); - - if (find == task::tasks.end()) - { - messages::resourceNotFound(asyncResp->res, "Task", strParam); - return; - } - - const std::shared_ptr<task::TaskData>& ptr = *find; - - asyncResp->res.jsonValue["@odata.type"] = "#Task.v1_4_3.Task"; - asyncResp->res.jsonValue["Id"] = strParam; - asyncResp->res.jsonValue["Name"] = "Task " + strParam; - asyncResp->res.jsonValue["TaskState"] = ptr->state; - asyncResp->res.jsonValue["StartTime"] = - redfish::time_utils::getDateTimeStdtime(ptr->startTime); - if (ptr->endTime) - { - asyncResp->res.jsonValue["EndTime"] = - redfish::time_utils::getDateTimeStdtime(*(ptr->endTime)); - } - asyncResp->res.jsonValue["TaskStatus"] = ptr->status; - asyncResp->res.jsonValue["Messages"] = ptr->messages; - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/TaskService/Tasks/{}", strParam); - if (!ptr->gave204) - { - asyncResp->res.jsonValue["TaskMonitor"] = - "/redfish/v1/TaskService/Tasks/" + strParam + "/Monitor"; - } - - asyncResp->res.jsonValue["HidePayload"] = !ptr->payload; - - if (ptr->payload) - { - const task::Payload& p = *(ptr->payload); - asyncResp->res.jsonValue["Payload"]["TargetUri"] = p.targetUri; - asyncResp->res.jsonValue["Payload"]["HttpOperation"] = - p.httpOperation; - asyncResp->res.jsonValue["Payload"]["HttpHeaders"] = p.httpHeaders; - asyncResp->res.jsonValue["Payload"]["JsonBody"] = p.jsonBody.dump( - 2, ' ', true, nlohmann::json::error_handler_t::replace); - } - asyncResp->res.jsonValue["PercentComplete"] = ptr->percentComplete; - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + auto find = std::ranges::find_if( + task::tasks, + [&strParam](const std::shared_ptr<task::TaskData>& task) { + if (!task) + { + return false; + } + + // we compare against the string version as on failure + // strtoul returns 0 + return std::to_string(task->index) == strParam; + }); + + if (find == task::tasks.end()) + { + messages::resourceNotFound(asyncResp->res, "Task", + strParam); + return; + } + + const std::shared_ptr<task::TaskData>& ptr = *find; + + asyncResp->res.jsonValue["@odata.type"] = "#Task.v1_4_3.Task"; + asyncResp->res.jsonValue["Id"] = strParam; + asyncResp->res.jsonValue["Name"] = "Task " + strParam; + asyncResp->res.jsonValue["TaskState"] = ptr->state; + asyncResp->res.jsonValue["StartTime"] = + redfish::time_utils::getDateTimeStdtime(ptr->startTime); + if (ptr->endTime) + { + asyncResp->res.jsonValue["EndTime"] = + redfish::time_utils::getDateTimeStdtime( + *(ptr->endTime)); + } + asyncResp->res.jsonValue["TaskStatus"] = ptr->status; + asyncResp->res.jsonValue["Messages"] = ptr->messages; + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/TaskService/Tasks/{}", strParam); + if (!ptr->gave204) + { + asyncResp->res.jsonValue["TaskMonitor"] = + boost::urls::format( + "/redfish/v1/TaskService/TaskMonitors/{}", + strParam); + } + + asyncResp->res.jsonValue["HidePayload"] = !ptr->payload; + + if (ptr->payload) + { + const task::Payload& p = *(ptr->payload); + asyncResp->res.jsonValue["Payload"]["TargetUri"] = + p.targetUri; + asyncResp->res.jsonValue["Payload"]["HttpOperation"] = + p.httpOperation; + asyncResp->res.jsonValue["Payload"]["HttpHeaders"] = + p.httpHeaders; + asyncResp->res.jsonValue["Payload"]["JsonBody"] = + p.jsonBody.dump( + -1, ' ', true, + nlohmann::json::error_handler_t::replace); + } + asyncResp->res.jsonValue["PercentComplete"] = + ptr->percentComplete; + }); } inline void requestRoutesTaskCollection(App& app) @@ -441,31 +445,33 @@ inline void requestRoutesTaskCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#TaskCollection.TaskCollection"; - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/TaskService/Tasks"; - asyncResp->res.jsonValue["Name"] = "Task Collection"; - asyncResp->res.jsonValue["Members@odata.count"] = task::tasks.size(); - nlohmann::json& members = asyncResp->res.jsonValue["Members"]; - members = nlohmann::json::array(); - - for (const std::shared_ptr<task::TaskData>& task : task::tasks) - { - if (task == nullptr) - { - continue; // shouldn't be possible - } - nlohmann::json::object_t member; - member["@odata.id"] = - boost::urls::format("/redfish/v1/TaskService/Tasks/{}", - std::to_string(task->index)); - members.emplace_back(std::move(member)); - } - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#TaskCollection.TaskCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/TaskService/Tasks"; + asyncResp->res.jsonValue["Name"] = "Task Collection"; + asyncResp->res.jsonValue["Members@odata.count"] = + task::tasks.size(); + nlohmann::json& members = asyncResp->res.jsonValue["Members"]; + members = nlohmann::json::array(); + + for (const std::shared_ptr<task::TaskData>& task : task::tasks) + { + if (task == nullptr) + { + continue; // shouldn't be possible + } + nlohmann::json::object_t member; + member["@odata.id"] = + boost::urls::format("/redfish/v1/TaskService/Tasks/{}", + std::to_string(task->index)); + members.emplace_back(std::move(member)); + } + }); } inline void requestRoutesTaskService(App& app) @@ -475,26 +481,30 @@ inline void requestRoutesTaskService(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#TaskService.v1_1_4.TaskService"; - asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/TaskService"; - asyncResp->res.jsonValue["Name"] = "Task Service"; - asyncResp->res.jsonValue["Id"] = "TaskService"; - asyncResp->res.jsonValue["DateTime"] = - redfish::time_utils::getDateTimeOffsetNow().first; - asyncResp->res.jsonValue["CompletedTaskOverWritePolicy"] = "Oldest"; - - asyncResp->res.jsonValue["LifeCycleEventOnTaskStateChange"] = true; - - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["ServiceEnabled"] = true; - asyncResp->res.jsonValue["Tasks"]["@odata.id"] = - "/redfish/v1/TaskService/Tasks"; - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#TaskService.v1_1_4.TaskService"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/TaskService"; + asyncResp->res.jsonValue["Name"] = "Task Service"; + asyncResp->res.jsonValue["Id"] = "TaskService"; + asyncResp->res.jsonValue["DateTime"] = + redfish::time_utils::getDateTimeOffsetNow().first; + asyncResp->res.jsonValue["CompletedTaskOverWritePolicy"] = + task_service::OverWritePolicy::Oldest; + + asyncResp->res.jsonValue["LifeCycleEventOnTaskStateChange"] = + true; + + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; + asyncResp->res.jsonValue["ServiceEnabled"] = true; + asyncResp->res.jsonValue["Tasks"]["@odata.id"] = + "/redfish/v1/TaskService/Tasks"; + }); } } // namespace redfish diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp index 724e8aad27..cd051dcc0c 100644 --- a/redfish-core/lib/telemetry_service.hpp +++ b/redfish-core/lib/telemetry_service.hpp @@ -2,6 +2,7 @@ #include "app.hpp" #include "dbus_utility.hpp" +#include "generated/enums/resource.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/dbus_utils.hpp" @@ -41,53 +42,55 @@ inline void handleTelemetryServiceGet( "xyz.openbmc_project.Telemetry.ReportManager", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& ret) { - if (ec == boost::system::errc::host_unreachable) - { - asyncResp->res.jsonValue["Status"]["State"] = "Absent"; - return; - } - if (ec) - { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - messages::internalError(asyncResp->res); - return; - } + if (ec == boost::system::errc::host_unreachable) + { + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Absent; + return; + } + if (ec) + { + BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); + messages::internalError(asyncResp->res); + return; + } - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Enabled; - const size_t* maxReports = nullptr; - const uint64_t* minInterval = nullptr; + const size_t* maxReports = nullptr; + const uint64_t* minInterval = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), ret, "MaxReports", maxReports, - "MinInterval", minInterval); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), ret, "MaxReports", maxReports, + "MinInterval", minInterval); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (maxReports != nullptr) - { - asyncResp->res.jsonValue["MaxReports"] = *maxReports; - } + if (maxReports != nullptr) + { + asyncResp->res.jsonValue["MaxReports"] = *maxReports; + } - if (minInterval != nullptr) - { - asyncResp->res.jsonValue["MinCollectionInterval"] = - time_utils::toDurationString(std::chrono::milliseconds( - static_cast<time_t>(*minInterval))); - } - nlohmann::json::array_t supportedCollectionFunctions; - supportedCollectionFunctions.emplace_back("Maximum"); - supportedCollectionFunctions.emplace_back("Minimum"); - supportedCollectionFunctions.emplace_back("Average"); - supportedCollectionFunctions.emplace_back("Summation"); + if (minInterval != nullptr) + { + asyncResp->res.jsonValue["MinCollectionInterval"] = + time_utils::toDurationString(std::chrono::milliseconds( + static_cast<time_t>(*minInterval))); + } + nlohmann::json::array_t supportedCollectionFunctions; + supportedCollectionFunctions.emplace_back("Maximum"); + supportedCollectionFunctions.emplace_back("Minimum"); + supportedCollectionFunctions.emplace_back("Average"); + supportedCollectionFunctions.emplace_back("Summation"); - asyncResp->res.jsonValue["SupportedCollectionFunctions"] = - std::move(supportedCollectionFunctions); - }); + asyncResp->res.jsonValue["SupportedCollectionFunctions"] = + std::move(supportedCollectionFunctions); + }); } inline void requestRoutesTelemetryService(App& app) diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp index 84bf37a04e..adcd4b995d 100644 --- a/redfish-core/lib/thermal.hpp +++ b/redfish-core/lib/thermal.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -20,6 +20,7 @@ #include "registries/privilege_registry.hpp" #include "sensors.hpp" #include "utils/json_utils.hpp" +#include "utils/sensor_utils.hpp" namespace redfish { @@ -32,18 +33,19 @@ inline void requestRoutesThermal(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& chassisName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( - asyncResp, chassisName, sensors::dbus::thermalPaths, - sensors::node::thermal); + auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( + asyncResp, chassisName, sensors::dbus::thermalPaths, + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::thermalNode)); - // TODO Need to get Chassis Redundancy information. - getChassisData(sensorAsyncResp); - }); + // TODO Need to get Chassis Redundancy information. + getChassisData(sensorAsyncResp); + }); BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Thermal/") .privileges(redfish::privileges::patchThermal) @@ -51,44 +53,48 @@ inline void requestRoutesThermal(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& chassisName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } - std::optional<std::vector<nlohmann::json::object_t>> - temperatureCollections; - std::optional<std::vector<nlohmann::json::object_t>> fanCollections; - std::unordered_map<std::string, std::vector<nlohmann::json::object_t>> - allCollections; + std::optional<std::vector<nlohmann::json::object_t>> + temperatureCollections; + std::optional<std::vector<nlohmann::json::object_t>> + fanCollections; + std::unordered_map<std::string, + std::vector<nlohmann::json::object_t>> + allCollections; - auto sensorsAsyncResp = std::make_shared<SensorsAsyncResp>( - asyncResp, chassisName, sensors::dbus::thermalPaths, - sensors::node::thermal); + auto sensorsAsyncResp = std::make_shared<SensorsAsyncResp>( + asyncResp, chassisName, sensors::dbus::thermalPaths, + sensor_utils::chassisSubNodeToString( + sensor_utils::ChassisSubNode::thermalNode)); - if (!json_util::readJsonPatch(req, sensorsAsyncResp->asyncResp->res, - "Temperatures", temperatureCollections, - "Fans", fanCollections)) - { - return; - } - if (!temperatureCollections && !fanCollections) - { - messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, - "Thermal", "Temperatures / Voltages"); - return; - } - if (temperatureCollections) - { - allCollections.emplace("Temperatures", - *std::move(temperatureCollections)); - } - if (fanCollections) - { - allCollections.emplace("Fans", *std::move(fanCollections)); - } - setSensorsOverride(sensorsAsyncResp, allCollections); - }); + if (!json_util::readJsonPatch( + req, sensorsAsyncResp->asyncResp->res, "Temperatures", + temperatureCollections, "Fans", fanCollections)) + { + return; + } + if (!temperatureCollections && !fanCollections) + { + messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, + "Thermal", + "Temperatures / Voltages"); + return; + } + if (temperatureCollections) + { + allCollections.emplace("Temperatures", + *std::move(temperatureCollections)); + } + if (fanCollections) + { + allCollections.emplace("Fans", *std::move(fanCollections)); + } + setSensorsOverride(sensorsAsyncResp, allCollections); + }); } } // namespace redfish diff --git a/redfish-core/lib/thermal_metrics.hpp b/redfish-core/lib/thermal_metrics.hpp index f2541182f7..da4da01858 100644 --- a/redfish-core/lib/thermal_metrics.hpp +++ b/redfish-core/lib/thermal_metrics.hpp @@ -48,15 +48,16 @@ inline void handleThermalMetricsHead( asyncResp, chassisId, [asyncResp, chassisId](const std::optional<std::string>& validChassisPath) { - if (!validChassisPath) - { - messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); - return; - } - asyncResp->res.addHeader( - boost::beast::http::field::link, - "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby"); - }); + if (!validChassisPath) + { + messages::resourceNotFound(asyncResp->res, "Chassis", + chassisId); + return; + } + asyncResp->res.addHeader( + boost::beast::http::field::link, + "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby"); + }); } inline void diff --git a/redfish-core/lib/thermal_subsystem.hpp b/redfish-core/lib/thermal_subsystem.hpp index c52f395686..5365cb5a15 100644 --- a/redfish-core/lib/thermal_subsystem.hpp +++ b/redfish-core/lib/thermal_subsystem.hpp @@ -1,6 +1,7 @@ #pragma once #include "app.hpp" +#include "generated/enums/resource.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -46,8 +47,8 @@ inline void doThermalSubsystemCollection( "/redfish/v1/Chassis/{}/ThermalSubsystem/ThermalMetrics", chassisId); - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; - asyncResp->res.jsonValue["Status"]["Health"] = "OK"; + asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; + asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; } inline void handleThermalSubsystemCollectionHead( diff --git a/redfish-core/lib/trigger.hpp b/redfish-core/lib/trigger.hpp index a6101a2a1d..d405d26d60 100644 --- a/redfish-core/lib/trigger.hpp +++ b/redfish-core/lib/trigger.hpp @@ -1,15 +1,16 @@ #pragma once #include "app.hpp" +#include "generated/enums/metric_definition.hpp" #include "generated/enums/resource.hpp" #include "generated/enums/triggers.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" -#include "sensors.hpp" #include "utility.hpp" #include "utils/collection.hpp" #include "utils/dbus_utils.hpp" #include "utils/json_utils.hpp" +#include "utils/sensor_utils.hpp" #include "utils/telemetry_utils.hpp" #include "utils/time_utils.hpp" @@ -321,9 +322,8 @@ struct NumericThresholds } }; -inline bool parseNumericThresholds(crow::Response& res, - NumericThresholds& numericThresholds, - Context& ctx) +inline bool parseNumericThresholds( + crow::Response& res, NumericThresholds& numericThresholds, Context& ctx) { std::vector<NumericThresholdParams> parsedParams; if (numericThresholds.upperCritical) @@ -567,7 +567,7 @@ inline bool parseMetricProperties(crow::Response& res, Context& ctx) } std::pair<std::string, std::string> split = - splitSensorNameAndType(sensorName); + redfish::sensor_utils::splitSensorNameAndType(sensorName); if (split.first.empty() || split.second.empty()) { messages::propertyValueIncorrect( @@ -575,8 +575,8 @@ inline bool parseMetricProperties(crow::Response& res, Context& ctx) return false; } - std::string sensorPath = "/xyz/openbmc_project/sensors/" + split.first + - '/' + split.second; + std::string sensorPath = + "/xyz/openbmc_project/sensors/" + split.first + '/' + split.second; ctx.sensors.emplace_back(sensorPath, uriStr); uriIdx++; @@ -900,7 +900,7 @@ inline bool fillTrigger( json["DiscreteTriggers"] = *discreteTriggers; json["DiscreteTriggerCondition"] = discreteTriggers->empty() ? "Changed" : "Specified"; - json["MetricType"] = "Discrete"; + json["MetricType"] = metric_definition::MetricType::Discrete; } else { @@ -916,7 +916,7 @@ inline bool fillTrigger( } json["NumericThresholds"] = *numericThresholds; - json["MetricType"] = "Numeric"; + json["MetricType"] = metric_definition::MetricType::Numeric; } } @@ -956,8 +956,8 @@ inline void handleTriggerCollectionPost( crow::connections::systemBus->async_method_call( [asyncResp, id = ctx.id](const boost::system::error_code& ec, const std::string& dbusPath) { - afterCreateTrigger(ec, dbusPath, asyncResp, id); - }, + afterCreateTrigger(ec, dbusPath, asyncResp, id); + }, service, "/xyz/openbmc_project/Telemetry/Triggers", "xyz.openbmc_project.Telemetry.TriggerManager", "AddTrigger", "TelemetryService/" + ctx.id, ctx.name, ctx.actions, ctx.sensors, @@ -973,23 +973,23 @@ inline void requestRoutesTriggerCollection(App& app) .methods(boost::beast::http::verb::get)( [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#TriggersCollection.TriggersCollection"; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/TelemetryService/Triggers"; - asyncResp->res.jsonValue["Name"] = "Triggers Collection"; - constexpr std::array<std::string_view, 1> interfaces{ - telemetry::triggerInterface}; - collection_util::getCollectionMembers( - asyncResp, - boost::urls::url("/redfish/v1/TelemetryService/Triggers"), - interfaces, - "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#TriggersCollection.TriggersCollection"; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/TelemetryService/Triggers"; + asyncResp->res.jsonValue["Name"] = "Triggers Collection"; + constexpr std::array<std::string_view, 1> interfaces{ + telemetry::triggerInterface}; + collection_util::getCollectionMembers( + asyncResp, + boost::urls::url("/redfish/v1/TelemetryService/Triggers"), + interfaces, + "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService"); + }); BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/") .privileges(redfish::privileges::postTriggersCollection) @@ -1005,36 +1005,40 @@ inline void requestRoutesTrigger(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - sdbusplus::asio::getAllProperties( - *crow::connections::systemBus, telemetry::service, - telemetry::getDbusTriggerPath(id), telemetry::triggerInterface, - [asyncResp, - id](const boost::system::error_code& ec, - const std::vector<std::pair< - std::string, telemetry::TriggerGetParamsVariant>>& ret) { - if (ec.value() == EBADR || - ec == boost::system::errc::host_unreachable) - { - messages::resourceNotFound(asyncResp->res, "Triggers", id); - return; - } - if (ec) - { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret)) - { - messages::internalError(asyncResp->res); - } - }); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + sdbusplus::asio::getAllProperties( + *crow::connections::systemBus, telemetry::service, + telemetry::getDbusTriggerPath(id), + telemetry::triggerInterface, + [asyncResp, + id](const boost::system::error_code& ec, + const std::vector<std::pair< + std::string, telemetry::TriggerGetParamsVariant>>& + ret) { + if (ec.value() == EBADR || + ec == boost::system::errc::host_unreachable) + { + messages::resourceNotFound(asyncResp->res, + "Triggers", id); + return; + } + if (ec) + { + BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); + messages::internalError(asyncResp->res); + return; + } + + if (!telemetry::fillTrigger(asyncResp->res.jsonValue, + id, ret)) + { + messages::internalError(asyncResp->res); + } + }); + }); BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/") .privileges(redfish::privileges::deleteTriggers) @@ -1042,32 +1046,35 @@ inline void requestRoutesTrigger(App& app) [&app](const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - const std::string triggerPath = telemetry::getDbusTriggerPath(id); - - crow::connections::systemBus->async_method_call( - [asyncResp, id](const boost::system::error_code& ec) { - if (ec.value() == EBADR) - { - messages::resourceNotFound(asyncResp->res, "Triggers", id); - return; - } - - if (ec) - { - BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); - messages::internalError(asyncResp->res); - return; - } - - asyncResp->res.result(boost::beast::http::status::no_content); - }, - telemetry::service, triggerPath, - "xyz.openbmc_project.Object.Delete", "Delete"); - }); + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + const std::string triggerPath = + telemetry::getDbusTriggerPath(id); + + crow::connections::systemBus->async_method_call( + [asyncResp, id](const boost::system::error_code& ec) { + if (ec.value() == EBADR) + { + messages::resourceNotFound(asyncResp->res, + "Triggers", id); + return; + } + + if (ec) + { + BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); + messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.result( + boost::beast::http::status::no_content); + }, + telemetry::service, triggerPath, + "xyz.openbmc_project.Object.Delete", "Delete"); + }); } } // namespace redfish diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp index 9a399affa4..eb25d1f8f6 100644 --- a/redfish-core/lib/update_service.hpp +++ b/redfish-core/lib/update_service.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -118,12 +118,12 @@ inline void activateImage(const std::string& objPath, "xyz.openbmc_project.Software.Activation", "RequestedActivation", "xyz.openbmc_project.Software.Activation.RequestedActivations.Active", [](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_DEBUG("error_code = {}", ec); - BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); - } - }); + if (ec) + { + BMCWEB_LOG_DEBUG("error_code = {}", ec); + BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); + } + }); } inline bool handleCreateTask(const boost::system::error_code& ec2, @@ -243,7 +243,7 @@ inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, // Note that asyncResp can be either a valid pointer or nullptr. If nullptr // then no asyncResp updates will occur -static void +inline void softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, sdbusplus::message_t& m, task::Payload&& payload) { @@ -270,40 +270,41 @@ static void const std::vector< std::pair<std::string, std::vector<std::string>>>& objInfo) mutable { - if (ec) - { - BMCWEB_LOG_DEBUG("error_code = {}", ec); - BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); - if (asyncResp) + if (ec) { - messages::internalError(asyncResp->res); + BMCWEB_LOG_DEBUG("error_code = {}", ec); + BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); + if (asyncResp) + { + messages::internalError(asyncResp->res); + } + cleanUp(); + return; } - cleanUp(); - return; - } - // Ensure we only got one service back - if (objInfo.size() != 1) - { - BMCWEB_LOG_ERROR("Invalid Object Size {}", objInfo.size()); - if (asyncResp) + // Ensure we only got one service back + if (objInfo.size() != 1) { - messages::internalError(asyncResp->res); + BMCWEB_LOG_ERROR("Invalid Object Size {}", + objInfo.size()); + if (asyncResp) + { + messages::internalError(asyncResp->res); + } + cleanUp(); + return; } - cleanUp(); - return; - } - // cancel timer only when - // xyz.openbmc_project.Software.Activation interface - // is added - fwAvailableTimer = nullptr; + // cancel timer only when + // xyz.openbmc_project.Software.Activation interface + // is added + fwAvailableTimer = nullptr; - activateImage(objPath.str, objInfo[0].first); - if (asyncResp) - { - createTask(asyncResp, std::move(payload), objPath); - } - fwUpdateInProgress = false; - }); + activateImage(objPath.str, objInfo[0].first); + if (asyncResp) + { + createTask(asyncResp, std::move(payload), objPath); + } + fwUpdateInProgress = false; + }); break; } @@ -485,10 +486,9 @@ inline void monitorForSoftwareAvailable( std::bind_front(afterUpdateErrorMatcher, asyncResp, url)); } -inline std::optional<boost::urls::url> - parseSimpleUpdateUrl(std::string imageURI, - std::optional<std::string> transferProtocol, - crow::Response& res) +inline std::optional<boost::urls::url> parseSimpleUpdateUrl( + std::string imageURI, std::optional<std::string> transferProtocol, + crow::Response& res) { if (imageURI.find("://") == std::string::npos) { @@ -574,61 +574,6 @@ inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, url.buffer()); } -inline void doTftpUpdate(const crow::Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const boost::urls::url_view_base& url) -{ - if (!BMCWEB_INSECURE_TFTP_UPDATE) - { - messages::actionParameterNotSupported(asyncResp->res, "ImageURI", - url.buffer()); - return; - } - - std::string path(url.encoded_path()); - if (path.size() < 2) - { - messages::actionParameterNotSupported(asyncResp->res, "ImageURI", - url.buffer()); - return; - } - // TFTP expects a path without a / - path.erase(0, 1); - std::string host(url.encoded_host_and_port()); - BMCWEB_LOG_DEBUG("Server: {} File: {}", host, path); - - // Setup callback for when new software detected - // Give TFTP 10 minutes to complete - monitorForSoftwareAvailable( - asyncResp, req, - "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate", 600); - - // TFTP can take up to 10 minutes depending on image size and - // connection speed. Return to caller as soon as the TFTP operation - // has been started. The callback above will ensure the activate - // is started once the download has completed - redfish::messages::success(asyncResp->res); - - // Call TFTP service - crow::connections::systemBus->async_method_call( - [](const boost::system::error_code& ec) { - if (ec) - { - // messages::internalError(asyncResp->res); - cleanUp(); - BMCWEB_LOG_DEBUG("error_code = {}", ec); - BMCWEB_LOG_DEBUG("error msg = {}", ec.message()); - } - else - { - BMCWEB_LOG_DEBUG("Call to DownloaViaTFTP Success"); - } - }, - "xyz.openbmc_project.Software.Download", - "/xyz/openbmc_project/software", "xyz.openbmc_project.Common.TFTP", - "DownloadViaTFTP", path, host); -} - inline void handleUpdateServiceSimpleUpdateAction( crow::App& app, const crow::Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) @@ -662,11 +607,7 @@ inline void handleUpdateServiceSimpleUpdateAction( { return; } - if (url->scheme() == "tftp") - { - doTftpUpdate(req, asyncResp, *url); - } - else if (url->scheme() == "https") + if (url->scheme() == "https") { doHttpsUpdate(asyncResp, *url); } @@ -688,8 +629,8 @@ inline void uploadImageFile(crow::Response& res, std::string_view body) std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); // set the permission of the file to 640 - std::filesystem::perms permission = std::filesystem::perms::owner_read | - std::filesystem::perms::group_read; + std::filesystem::perms permission = + std::filesystem::perms::owner_read | std::filesystem::perms::group_read; std::filesystem::permissions(filepath, permission); out << body; @@ -811,7 +752,11 @@ inline std::optional<MultiPartUpdateParameters> { std::vector<std::string> tempTargets; nlohmann::json content = - nlohmann::json::parse(formpart.content); + nlohmann::json::parse(formpart.content, nullptr, false); + if (content.is_discarded()) + { + return std::nullopt; + } nlohmann::json::object_t* obj = content.get_ptr<nlohmann::json::object_t*>(); if (obj == nullptr) @@ -872,11 +817,10 @@ inline std::optional<MultiPartUpdateParameters> return multiRet; } -inline void - handleStartUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - task::Payload payload, const std::string& objectPath, - const boost::system::error_code& ec, - const sdbusplus::message::object_path& retPath) +inline void handleStartUpdate( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, + const std::string& objectPath, const boost::system::error_code& ec, + const sdbusplus::message::object_path& retPath) { if (ec) { @@ -886,34 +830,37 @@ inline void return; } - BMCWEB_LOG_INFO("Call to StartUpdate Success, retPath = {}", retPath.str); - createTask(asyncResp, std::move(payload), objectPath); + BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}", + objectPath, retPath.str); + createTask(asyncResp, std::move(payload), retPath); } -inline void startUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - task::Payload payload, - const MemoryFileDescriptor& memfd, - const std::string& applyTime, - const std::string& objectPath, - const std::string& serviceName) +inline void startUpdate( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, + const MemoryFileDescriptor& memfd, const std::string& applyTime, + const std::string& objectPath, const std::string& serviceName) { crow::connections::systemBus->async_method_call( [asyncResp, payload = std::move(payload), objectPath](const boost::system::error_code& ec1, const sdbusplus::message::object_path& retPath) mutable { - handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, - retPath); - }, + handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1, + retPath); + }, serviceName, objectPath, "xyz.openbmc_project.Software.Update", "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime); } -inline void getAssociatedUpdateInterface( - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, - const MemoryFileDescriptor& memfd, const std::string& applyTime, - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreeResponse& subtree) +inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + task::Payload payload, const MemoryFileDescriptor& memfd, + const std::string& applyTime, const std::string& target, + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& subtree) { + using SwInfoMap = std::unordered_map< + std::string, std::pair<sdbusplus::message::object_path, std::string>>; + SwInfoMap swInfoMap; + if (ec) { BMCWEB_LOG_ERROR("error_code = {}", ec); @@ -921,35 +868,36 @@ inline void getAssociatedUpdateInterface( messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_DEBUG("Found {} startUpdate subtree paths", subtree.size()); + BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); - if (subtree.size() > 1) + for (const auto& entry : subtree) { - BMCWEB_LOG_ERROR("Found more than one startUpdate subtree paths"); - messages::internalError(asyncResp->res); + sdbusplus::message::object_path path(entry.first); + std::string swId = path.filename(); + swInfoMap.emplace(swId, make_pair(path, entry.second[0].first)); + } + + auto swEntry = swInfoMap.find(target); + if (swEntry == swInfoMap.end()) + { + BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); + messages::propertyValueFormatError(asyncResp->res, target, "Targets"); return; } - auto objectPath = subtree[0].first; - auto serviceName = subtree[0].second[0].first; + BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}", + swEntry->second.first.str, swEntry->second.second); - BMCWEB_LOG_DEBUG("Found objectPath {} serviceName {}", objectPath, - serviceName); - startUpdate(asyncResp, std::move(payload), memfd, applyTime, objectPath, - serviceName); + startUpdate(asyncResp, std::move(payload), memfd, applyTime, + swEntry->second.first.str, swEntry->second.second); } -inline void - getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - task::Payload payload, MemoryFileDescriptor memfd, - const std::string& applyTime, const std::string& target, - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& subtree) +inline void handleBMCUpdate( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload, + const MemoryFileDescriptor& memfd, const std::string& applyTime, + const boost::system::error_code& ec, + const dbus::utility::MapperEndPoints& functionalSoftware) { - using SwInfoMap = - std::unordered_map<std::string, sdbusplus::message::object_path>; - SwInfoMap swInfoMap; - if (ec) { BMCWEB_LOG_ERROR("error_code = {}", ec); @@ -957,47 +905,22 @@ inline void messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size()); - - for (const auto& objectPath : subtree) - { - sdbusplus::message::object_path path(objectPath); - std::string swId = path.filename(); - swInfoMap.emplace(swId, path); - } - - auto swEntry = swInfoMap.find(target); - if (swEntry == swInfoMap.end()) + if (functionalSoftware.size() != 1) { - BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target); - messages::propertyValueFormatError(asyncResp->res, target, "Targets"); + BMCWEB_LOG_ERROR("Found {} functional software endpoints", + functionalSoftware.size()); + messages::internalError(asyncResp->res); return; } - BMCWEB_LOG_DEBUG("Found software version path {}", swEntry->second.str); - - sdbusplus::message::object_path swObjectPath = swEntry->second / - "software_version"; - constexpr std::array<std::string_view, 1> interfaces = { - "xyz.openbmc_project.Software.Update"}; - dbus::utility::getAssociatedSubTree( - swObjectPath, - sdbusplus::message::object_path("/xyz/openbmc_project/software"), 0, - interfaces, - [asyncResp, payload = std::move(payload), memfd = std::move(memfd), - applyTime]( - const boost::system::error_code& ec1, - const dbus::utility::MapperGetSubTreeResponse& subtree1) mutable { - getAssociatedUpdateInterface(asyncResp, std::move(payload), memfd, - applyTime, ec1, subtree1); - }); + startUpdate(asyncResp, std::move(payload), memfd, applyTime, + functionalSoftware[0], "xyz.openbmc_project.Software.Manager"); } -inline void - processUpdateRequest(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - task::Payload&& payload, std::string_view body, - const std::string& applyTime, - std::vector<std::string>& targets) +inline void processUpdateRequest( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + task::Payload&& payload, std::string_view body, + const std::string& applyTime, std::vector<std::string>& targets) { MemoryFileDescriptor memfd("update-image"); if (memfd.fd == -1) @@ -1021,24 +944,29 @@ inline void if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME) { - startUpdate(asyncResp, std::move(payload), memfd, applyTime, - "/xyz/openbmc_project/software/bmc", - "xyz.openbmc_project.Software.Manager"); + dbus::utility::getAssociationEndPoints( + "/xyz/openbmc_project/software/bmc/functional", + [asyncResp, payload = std::move(payload), memfd = std::move(memfd), + applyTime]( + const boost::system::error_code& ec, + const dbus::utility::MapperEndPoints& objectPaths) mutable { + handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime, + ec, objectPaths); + }); } else { constexpr std::array<std::string_view, 1> interfaces = { "xyz.openbmc_project.Software.Version"}; - dbus::utility::getSubTreePaths( + dbus::utility::getSubTree( "/xyz/openbmc_project/software", 1, interfaces, [asyncResp, payload = std::move(payload), memfd = std::move(memfd), - applyTime, - targets](const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& - subtree) mutable { - getSwInfo(asyncResp, std::move(payload), std::move(memfd), - applyTime, targets[0], ec, subtree); - }); + applyTime, targets](const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreeResponse& + subtree) mutable { + getSwInfo(asyncResp, std::move(payload), memfd, applyTime, + targets[0], ec, subtree); + }); } } @@ -1176,8 +1104,8 @@ inline void asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; // Get the MaxImageSizeBytes - asyncResp->res.jsonValue["MaxImageSizeBytes"] = BMCWEB_HTTP_BODY_LIMIT * - 1024 * 1024; + asyncResp->res.jsonValue["MaxImageSizeBytes"] = + BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024; // Update Actions object. nlohmann::json& updateSvcSimpleUpdate = @@ -1196,8 +1124,9 @@ inline void updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] = std::move(allowed); - asyncResp->res.jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"] - ["ApplyTime"] = "Immediate"; + asyncResp->res + .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] = + update_service::ApplyTime::Immediate; } inline void handleUpdateServiceFirmwareInventoryCollectionGet( @@ -1263,65 +1192,65 @@ inline void [asyncResp, swId](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec) - { - messages::internalError(asyncResp->res); - return; - } + if (ec) + { + messages::internalError(asyncResp->res); + return; + } - const std::string* swInvPurpose = nullptr; - const std::string* version = nullptr; + const std::string* swInvPurpose = nullptr; + const std::string* version = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", - swInvPurpose, "Version", version); + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose", + swInvPurpose, "Version", version); - if (!success) - { - messages::internalError(asyncResp->res); - return; - } + if (!success) + { + messages::internalError(asyncResp->res); + return; + } - if (swInvPurpose == nullptr) - { - BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); - messages::internalError(asyncResp->res); - return; - } + if (swInvPurpose == nullptr) + { + BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!"); + messages::internalError(asyncResp->res); + return; + } - BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); + BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose); - if (version == nullptr) - { - BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); + if (version == nullptr) + { + BMCWEB_LOG_DEBUG("Can't find property \"Version\"!"); - messages::internalError(asyncResp->res); + messages::internalError(asyncResp->res); - return; - } - asyncResp->res.jsonValue["Version"] = *version; - asyncResp->res.jsonValue["Id"] = swId; - - // swInvPurpose is of format: - // xyz.openbmc_project.Software.Version.VersionPurpose.ABC - // Translate this to "ABC image" - size_t endDesc = swInvPurpose->rfind('.'); - if (endDesc == std::string::npos) - { - messages::internalError(asyncResp->res); - return; - } - endDesc++; - if (endDesc >= swInvPurpose->size()) - { - messages::internalError(asyncResp->res); - return; - } + return; + } + asyncResp->res.jsonValue["Version"] = *version; + asyncResp->res.jsonValue["Id"] = swId; + + // swInvPurpose is of format: + // xyz.openbmc_project.Software.Version.VersionPurpose.ABC + // Translate this to "ABC image" + size_t endDesc = swInvPurpose->rfind('.'); + if (endDesc == std::string::npos) + { + messages::internalError(asyncResp->res); + return; + } + endDesc++; + if (endDesc >= swInvPurpose->size()) + { + messages::internalError(asyncResp->res); + return; + } - std::string formatDesc = swInvPurpose->substr(endDesc); - asyncResp->res.jsonValue["Description"] = formatDesc + " image"; - getRelatedItems(asyncResp, *swInvPurpose); - }); + std::string formatDesc = swInvPurpose->substr(endDesc); + asyncResp->res.jsonValue["Description"] = formatDesc + " image"; + getRelatedItems(asyncResp, *swInvPurpose); + }); } inline void handleUpdateServiceFirmwareInventoryGet( @@ -1345,52 +1274,54 @@ inline void handleUpdateServiceFirmwareInventoryGet( [asyncResp, swId](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { - BMCWEB_LOG_DEBUG("doGet callback..."); - if (ec) - { - messages::internalError(asyncResp->res); - return; - } - - // Ensure we find our input swId, otherwise return an error - bool found = false; - for (const std::pair< - std::string, - std::vector<std::pair<std::string, std::vector<std::string>>>>& - obj : subtree) - { - if (!obj.first.ends_with(*swId)) + BMCWEB_LOG_DEBUG("doGet callback..."); + if (ec) { - continue; + messages::internalError(asyncResp->res); + return; } - if (obj.second.empty()) + // Ensure we find our input swId, otherwise return an error + bool found = false; + for (const std::pair<std::string, + std::vector<std::pair< + std::string, std::vector<std::string>>>>& + obj : subtree) { - continue; - } + if (!obj.first.ends_with(*swId)) + { + continue; + } - found = true; - sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); - getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, - *swId); - } - if (!found) - { - BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); - messages::resourceMissingAtURI( - asyncResp->res, - boost::urls::format( - "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId)); - return; - } - asyncResp->res.jsonValue["@odata.type"] = - "#SoftwareInventory.v1_1_0.SoftwareInventory"; - asyncResp->res.jsonValue["Name"] = "Software Inventory"; - asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK"; - - asyncResp->res.jsonValue["Updateable"] = false; - sw_util::getSwUpdatableStatus(asyncResp, swId); - }); + if (obj.second.empty()) + { + continue; + } + + found = true; + sw_util::getSwStatus(asyncResp, swId, obj.second[0].first); + getSoftwareVersion(asyncResp, obj.second[0].first, obj.first, + *swId); + } + if (!found) + { + BMCWEB_LOG_WARNING("Input swID {} not found!", *swId); + messages::resourceMissingAtURI( + asyncResp->res, + boost::urls::format( + "/redfish/v1/UpdateService/FirmwareInventory/{}", + *swId)); + return; + } + asyncResp->res.jsonValue["@odata.type"] = + "#SoftwareInventory.v1_1_0.SoftwareInventory"; + asyncResp->res.jsonValue["Name"] = "Software Inventory"; + asyncResp->res.jsonValue["Status"]["HealthRollup"] = + resource::Health::OK; + + asyncResp->res.jsonValue["Updateable"] = false; + sw_util::getSwUpdatableStatus(asyncResp, swId); + }); } inline void requestRoutesUpdateService(App& app) diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp index 37fb2045c9..a18f1d03ca 100644 --- a/redfish-core/lib/virtual_media.hpp +++ b/redfish-core/lib/virtual_media.hpp @@ -1,17 +1,17 @@ /* -// Copyright (c) 2018 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +Copyright (c) 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once @@ -43,9 +43,8 @@ enum class VmMode Proxy }; -inline VmMode - parseObjectPathAndGetMode(const sdbusplus::message::object_path& itemPath, - const std::string& resName) +inline VmMode parseObjectPathAndGetMode( + const sdbusplus::message::object_path& itemPath, const std::string& resName) { std::string thisPath = itemPath.filename(); BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath); @@ -101,26 +100,26 @@ inline void [service, resName, asyncResp, handler = std::move(handler)]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); + if (ec) + { + BMCWEB_LOG_DEBUG("DBUS response error"); - return; - } + return; + } - for (const auto& item : subtree) - { - VmMode mode = parseObjectPathAndGetMode(item.first, resName); - if (mode != VmMode::Invalid) + for (const auto& item : subtree) { - handler(service, resName, asyncResp, item); - return; + VmMode mode = parseObjectPathAndGetMode(item.first, resName); + if (mode != VmMode::Invalid) + { + handler(service, resName, asyncResp, item); + return; + } } - } - BMCWEB_LOG_DEBUG("Parent item not found"); - asyncResp->res.result(boost::beast::http::status::not_found); - }); + BMCWEB_LOG_DEBUG("Parent item not found"); + asyncResp->res.result(boost::beast::http::status::not_found); + }); } /** @@ -257,9 +256,9 @@ inline nlohmann::json vmItemTemplate(const std::string& name, item["WriteProtected"] = true; item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected; item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"}); - item["TransferMethod"] = "Stream"; + item["TransferMethod"] = virtual_media::TransferMethod::Stream; item["Oem"]["OpenBMC"]["@odata.type"] = - "#OemVirtualMedia.v1_0_0.VirtualMedia"; + "#OpenBMCVirtualMedia.v1_0_0.VirtualMedia"; item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format( "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName); @@ -281,29 +280,29 @@ inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp, [name, asyncResp{std::move(asyncResp)}]( const boost::system::error_code& ec, const dbus::utility::ManagedObjectType& subtree) { - if (ec) - { - BMCWEB_LOG_DEBUG("DBUS response error"); - return; - } - nlohmann::json& members = asyncResp->res.jsonValue["Members"]; - members = nlohmann::json::array(); - - for (const auto& object : subtree) - { - nlohmann::json item; - std::string path = object.first.filename(); - if (path.empty()) + if (ec) { - continue; + BMCWEB_LOG_DEBUG("DBUS response error"); + return; } + nlohmann::json& members = asyncResp->res.jsonValue["Members"]; + members = nlohmann::json::array(); - item["@odata.id"] = boost::urls::format( - "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path); - members.emplace_back(std::move(item)); - } - asyncResp->res.jsonValue["Members@odata.count"] = members.size(); - }); + for (const auto& object : subtree) + { + nlohmann::json item; + std::string path = object.first.filename(); + if (path.empty()) + { + continue; + } + + item["@odata.id"] = boost::urls::format( + "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path); + members.emplace_back(std::move(item)); + } + asyncResp->res.jsonValue["Members@odata.count"] = members.size(); + }); } inline void @@ -415,9 +414,8 @@ inline std::optional<TransferProtocol> getTransferProtocolFromParam( * @brief Function extends URI with transfer protocol type. * */ -inline std::string - getUriWithTransferProtocol(const std::string& imageUri, - const TransferProtocol& transferProtocol) +inline std::string getUriWithTransferProtocol( + const std::string& imageUri, const TransferProtocol& transferProtocol) { if (transferProtocol == TransferProtocol::smb) { @@ -474,14 +472,14 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, // Pass secret over pipe secretPipe->asyncWrite( std::move(userName), std::move(password), - [asyncResp, secretPipe](const boost::system::error_code& ec, - std::size_t) { - if (ec) - { - BMCWEB_LOG_ERROR("Failed to pass secret: {}", ec); - messages::internalError(asyncResp->res); - } - }); + [asyncResp, + secretPipe](const boost::system::error_code& ec, std::size_t) { + if (ec) + { + BMCWEB_LOG_ERROR("Failed to pass secret: {}", ec); + messages::internalError(asyncResp->res); + } + }); } dbus::utility::DbusVariantType unixFd( @@ -491,20 +489,20 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, "/xyz/openbmc_project/VirtualMedia/Legacy"); path /= name; crow::connections::systemBus->async_method_call( - [asyncResp, secretPipe](const boost::system::error_code& ec, - bool success) { - if (ec) - { - BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - if (!success) - { - BMCWEB_LOG_ERROR("Service responded with error"); - messages::internalError(asyncResp->res); - } - }, + [asyncResp, + secretPipe](const boost::system::error_code& ec, bool success) { + if (ec) + { + BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); + messages::internalError(asyncResp->res); + return; + } + if (!success) + { + BMCWEB_LOG_ERROR("Service responded with error"); + messages::internalError(asyncResp->res); + } + }, service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw, unixFd); } @@ -662,14 +660,14 @@ inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, { crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); + if (ec) + { + BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - }, + messages::internalError(asyncResp->res); + return; + } + }, service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); } @@ -677,14 +675,14 @@ inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, { crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { - if (ec) - { - BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); + if (ec) + { + BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); - messages::internalError(asyncResp->res); - return; - } - }, + messages::internalError(asyncResp->res); + return; + } + }, service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); } @@ -726,47 +724,51 @@ inline void handleManagersVirtualMediaActionInsertPost( [asyncResp, action, actionParams, resName](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& getObjectType) mutable { - if (ec) - { - BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); - messages::resourceNotFound(asyncResp->res, action, resName); - - return; - } - - std::string service = getObjectType.begin()->first; - BMCWEB_LOG_DEBUG("GetObjectType: {}", service); - - sdbusplus::message::object_path path( - "/xyz/openbmc_project/VirtualMedia"); - dbus::utility::getManagedObjects( - service, path, - [service, resName, action, actionParams, asyncResp]( - const boost::system::error_code& ec2, - const dbus::utility::ManagedObjectType& subtree) mutable { - if (ec2) + if (ec) { - // Not possible in proxy mode - BMCWEB_LOG_DEBUG("InsertMedia not " - "allowed in proxy mode"); + BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); messages::resourceNotFound(asyncResp->res, action, resName); return; } - for (const auto& object : subtree) - { - VmMode mode = parseObjectPathAndGetMode(object.first, resName); - if (mode == VmMode::Legacy) - { - validateParams(asyncResp, service, resName, actionParams); - return; - } - } - BMCWEB_LOG_DEBUG("Parent item not found"); - messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG("GetObjectType: {}", service); + + sdbusplus::message::object_path path( + "/xyz/openbmc_project/VirtualMedia"); + dbus::utility::getManagedObjects( + service, path, + [service, resName, action, actionParams, asyncResp]( + const boost::system::error_code& ec2, + const dbus::utility::ManagedObjectType& subtree) mutable { + if (ec2) + { + // Not possible in proxy mode + BMCWEB_LOG_DEBUG("InsertMedia not " + "allowed in proxy mode"); + messages::resourceNotFound(asyncResp->res, action, + resName); + + return; + } + for (const auto& object : subtree) + { + VmMode mode = + parseObjectPathAndGetMode(object.first, resName); + if (mode == VmMode::Legacy) + { + validateParams(asyncResp, service, resName, + actionParams); + + return; + } + } + BMCWEB_LOG_DEBUG("Parent item not found"); + messages::resourceNotFound(asyncResp->res, "VirtualMedia", + resName); + }); }); - }); } inline void handleManagersVirtualMediaActionEject( @@ -792,44 +794,48 @@ inline void handleManagersVirtualMediaActionEject( [asyncResp, action, resName](const boost::system::error_code& ec2, const dbus::utility::MapperGetObject& getObjectType) { - if (ec2) - { - BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec2); - messages::internalError(asyncResp->res); - - return; - } - std::string service = getObjectType.begin()->first; - BMCWEB_LOG_DEBUG("GetObjectType: {}", service); - - sdbusplus::message::object_path path( - "/xyz/openbmc_project/VirtualMedia"); - dbus::utility::getManagedObjects( - service, path, - [resName, service, action, - asyncResp](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& subtree) { - if (ec) + if (ec2) { - BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); - messages::resourceNotFound(asyncResp->res, action, resName); + BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", + ec2); + messages::internalError(asyncResp->res); + return; } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG("GetObjectType: {}", service); + + sdbusplus::message::object_path path( + "/xyz/openbmc_project/VirtualMedia"); + dbus::utility::getManagedObjects( + service, path, + [resName, service, action, + asyncResp](const boost::system::error_code& ec, + const dbus::utility::ManagedObjectType& subtree) { + if (ec) + { + BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); + messages::resourceNotFound(asyncResp->res, action, + resName); + return; + } - for (const auto& object : subtree) - { - VmMode mode = parseObjectPathAndGetMode(object.first, resName); - if (mode != VmMode::Invalid) - { - doEjectAction(asyncResp, service, resName, - mode == VmMode::Legacy); - return; - } - } - BMCWEB_LOG_DEBUG("Parent item not found"); - messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); + for (const auto& object : subtree) + { + VmMode mode = + parseObjectPathAndGetMode(object.first, resName); + if (mode != VmMode::Invalid) + { + doEjectAction(asyncResp, service, resName, + mode == VmMode::Legacy); + return; + } + } + BMCWEB_LOG_DEBUG("Parent item not found"); + messages::resourceNotFound(asyncResp->res, "VirtualMedia", + resName); + }); }); - }); } inline void handleManagersVirtualMediaCollectionGet( @@ -858,18 +864,18 @@ inline void handleManagersVirtualMediaCollectionGet( "/xyz/openbmc_project/VirtualMedia", {}, [asyncResp, name](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& getObjectType) { - if (ec) - { - BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); - messages::internalError(asyncResp->res); + if (ec) + { + BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); + messages::internalError(asyncResp->res); - return; - } - std::string service = getObjectType.begin()->first; - BMCWEB_LOG_DEBUG("GetObjectType: {}", service); + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG("GetObjectType: {}", service); - getVmResourceList(asyncResp, service, name); - }); + getVmResourceList(asyncResp, service, name); + }); } inline void @@ -893,18 +899,18 @@ inline void [asyncResp, name, resName](const boost::system::error_code& ec, const dbus::utility::MapperGetObject& getObjectType) { - if (ec) - { - BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); - messages::internalError(asyncResp->res); + if (ec) + { + BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); + messages::internalError(asyncResp->res); - return; - } - std::string service = getObjectType.begin()->first; - BMCWEB_LOG_DEBUG("GetObjectType: {}", service); + return; + } + std::string service = getObjectType.begin()->first; + BMCWEB_LOG_DEBUG("GetObjectType: {}", service); - getVmData(asyncResp, service, name, resName); - }); + getVmData(asyncResp, service, name, resName); + }); } inline void requestNBDVirtualMediaRoutes(App& app) |