17fb33566SCarson Labrado #pragma once 27fb33566SCarson Labrado 37fb33566SCarson Labrado #include <http_client.hpp> 47fb33566SCarson Labrado 57fb33566SCarson Labrado namespace redfish 67fb33566SCarson Labrado { 77fb33566SCarson Labrado 87fb33566SCarson Labrado class RedfishAggregator 97fb33566SCarson Labrado { 107fb33566SCarson Labrado private: 11*a7a80296SCarson Labrado const std::string retryPolicyName = "RedfishAggregation"; 12*a7a80296SCarson Labrado const uint32_t retryAttempts = 5; 13*a7a80296SCarson Labrado const uint32_t retryTimeoutInterval = 0; 14*a7a80296SCarson Labrado 157fb33566SCarson Labrado RedfishAggregator() 167fb33566SCarson Labrado { 177fb33566SCarson Labrado getSatelliteConfigs(constructorCallback); 18*a7a80296SCarson Labrado 19*a7a80296SCarson Labrado // Setup the retry policy to be used by Redfish Aggregation 20*a7a80296SCarson Labrado crow::HttpClient::getInstance().setRetryConfig( 21*a7a80296SCarson Labrado retryAttempts, retryTimeoutInterval, aggregationRetryHandler, 22*a7a80296SCarson Labrado retryPolicyName); 237fb33566SCarson Labrado } 247fb33566SCarson Labrado 25*a7a80296SCarson Labrado static inline boost::system::error_code 26*a7a80296SCarson Labrado aggregationRetryHandler(unsigned int respCode) 27*a7a80296SCarson Labrado { 28*a7a80296SCarson Labrado // As a default, assume 200X is alright. 29*a7a80296SCarson Labrado // We don't need to retry on a 404 30*a7a80296SCarson Labrado if ((respCode < 200) || ((respCode >= 300) && (respCode != 404))) 31*a7a80296SCarson Labrado { 32*a7a80296SCarson Labrado return boost::system::errc::make_error_code( 33*a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 34*a7a80296SCarson Labrado } 35*a7a80296SCarson Labrado 36*a7a80296SCarson Labrado // Return 0 if the response code is valid 37*a7a80296SCarson Labrado return boost::system::errc::make_error_code( 38*a7a80296SCarson Labrado boost::system::errc::success); 39*a7a80296SCarson Labrado }; 40*a7a80296SCarson Labrado 417fb33566SCarson Labrado // Dummy callback used by the Constructor so that it can report the number 427fb33566SCarson Labrado // of satellite configs when the class is first created 437fb33566SCarson Labrado static void constructorCallback( 447fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 457fb33566SCarson Labrado { 467fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "There were " 477fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 487fb33566SCarson Labrado << " satellite configs found at startup"; 497fb33566SCarson Labrado } 507fb33566SCarson Labrado 517fb33566SCarson Labrado // Polls D-Bus to get all available satellite config information 527fb33566SCarson Labrado // Expects a handler which interacts with the returned configs 537fb33566SCarson Labrado static void getSatelliteConfigs( 547fb33566SCarson Labrado const std::function<void( 557fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>&)>& handler) 567fb33566SCarson Labrado { 577fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Gathering satellite configs"; 587fb33566SCarson Labrado crow::connections::systemBus->async_method_call( 597fb33566SCarson Labrado [handler](const boost::system::error_code ec, 607fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects) { 617fb33566SCarson Labrado if (ec) 627fb33566SCarson Labrado { 63002d39b4SEd Tanous BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", " 64002d39b4SEd Tanous << ec.message(); 657fb33566SCarson Labrado return; 667fb33566SCarson Labrado } 677fb33566SCarson Labrado 687fb33566SCarson Labrado // Maps a chosen alias representing a satellite BMC to a url 697fb33566SCarson Labrado // containing the information required to create a http 707fb33566SCarson Labrado // connection to the satellite 717fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url> satelliteInfo; 727fb33566SCarson Labrado 737fb33566SCarson Labrado findSatelliteConfigs(objects, satelliteInfo); 747fb33566SCarson Labrado 757fb33566SCarson Labrado if (!satelliteInfo.empty()) 767fb33566SCarson Labrado { 777fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with " 787fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 797fb33566SCarson Labrado << " satellite BMCs"; 807fb33566SCarson Labrado } 817fb33566SCarson Labrado else 827fb33566SCarson Labrado { 837fb33566SCarson Labrado BMCWEB_LOG_DEBUG 847fb33566SCarson Labrado << "No satellite BMCs detected. Redfish Aggregation not enabled"; 857fb33566SCarson Labrado } 867fb33566SCarson Labrado handler(satelliteInfo); 877fb33566SCarson Labrado }, 887fb33566SCarson Labrado "xyz.openbmc_project.EntityManager", "/", 897fb33566SCarson Labrado "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 907fb33566SCarson Labrado } 917fb33566SCarson Labrado 927fb33566SCarson Labrado // Search D-Bus objects for satellite config objects and add their 937fb33566SCarson Labrado // information if valid 947fb33566SCarson Labrado static void findSatelliteConfigs( 957fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects, 967fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 977fb33566SCarson Labrado { 987fb33566SCarson Labrado for (const auto& objectPath : objects) 997fb33566SCarson Labrado { 1007fb33566SCarson Labrado for (const auto& interface : objectPath.second) 1017fb33566SCarson Labrado { 1027fb33566SCarson Labrado if (interface.first == 1037fb33566SCarson Labrado "xyz.openbmc_project.Configuration.SatelliteController") 1047fb33566SCarson Labrado { 1057fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Found Satellite Controller at " 1067fb33566SCarson Labrado << objectPath.first.str; 1077fb33566SCarson Labrado 1087fb33566SCarson Labrado addSatelliteConfig(interface.second, satelliteInfo); 1097fb33566SCarson Labrado } 1107fb33566SCarson Labrado } 1117fb33566SCarson Labrado } 1127fb33566SCarson Labrado } 1137fb33566SCarson Labrado 1147fb33566SCarson Labrado // Parse the properties of a satellite config object and add the 1157fb33566SCarson Labrado // configuration if the properties are valid 1167fb33566SCarson Labrado static void addSatelliteConfig( 1177fb33566SCarson Labrado const dbus::utility::DBusPropertiesMap& properties, 1187fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1197fb33566SCarson Labrado { 1207fb33566SCarson Labrado boost::urls::url url; 1217fb33566SCarson Labrado std::string name; 1227fb33566SCarson Labrado 1237fb33566SCarson Labrado for (const auto& prop : properties) 1247fb33566SCarson Labrado { 1257fb33566SCarson Labrado if (prop.first == "Name") 1267fb33566SCarson Labrado { 1277fb33566SCarson Labrado const std::string* propVal = 1287fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1297fb33566SCarson Labrado if (propVal == nullptr) 1307fb33566SCarson Labrado { 1317fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Name value"; 1327fb33566SCarson Labrado return; 1337fb33566SCarson Labrado } 1347fb33566SCarson Labrado 1357fb33566SCarson Labrado // The IDs will become <Name>_<ID> so the name should not 1367fb33566SCarson Labrado // contain a '_' 1377fb33566SCarson Labrado if (propVal->find('_') != std::string::npos) 1387fb33566SCarson Labrado { 1397fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Name cannot contain a \"_\""; 1407fb33566SCarson Labrado return; 1417fb33566SCarson Labrado } 1427fb33566SCarson Labrado name = *propVal; 1437fb33566SCarson Labrado } 1447fb33566SCarson Labrado 1457fb33566SCarson Labrado else if (prop.first == "Hostname") 1467fb33566SCarson Labrado { 1477fb33566SCarson Labrado const std::string* propVal = 1487fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1497fb33566SCarson Labrado if (propVal == nullptr) 1507fb33566SCarson Labrado { 1517fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Hostname value"; 1527fb33566SCarson Labrado return; 1537fb33566SCarson Labrado } 1547fb33566SCarson Labrado url.set_host(*propVal); 1557fb33566SCarson Labrado } 1567fb33566SCarson Labrado 1577fb33566SCarson Labrado else if (prop.first == "Port") 1587fb33566SCarson Labrado { 1597fb33566SCarson Labrado const uint64_t* propVal = std::get_if<uint64_t>(&prop.second); 1607fb33566SCarson Labrado if (propVal == nullptr) 1617fb33566SCarson Labrado { 1627fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Port value"; 1637fb33566SCarson Labrado return; 1647fb33566SCarson Labrado } 1657fb33566SCarson Labrado 1667fb33566SCarson Labrado if (*propVal > std::numeric_limits<uint16_t>::max()) 1677fb33566SCarson Labrado { 1687fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Port value out of range"; 1697fb33566SCarson Labrado return; 1707fb33566SCarson Labrado } 1717fb33566SCarson Labrado url.set_port(static_cast<uint16_t>(*propVal)); 1727fb33566SCarson Labrado } 1737fb33566SCarson Labrado 1747fb33566SCarson Labrado else if (prop.first == "AuthType") 1757fb33566SCarson Labrado { 1767fb33566SCarson Labrado const std::string* propVal = 1777fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1787fb33566SCarson Labrado if (propVal == nullptr) 1797fb33566SCarson Labrado { 1807fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid AuthType value"; 1817fb33566SCarson Labrado return; 1827fb33566SCarson Labrado } 1837fb33566SCarson Labrado 1847fb33566SCarson Labrado // For now assume authentication not required to communicate 1857fb33566SCarson Labrado // with the satellite BMC 1867fb33566SCarson Labrado if (*propVal != "None") 1877fb33566SCarson Labrado { 1887fb33566SCarson Labrado BMCWEB_LOG_ERROR 1897fb33566SCarson Labrado << "Unsupported AuthType value: " << *propVal 1907fb33566SCarson Labrado << ", only \"none\" is supported"; 1917fb33566SCarson Labrado return; 1927fb33566SCarson Labrado } 1937fb33566SCarson Labrado url.set_scheme("http"); 1947fb33566SCarson Labrado } 1957fb33566SCarson Labrado } // Finished reading properties 1967fb33566SCarson Labrado 1977fb33566SCarson Labrado // Make sure all required config information was made available 1987fb33566SCarson Labrado if (name.empty()) 1997fb33566SCarson Labrado { 2007fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config missing Name"; 2017fb33566SCarson Labrado return; 2027fb33566SCarson Labrado } 2037fb33566SCarson Labrado 2047fb33566SCarson Labrado if (url.host().empty()) 2057fb33566SCarson Labrado { 2067fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host"; 2077fb33566SCarson Labrado return; 2087fb33566SCarson Labrado } 2097fb33566SCarson Labrado 2107fb33566SCarson Labrado if (!url.has_port()) 2117fb33566SCarson Labrado { 2127fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port"; 2137fb33566SCarson Labrado return; 2147fb33566SCarson Labrado } 2157fb33566SCarson Labrado 2167fb33566SCarson Labrado if (!url.has_scheme()) 2177fb33566SCarson Labrado { 2187fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name 2197fb33566SCarson Labrado << " missing AuthType"; 2207fb33566SCarson Labrado return; 2217fb33566SCarson Labrado } 2227fb33566SCarson Labrado 2237fb33566SCarson Labrado std::string resultString; 2247fb33566SCarson Labrado auto result = satelliteInfo.insert_or_assign(name, std::move(url)); 2257fb33566SCarson Labrado if (result.second) 2267fb33566SCarson Labrado { 2277fb33566SCarson Labrado resultString = "Added new satellite config "; 2287fb33566SCarson Labrado } 2297fb33566SCarson Labrado else 2307fb33566SCarson Labrado { 2317fb33566SCarson Labrado resultString = "Updated existing satellite config "; 2327fb33566SCarson Labrado } 2337fb33566SCarson Labrado 2347fb33566SCarson Labrado BMCWEB_LOG_DEBUG << resultString << name << " at " 2357fb33566SCarson Labrado << result.first->second.scheme() << "://" 2367fb33566SCarson Labrado << result.first->second.encoded_host_and_port(); 2377fb33566SCarson Labrado } 2387fb33566SCarson Labrado 2397fb33566SCarson Labrado public: 2407fb33566SCarson Labrado RedfishAggregator(const RedfishAggregator&) = delete; 2417fb33566SCarson Labrado RedfishAggregator& operator=(const RedfishAggregator&) = delete; 2427fb33566SCarson Labrado RedfishAggregator(RedfishAggregator&&) = delete; 2437fb33566SCarson Labrado RedfishAggregator& operator=(RedfishAggregator&&) = delete; 2447fb33566SCarson Labrado ~RedfishAggregator() = default; 2457fb33566SCarson Labrado 2467fb33566SCarson Labrado static RedfishAggregator& getInstance() 2477fb33566SCarson Labrado { 2487fb33566SCarson Labrado static RedfishAggregator handler; 2497fb33566SCarson Labrado return handler; 2507fb33566SCarson Labrado } 2517fb33566SCarson Labrado }; 2527fb33566SCarson Labrado 2537fb33566SCarson Labrado } // namespace redfish 254