xref: /openbmc/bmcweb/features/redfish/include/redfish_aggregator.hpp (revision a7a80296f731ef1069d3ecfbd3069668fb71cd68)
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