17fb33566SCarson Labrado #pragma once 27fb33566SCarson Labrado 37fb33566SCarson Labrado #include <http_client.hpp> 47fb33566SCarson Labrado 57fb33566SCarson Labrado namespace redfish 67fb33566SCarson Labrado { 77fb33566SCarson Labrado 8*05916cefSCarson Labrado enum class Result 9*05916cefSCarson Labrado { 10*05916cefSCarson Labrado LocalHandle, 11*05916cefSCarson Labrado NoLocalHandle 12*05916cefSCarson Labrado }; 13*05916cefSCarson Labrado 14*05916cefSCarson Labrado // Checks if the provided path is related to one of the resource collections 15*05916cefSCarson Labrado // under UpdateService 16*05916cefSCarson Labrado static inline bool 17*05916cefSCarson Labrado isUpdateServiceCollection(const std::vector<std::string>& segments) 18*05916cefSCarson Labrado { 19*05916cefSCarson Labrado if (segments.size() < 4) 20*05916cefSCarson Labrado { 21*05916cefSCarson Labrado return false; 22*05916cefSCarson Labrado } 23*05916cefSCarson Labrado 24*05916cefSCarson Labrado return (segments[2] == "UpdateService") && 25*05916cefSCarson Labrado ((segments[3] == "FirmwareInventory") || 26*05916cefSCarson Labrado (segments[3] == "SoftwareInventory")); 27*05916cefSCarson Labrado } 28*05916cefSCarson Labrado 297fb33566SCarson Labrado class RedfishAggregator 307fb33566SCarson Labrado { 317fb33566SCarson Labrado private: 32a7a80296SCarson Labrado const std::string retryPolicyName = "RedfishAggregation"; 33a7a80296SCarson Labrado const uint32_t retryAttempts = 5; 34a7a80296SCarson Labrado const uint32_t retryTimeoutInterval = 0; 35a7a80296SCarson Labrado 367fb33566SCarson Labrado RedfishAggregator() 377fb33566SCarson Labrado { 387fb33566SCarson Labrado getSatelliteConfigs(constructorCallback); 39a7a80296SCarson Labrado 40a7a80296SCarson Labrado // Setup the retry policy to be used by Redfish Aggregation 41a7a80296SCarson Labrado crow::HttpClient::getInstance().setRetryConfig( 42a7a80296SCarson Labrado retryAttempts, retryTimeoutInterval, aggregationRetryHandler, 43a7a80296SCarson Labrado retryPolicyName); 447fb33566SCarson Labrado } 457fb33566SCarson Labrado 46a7a80296SCarson Labrado static inline boost::system::error_code 47a7a80296SCarson Labrado aggregationRetryHandler(unsigned int respCode) 48a7a80296SCarson Labrado { 49a7a80296SCarson Labrado // As a default, assume 200X is alright. 50a7a80296SCarson Labrado // We don't need to retry on a 404 51a7a80296SCarson Labrado if ((respCode < 200) || ((respCode >= 300) && (respCode != 404))) 52a7a80296SCarson Labrado { 53a7a80296SCarson Labrado return boost::system::errc::make_error_code( 54a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 55a7a80296SCarson Labrado } 56a7a80296SCarson Labrado 57a7a80296SCarson Labrado // Return 0 if the response code is valid 58a7a80296SCarson Labrado return boost::system::errc::make_error_code( 59a7a80296SCarson Labrado boost::system::errc::success); 609fa6d147SNan Zhou } 61a7a80296SCarson Labrado 627fb33566SCarson Labrado // Dummy callback used by the Constructor so that it can report the number 637fb33566SCarson Labrado // of satellite configs when the class is first created 647fb33566SCarson Labrado static void constructorCallback( 657fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 667fb33566SCarson Labrado { 677fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "There were " 687fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 697fb33566SCarson Labrado << " satellite configs found at startup"; 707fb33566SCarson Labrado } 717fb33566SCarson Labrado 727fb33566SCarson Labrado // Polls D-Bus to get all available satellite config information 737fb33566SCarson Labrado // Expects a handler which interacts with the returned configs 747fb33566SCarson Labrado static void getSatelliteConfigs( 757fb33566SCarson Labrado const std::function<void( 767fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>&)>& handler) 777fb33566SCarson Labrado { 787fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Gathering satellite configs"; 797fb33566SCarson Labrado crow::connections::systemBus->async_method_call( 807fb33566SCarson Labrado [handler](const boost::system::error_code ec, 817fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects) { 827fb33566SCarson Labrado if (ec) 837fb33566SCarson Labrado { 84002d39b4SEd Tanous BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", " 85002d39b4SEd Tanous << ec.message(); 867fb33566SCarson Labrado return; 877fb33566SCarson Labrado } 887fb33566SCarson Labrado 897fb33566SCarson Labrado // Maps a chosen alias representing a satellite BMC to a url 907fb33566SCarson Labrado // containing the information required to create a http 917fb33566SCarson Labrado // connection to the satellite 927fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url> satelliteInfo; 937fb33566SCarson Labrado 947fb33566SCarson Labrado findSatelliteConfigs(objects, satelliteInfo); 957fb33566SCarson Labrado 967fb33566SCarson Labrado if (!satelliteInfo.empty()) 977fb33566SCarson Labrado { 987fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with " 997fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 1007fb33566SCarson Labrado << " satellite BMCs"; 1017fb33566SCarson Labrado } 1027fb33566SCarson Labrado else 1037fb33566SCarson Labrado { 1047fb33566SCarson Labrado BMCWEB_LOG_DEBUG 1057fb33566SCarson Labrado << "No satellite BMCs detected. Redfish Aggregation not enabled"; 1067fb33566SCarson Labrado } 1077fb33566SCarson Labrado handler(satelliteInfo); 1087fb33566SCarson Labrado }, 1097fb33566SCarson Labrado "xyz.openbmc_project.EntityManager", "/", 1107fb33566SCarson Labrado "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1117fb33566SCarson Labrado } 1127fb33566SCarson Labrado 1137fb33566SCarson Labrado // Search D-Bus objects for satellite config objects and add their 1147fb33566SCarson Labrado // information if valid 1157fb33566SCarson Labrado static void findSatelliteConfigs( 1167fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects, 1177fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1187fb33566SCarson Labrado { 1197fb33566SCarson Labrado for (const auto& objectPath : objects) 1207fb33566SCarson Labrado { 1217fb33566SCarson Labrado for (const auto& interface : objectPath.second) 1227fb33566SCarson Labrado { 1237fb33566SCarson Labrado if (interface.first == 1247fb33566SCarson Labrado "xyz.openbmc_project.Configuration.SatelliteController") 1257fb33566SCarson Labrado { 1267fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Found Satellite Controller at " 1277fb33566SCarson Labrado << objectPath.first.str; 1287fb33566SCarson Labrado 129*05916cefSCarson Labrado if (!satelliteInfo.empty()) 130*05916cefSCarson Labrado { 131*05916cefSCarson Labrado BMCWEB_LOG_ERROR 132*05916cefSCarson Labrado << "Redfish Aggregation only supports one satellite!"; 133*05916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Clearing all satellite data"; 134*05916cefSCarson Labrado satelliteInfo.clear(); 135*05916cefSCarson Labrado return; 136*05916cefSCarson Labrado } 137*05916cefSCarson Labrado 138*05916cefSCarson Labrado // For now assume there will only be one satellite config. 139*05916cefSCarson Labrado // Assign it the name/prefix "5B247A" 140*05916cefSCarson Labrado addSatelliteConfig("5B247A", interface.second, 141*05916cefSCarson Labrado satelliteInfo); 1427fb33566SCarson Labrado } 1437fb33566SCarson Labrado } 1447fb33566SCarson Labrado } 1457fb33566SCarson Labrado } 1467fb33566SCarson Labrado 1477fb33566SCarson Labrado // Parse the properties of a satellite config object and add the 1487fb33566SCarson Labrado // configuration if the properties are valid 1497fb33566SCarson Labrado static void addSatelliteConfig( 150*05916cefSCarson Labrado const std::string& name, 1517fb33566SCarson Labrado const dbus::utility::DBusPropertiesMap& properties, 1527fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1537fb33566SCarson Labrado { 1547fb33566SCarson Labrado boost::urls::url url; 1557fb33566SCarson Labrado 1567fb33566SCarson Labrado for (const auto& prop : properties) 1577fb33566SCarson Labrado { 158*05916cefSCarson Labrado if (prop.first == "Hostname") 1597fb33566SCarson Labrado { 1607fb33566SCarson Labrado const std::string* propVal = 1617fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1627fb33566SCarson Labrado if (propVal == nullptr) 1637fb33566SCarson Labrado { 1647fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Hostname value"; 1657fb33566SCarson Labrado return; 1667fb33566SCarson Labrado } 1677fb33566SCarson Labrado url.set_host(*propVal); 1687fb33566SCarson Labrado } 1697fb33566SCarson Labrado 1707fb33566SCarson Labrado else if (prop.first == "Port") 1717fb33566SCarson Labrado { 1727fb33566SCarson Labrado const uint64_t* propVal = std::get_if<uint64_t>(&prop.second); 1737fb33566SCarson Labrado if (propVal == nullptr) 1747fb33566SCarson Labrado { 1757fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Port value"; 1767fb33566SCarson Labrado return; 1777fb33566SCarson Labrado } 1787fb33566SCarson Labrado 1797fb33566SCarson Labrado if (*propVal > std::numeric_limits<uint16_t>::max()) 1807fb33566SCarson Labrado { 1817fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Port value out of range"; 1827fb33566SCarson Labrado return; 1837fb33566SCarson Labrado } 1847fb33566SCarson Labrado url.set_port(static_cast<uint16_t>(*propVal)); 1857fb33566SCarson Labrado } 1867fb33566SCarson Labrado 1877fb33566SCarson Labrado else if (prop.first == "AuthType") 1887fb33566SCarson Labrado { 1897fb33566SCarson Labrado const std::string* propVal = 1907fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1917fb33566SCarson Labrado if (propVal == nullptr) 1927fb33566SCarson Labrado { 1937fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid AuthType value"; 1947fb33566SCarson Labrado return; 1957fb33566SCarson Labrado } 1967fb33566SCarson Labrado 1977fb33566SCarson Labrado // For now assume authentication not required to communicate 1987fb33566SCarson Labrado // with the satellite BMC 1997fb33566SCarson Labrado if (*propVal != "None") 2007fb33566SCarson Labrado { 2017fb33566SCarson Labrado BMCWEB_LOG_ERROR 2027fb33566SCarson Labrado << "Unsupported AuthType value: " << *propVal 2037fb33566SCarson Labrado << ", only \"none\" is supported"; 2047fb33566SCarson Labrado return; 2057fb33566SCarson Labrado } 2067fb33566SCarson Labrado url.set_scheme("http"); 2077fb33566SCarson Labrado } 2087fb33566SCarson Labrado } // Finished reading properties 2097fb33566SCarson Labrado 2107fb33566SCarson Labrado // Make sure all required config information was made available 2117fb33566SCarson Labrado if (url.host().empty()) 2127fb33566SCarson Labrado { 2137fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host"; 2147fb33566SCarson Labrado return; 2157fb33566SCarson Labrado } 2167fb33566SCarson Labrado 2177fb33566SCarson Labrado if (!url.has_port()) 2187fb33566SCarson Labrado { 2197fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port"; 2207fb33566SCarson Labrado return; 2217fb33566SCarson Labrado } 2227fb33566SCarson Labrado 2237fb33566SCarson Labrado if (!url.has_scheme()) 2247fb33566SCarson Labrado { 2257fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name 2267fb33566SCarson Labrado << " missing AuthType"; 2277fb33566SCarson Labrado return; 2287fb33566SCarson Labrado } 2297fb33566SCarson Labrado 2307fb33566SCarson Labrado std::string resultString; 2317fb33566SCarson Labrado auto result = satelliteInfo.insert_or_assign(name, std::move(url)); 2327fb33566SCarson Labrado if (result.second) 2337fb33566SCarson Labrado { 2347fb33566SCarson Labrado resultString = "Added new satellite config "; 2357fb33566SCarson Labrado } 2367fb33566SCarson Labrado else 2377fb33566SCarson Labrado { 2387fb33566SCarson Labrado resultString = "Updated existing satellite config "; 2397fb33566SCarson Labrado } 2407fb33566SCarson Labrado 2417fb33566SCarson Labrado BMCWEB_LOG_DEBUG << resultString << name << " at " 2427fb33566SCarson Labrado << result.first->second.scheme() << "://" 2437fb33566SCarson Labrado << result.first->second.encoded_host_and_port(); 2447fb33566SCarson Labrado } 2457fb33566SCarson Labrado 2467fb33566SCarson Labrado public: 2477fb33566SCarson Labrado RedfishAggregator(const RedfishAggregator&) = delete; 2487fb33566SCarson Labrado RedfishAggregator& operator=(const RedfishAggregator&) = delete; 2497fb33566SCarson Labrado RedfishAggregator(RedfishAggregator&&) = delete; 2507fb33566SCarson Labrado RedfishAggregator& operator=(RedfishAggregator&&) = delete; 2517fb33566SCarson Labrado ~RedfishAggregator() = default; 2527fb33566SCarson Labrado 2537fb33566SCarson Labrado static RedfishAggregator& getInstance() 2547fb33566SCarson Labrado { 2557fb33566SCarson Labrado static RedfishAggregator handler; 2567fb33566SCarson Labrado return handler; 2577fb33566SCarson Labrado } 258*05916cefSCarson Labrado 259*05916cefSCarson Labrado // Entry point to Redfish Aggregation 260*05916cefSCarson Labrado // Returns Result stating whether or not we still need to locally handle the 261*05916cefSCarson Labrado // request 262*05916cefSCarson Labrado static Result 263*05916cefSCarson Labrado beginAggregation(const crow::Request& thisReq, 264*05916cefSCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 265*05916cefSCarson Labrado { 266*05916cefSCarson Labrado using crow::utility::OrMorePaths; 267*05916cefSCarson Labrado using crow::utility::readUrlSegments; 268*05916cefSCarson Labrado const boost::urls::url_view& url = thisReq.urlView; 269*05916cefSCarson Labrado // UpdateService is the only top level resource that is not a Collection 270*05916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService")) 271*05916cefSCarson Labrado { 272*05916cefSCarson Labrado return Result::LocalHandle; 273*05916cefSCarson Labrado } 274*05916cefSCarson Labrado 275*05916cefSCarson Labrado // Is the request for a resource collection?: 276*05916cefSCarson Labrado // /redfish/v1/<resource> 277*05916cefSCarson Labrado // e.g. /redfish/v1/Chassis 278*05916cefSCarson Labrado std::string collectionName; 279*05916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", std::ref(collectionName))) 280*05916cefSCarson Labrado { 281*05916cefSCarson Labrado return Result::LocalHandle; 282*05916cefSCarson Labrado } 283*05916cefSCarson Labrado 284*05916cefSCarson Labrado // We know that the ID of an aggregated resource will begin with 285*05916cefSCarson Labrado // "5B247A". For the most part the URI will begin like this: 286*05916cefSCarson Labrado // /redfish/v1/<resource>/<resource ID> 287*05916cefSCarson Labrado // Note, FirmwareInventory and SoftwareInventory are "special" because 288*05916cefSCarson Labrado // they are two levels deep, but still need aggregated 289*05916cefSCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory/<FirmwareInventory ID> 290*05916cefSCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory/<SoftwareInventory ID> 291*05916cefSCarson Labrado std::string memberName; 292*05916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 293*05916cefSCarson Labrado "SoftwareInventory", std::ref(memberName), 294*05916cefSCarson Labrado OrMorePaths()) || 295*05916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 296*05916cefSCarson Labrado "FirmwareInventory", std::ref(memberName), 297*05916cefSCarson Labrado OrMorePaths()) || 298*05916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", std::ref(collectionName), 299*05916cefSCarson Labrado std::ref(memberName), OrMorePaths())) 300*05916cefSCarson Labrado { 301*05916cefSCarson Labrado if (memberName.starts_with("5B247A")) 302*05916cefSCarson Labrado { 303*05916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Need to forward a request"; 304*05916cefSCarson Labrado 305*05916cefSCarson Labrado // TODO: Extract the prefix from the request's URI, retrieve 306*05916cefSCarson Labrado // the associated satellite config information, and then 307*05916cefSCarson Labrado // forward the request to that satellite. 308*05916cefSCarson Labrado redfish::messages::internalError(asyncResp->res); 309*05916cefSCarson Labrado return Result::NoLocalHandle; 310*05916cefSCarson Labrado } 311*05916cefSCarson Labrado return Result::LocalHandle; 312*05916cefSCarson Labrado } 313*05916cefSCarson Labrado 314*05916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation not required"; 315*05916cefSCarson Labrado return Result::LocalHandle; 316*05916cefSCarson Labrado } 3177fb33566SCarson Labrado }; 3187fb33566SCarson Labrado 3197fb33566SCarson Labrado } // namespace redfish 320