xref: /openbmc/bmcweb/features/redfish/include/redfish_aggregator.hpp (revision db18fc98241b4fc9a4274f45c1d6b07d7bd1b93d)
17fb33566SCarson Labrado #pragma once
27fb33566SCarson Labrado 
346a81465SCarson Labrado #include <dbus_utility.hpp>
446a81465SCarson Labrado #include <error_messages.hpp>
57fb33566SCarson Labrado #include <http_client.hpp>
646a81465SCarson Labrado #include <http_connection.hpp>
77fb33566SCarson Labrado 
87fb33566SCarson Labrado namespace redfish
97fb33566SCarson Labrado {
107fb33566SCarson Labrado 
1105916cefSCarson Labrado enum class Result
1205916cefSCarson Labrado {
1305916cefSCarson Labrado     LocalHandle,
1405916cefSCarson Labrado     NoLocalHandle
1505916cefSCarson Labrado };
1605916cefSCarson Labrado 
171c0bb5c6SCarson Labrado static void addPrefixToItem(nlohmann::json& item, std::string_view prefix)
181c0bb5c6SCarson Labrado {
191c0bb5c6SCarson Labrado     std::string* strValue = item.get_ptr<std::string*>();
201c0bb5c6SCarson Labrado     if (strValue == nullptr)
211c0bb5c6SCarson Labrado     {
221c0bb5c6SCarson Labrado         BMCWEB_LOG_CRITICAL << "Field wasn't a string????";
231c0bb5c6SCarson Labrado         return;
241c0bb5c6SCarson Labrado     }
251c0bb5c6SCarson Labrado     // Make sure the value is a properly formatted URI
261c0bb5c6SCarson Labrado     auto parsed = boost::urls::parse_relative_ref(*strValue);
271c0bb5c6SCarson Labrado     if (!parsed)
281c0bb5c6SCarson Labrado     {
291c0bb5c6SCarson Labrado         BMCWEB_LOG_CRITICAL << "Couldn't parse URI from resource " << *strValue;
301c0bb5c6SCarson Labrado         return;
311c0bb5c6SCarson Labrado     }
321c0bb5c6SCarson Labrado 
331c0bb5c6SCarson Labrado     boost::urls::url_view thisUrl = *parsed;
341c0bb5c6SCarson Labrado 
351c0bb5c6SCarson Labrado     // We don't need to add prefixes to these URIs since
361c0bb5c6SCarson Labrado     // /redfish/v1/UpdateService/ itself is not a collection
371c0bb5c6SCarson Labrado     // /redfish/v1/UpdateService/FirmwareInventory
381c0bb5c6SCarson Labrado     // /redfish/v1/UpdateService/SoftwareInventory
391c0bb5c6SCarson Labrado     if (crow::utility::readUrlSegments(thisUrl, "redfish", "v1",
401c0bb5c6SCarson Labrado                                        "UpdateService", "FirmwareInventory") ||
411c0bb5c6SCarson Labrado         crow::utility::readUrlSegments(thisUrl, "redfish", "v1",
421c0bb5c6SCarson Labrado                                        "UpdateService", "SoftwareInventory"))
431c0bb5c6SCarson Labrado     {
441c0bb5c6SCarson Labrado         BMCWEB_LOG_DEBUG << "Skipping UpdateService URI prefix fixing";
451c0bb5c6SCarson Labrado         return;
461c0bb5c6SCarson Labrado     }
471c0bb5c6SCarson Labrado 
481c0bb5c6SCarson Labrado     // We also need to aggregate FirmwareInventory and
491c0bb5c6SCarson Labrado     // SoftwareInventory so add an extra offset
501c0bb5c6SCarson Labrado     // /redfish/v1/UpdateService/FirmwareInventory/<id>
511c0bb5c6SCarson Labrado     // /redfish/v1/UpdateService/SoftwareInventory/<id>
521c0bb5c6SCarson Labrado     std::string collectionName;
531c0bb5c6SCarson Labrado     std::string softwareItem;
541c0bb5c6SCarson Labrado     if (crow::utility::readUrlSegments(
551c0bb5c6SCarson Labrado             thisUrl, "redfish", "v1", "UpdateService", std::ref(collectionName),
561c0bb5c6SCarson Labrado             std::ref(softwareItem), crow::utility::OrMorePaths()))
571c0bb5c6SCarson Labrado     {
581c0bb5c6SCarson Labrado         softwareItem.insert(0, "_");
591c0bb5c6SCarson Labrado         softwareItem.insert(0, prefix);
601c0bb5c6SCarson Labrado         item = crow::utility::replaceUrlSegment(thisUrl, 4, softwareItem);
611c0bb5c6SCarson Labrado     }
621c0bb5c6SCarson Labrado 
631c0bb5c6SCarson Labrado     // A collection URI that ends with "/" such as
641c0bb5c6SCarson Labrado     // "/redfish/v1/Chassis/" will have 4 segments so we need to
651c0bb5c6SCarson Labrado     // make sure we don't try to add a prefix to an empty segment
661c0bb5c6SCarson Labrado     if (crow::utility::readUrlSegments(
671c0bb5c6SCarson Labrado             thisUrl, "redfish", "v1", std::ref(collectionName),
681c0bb5c6SCarson Labrado             std::ref(softwareItem), crow::utility::OrMorePaths()))
691c0bb5c6SCarson Labrado     {
701c0bb5c6SCarson Labrado         softwareItem.insert(0, "_");
711c0bb5c6SCarson Labrado         softwareItem.insert(0, prefix);
721c0bb5c6SCarson Labrado         item = crow::utility::replaceUrlSegment(thisUrl, 3, softwareItem);
731c0bb5c6SCarson Labrado     }
741c0bb5c6SCarson Labrado }
751c0bb5c6SCarson Labrado 
761c0bb5c6SCarson Labrado // We need to attempt to update all URIs under Actions
771c0bb5c6SCarson Labrado static void addPrefixesToActions(nlohmann::json& json, std::string_view prefix)
781c0bb5c6SCarson Labrado {
791c0bb5c6SCarson Labrado     nlohmann::json::object_t* object =
801c0bb5c6SCarson Labrado         json.get_ptr<nlohmann::json::object_t*>();
811c0bb5c6SCarson Labrado     if (object != nullptr)
821c0bb5c6SCarson Labrado     {
831c0bb5c6SCarson Labrado         for (std::pair<const std::string, nlohmann::json>& item : *object)
841c0bb5c6SCarson Labrado         {
851c0bb5c6SCarson Labrado             std::string* strValue = item.second.get_ptr<std::string*>();
861c0bb5c6SCarson Labrado             if (strValue != nullptr)
871c0bb5c6SCarson Labrado             {
881c0bb5c6SCarson Labrado                 addPrefixToItem(item.second, prefix);
891c0bb5c6SCarson Labrado             }
901c0bb5c6SCarson Labrado             else
911c0bb5c6SCarson Labrado             {
921c0bb5c6SCarson Labrado                 addPrefixesToActions(item.second, prefix);
931c0bb5c6SCarson Labrado             }
941c0bb5c6SCarson Labrado         }
951c0bb5c6SCarson Labrado     }
961c0bb5c6SCarson Labrado }
971c0bb5c6SCarson Labrado 
981c0bb5c6SCarson Labrado // Search the json for all URIs and add the supplied prefix if the URI is for
991c0bb5c6SCarson Labrado // and aggregated resource.
1001c0bb5c6SCarson Labrado static void addPrefixes(nlohmann::json& json, std::string_view prefix)
1011c0bb5c6SCarson Labrado {
1021c0bb5c6SCarson Labrado     nlohmann::json::object_t* object =
1031c0bb5c6SCarson Labrado         json.get_ptr<nlohmann::json::object_t*>();
1041c0bb5c6SCarson Labrado     if (object != nullptr)
1051c0bb5c6SCarson Labrado     {
1061c0bb5c6SCarson Labrado         for (std::pair<const std::string, nlohmann::json>& item : *object)
1071c0bb5c6SCarson Labrado         {
1081c0bb5c6SCarson Labrado             if (item.first == "Actions")
1091c0bb5c6SCarson Labrado             {
1101c0bb5c6SCarson Labrado                 addPrefixesToActions(item.second, prefix);
1111c0bb5c6SCarson Labrado                 continue;
1121c0bb5c6SCarson Labrado             }
1131c0bb5c6SCarson Labrado 
1141c0bb5c6SCarson Labrado             if ((item.first == "@odata.id") || (item.first.ends_with("URI")))
1151c0bb5c6SCarson Labrado             {
1161c0bb5c6SCarson Labrado                 addPrefixToItem(item.second, prefix);
1171c0bb5c6SCarson Labrado             }
1181c0bb5c6SCarson Labrado             // Recusively parse the rest of the json
1191c0bb5c6SCarson Labrado             addPrefixes(item.second, prefix);
1201c0bb5c6SCarson Labrado         }
1211c0bb5c6SCarson Labrado         return;
1221c0bb5c6SCarson Labrado     }
1231c0bb5c6SCarson Labrado     nlohmann::json::array_t* array = json.get_ptr<nlohmann::json::array_t*>();
1241c0bb5c6SCarson Labrado     if (array != nullptr)
1251c0bb5c6SCarson Labrado     {
1261c0bb5c6SCarson Labrado         for (nlohmann::json& item : *array)
1271c0bb5c6SCarson Labrado         {
1281c0bb5c6SCarson Labrado             addPrefixes(item, prefix);
1291c0bb5c6SCarson Labrado         }
1301c0bb5c6SCarson Labrado     }
1311c0bb5c6SCarson Labrado }
1321c0bb5c6SCarson Labrado 
1337fb33566SCarson Labrado class RedfishAggregator
1347fb33566SCarson Labrado {
1357fb33566SCarson Labrado   private:
136a7a80296SCarson Labrado     const std::string retryPolicyName = "RedfishAggregation";
137ce969437SCarson Labrado     const std::string retryPolicyAction = "TerminateAfterRetries";
138ce969437SCarson Labrado     const uint32_t retryAttempts = 1;
139a7a80296SCarson Labrado     const uint32_t retryTimeoutInterval = 0;
14046a81465SCarson Labrado     const std::string id = "Aggregator";
141a7a80296SCarson Labrado 
1427fb33566SCarson Labrado     RedfishAggregator()
1437fb33566SCarson Labrado     {
1447fb33566SCarson Labrado         getSatelliteConfigs(constructorCallback);
145a7a80296SCarson Labrado 
146a7a80296SCarson Labrado         // Setup the retry policy to be used by Redfish Aggregation
147a7a80296SCarson Labrado         crow::HttpClient::getInstance().setRetryConfig(
148a7a80296SCarson Labrado             retryAttempts, retryTimeoutInterval, aggregationRetryHandler,
149a7a80296SCarson Labrado             retryPolicyName);
150ce969437SCarson Labrado         crow::HttpClient::getInstance().setRetryPolicy(retryPolicyAction,
151ce969437SCarson Labrado                                                        retryPolicyName);
1527fb33566SCarson Labrado     }
1537fb33566SCarson Labrado 
154a7a80296SCarson Labrado     static inline boost::system::error_code
155a7a80296SCarson Labrado         aggregationRetryHandler(unsigned int respCode)
156a7a80296SCarson Labrado     {
157a7a80296SCarson Labrado         // As a default, assume 200X is alright.
158a7a80296SCarson Labrado         // We don't need to retry on a 404
159a7a80296SCarson Labrado         if ((respCode < 200) || ((respCode >= 300) && (respCode != 404)))
160a7a80296SCarson Labrado         {
161a7a80296SCarson Labrado             return boost::system::errc::make_error_code(
162a7a80296SCarson Labrado                 boost::system::errc::result_out_of_range);
163a7a80296SCarson Labrado         }
164a7a80296SCarson Labrado 
165a7a80296SCarson Labrado         // Return 0 if the response code is valid
166a7a80296SCarson Labrado         return boost::system::errc::make_error_code(
167a7a80296SCarson Labrado             boost::system::errc::success);
1689fa6d147SNan Zhou     }
169a7a80296SCarson Labrado 
1707fb33566SCarson Labrado     // Dummy callback used by the Constructor so that it can report the number
1717fb33566SCarson Labrado     // of satellite configs when the class is first created
1727fb33566SCarson Labrado     static void constructorCallback(
1737fb33566SCarson Labrado         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
1747fb33566SCarson Labrado     {
1757fb33566SCarson Labrado         BMCWEB_LOG_DEBUG << "There were "
1767fb33566SCarson Labrado                          << std::to_string(satelliteInfo.size())
1777fb33566SCarson Labrado                          << " satellite configs found at startup";
1787fb33566SCarson Labrado     }
1797fb33566SCarson Labrado 
1807fb33566SCarson Labrado     // Polls D-Bus to get all available satellite config information
1817fb33566SCarson Labrado     // Expects a handler which interacts with the returned configs
1827fb33566SCarson Labrado     static void getSatelliteConfigs(
1837fb33566SCarson Labrado         const std::function<void(
1847fb33566SCarson Labrado             const std::unordered_map<std::string, boost::urls::url>&)>& handler)
1857fb33566SCarson Labrado     {
1867fb33566SCarson Labrado         BMCWEB_LOG_DEBUG << "Gathering satellite configs";
1877fb33566SCarson Labrado         crow::connections::systemBus->async_method_call(
1887fb33566SCarson Labrado             [handler](const boost::system::error_code ec,
1897fb33566SCarson Labrado                       const dbus::utility::ManagedObjectType& objects) {
1907fb33566SCarson Labrado             if (ec)
1917fb33566SCarson Labrado             {
192002d39b4SEd Tanous                 BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", "
193002d39b4SEd Tanous                                  << ec.message();
1947fb33566SCarson Labrado                 return;
1957fb33566SCarson Labrado             }
1967fb33566SCarson Labrado 
1977fb33566SCarson Labrado             // Maps a chosen alias representing a satellite BMC to a url
1987fb33566SCarson Labrado             // containing the information required to create a http
1997fb33566SCarson Labrado             // connection to the satellite
2007fb33566SCarson Labrado             std::unordered_map<std::string, boost::urls::url> satelliteInfo;
2017fb33566SCarson Labrado 
2027fb33566SCarson Labrado             findSatelliteConfigs(objects, satelliteInfo);
2037fb33566SCarson Labrado 
2047fb33566SCarson Labrado             if (!satelliteInfo.empty())
2057fb33566SCarson Labrado             {
2067fb33566SCarson Labrado                 BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with "
2077fb33566SCarson Labrado                                  << std::to_string(satelliteInfo.size())
2087fb33566SCarson Labrado                                  << " satellite BMCs";
2097fb33566SCarson Labrado             }
2107fb33566SCarson Labrado             else
2117fb33566SCarson Labrado             {
2127fb33566SCarson Labrado                 BMCWEB_LOG_DEBUG
2137fb33566SCarson Labrado                     << "No satellite BMCs detected.  Redfish Aggregation not enabled";
2147fb33566SCarson Labrado             }
2157fb33566SCarson Labrado             handler(satelliteInfo);
2167fb33566SCarson Labrado             },
2177fb33566SCarson Labrado             "xyz.openbmc_project.EntityManager", "/",
2187fb33566SCarson Labrado             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
2197fb33566SCarson Labrado     }
2207fb33566SCarson Labrado 
2217fb33566SCarson Labrado     // Search D-Bus objects for satellite config objects and add their
2227fb33566SCarson Labrado     // information if valid
2237fb33566SCarson Labrado     static void findSatelliteConfigs(
2247fb33566SCarson Labrado         const dbus::utility::ManagedObjectType& objects,
2257fb33566SCarson Labrado         std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
2267fb33566SCarson Labrado     {
2277fb33566SCarson Labrado         for (const auto& objectPath : objects)
2287fb33566SCarson Labrado         {
2297fb33566SCarson Labrado             for (const auto& interface : objectPath.second)
2307fb33566SCarson Labrado             {
2317fb33566SCarson Labrado                 if (interface.first ==
2327fb33566SCarson Labrado                     "xyz.openbmc_project.Configuration.SatelliteController")
2337fb33566SCarson Labrado                 {
2347fb33566SCarson Labrado                     BMCWEB_LOG_DEBUG << "Found Satellite Controller at "
2357fb33566SCarson Labrado                                      << objectPath.first.str;
2367fb33566SCarson Labrado 
23705916cefSCarson Labrado                     if (!satelliteInfo.empty())
23805916cefSCarson Labrado                     {
23905916cefSCarson Labrado                         BMCWEB_LOG_ERROR
24005916cefSCarson Labrado                             << "Redfish Aggregation only supports one satellite!";
24105916cefSCarson Labrado                         BMCWEB_LOG_DEBUG << "Clearing all satellite data";
24205916cefSCarson Labrado                         satelliteInfo.clear();
24305916cefSCarson Labrado                         return;
24405916cefSCarson Labrado                     }
24505916cefSCarson Labrado 
24605916cefSCarson Labrado                     // For now assume there will only be one satellite config.
24705916cefSCarson Labrado                     // Assign it the name/prefix "5B247A"
24805916cefSCarson Labrado                     addSatelliteConfig("5B247A", interface.second,
24905916cefSCarson Labrado                                        satelliteInfo);
2507fb33566SCarson Labrado                 }
2517fb33566SCarson Labrado             }
2527fb33566SCarson Labrado         }
2537fb33566SCarson Labrado     }
2547fb33566SCarson Labrado 
2557fb33566SCarson Labrado     // Parse the properties of a satellite config object and add the
2567fb33566SCarson Labrado     // configuration if the properties are valid
2577fb33566SCarson Labrado     static void addSatelliteConfig(
25805916cefSCarson Labrado         const std::string& name,
2597fb33566SCarson Labrado         const dbus::utility::DBusPropertiesMap& properties,
2607fb33566SCarson Labrado         std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
2617fb33566SCarson Labrado     {
2627fb33566SCarson Labrado         boost::urls::url url;
2637fb33566SCarson Labrado 
2647fb33566SCarson Labrado         for (const auto& prop : properties)
2657fb33566SCarson Labrado         {
26605916cefSCarson Labrado             if (prop.first == "Hostname")
2677fb33566SCarson Labrado             {
2687fb33566SCarson Labrado                 const std::string* propVal =
2697fb33566SCarson Labrado                     std::get_if<std::string>(&prop.second);
2707fb33566SCarson Labrado                 if (propVal == nullptr)
2717fb33566SCarson Labrado                 {
2727fb33566SCarson Labrado                     BMCWEB_LOG_ERROR << "Invalid Hostname value";
2737fb33566SCarson Labrado                     return;
2747fb33566SCarson Labrado                 }
2757fb33566SCarson Labrado                 url.set_host(*propVal);
2767fb33566SCarson Labrado             }
2777fb33566SCarson Labrado 
2787fb33566SCarson Labrado             else if (prop.first == "Port")
2797fb33566SCarson Labrado             {
2807fb33566SCarson Labrado                 const uint64_t* propVal = std::get_if<uint64_t>(&prop.second);
2817fb33566SCarson Labrado                 if (propVal == nullptr)
2827fb33566SCarson Labrado                 {
2837fb33566SCarson Labrado                     BMCWEB_LOG_ERROR << "Invalid Port value";
2847fb33566SCarson Labrado                     return;
2857fb33566SCarson Labrado                 }
2867fb33566SCarson Labrado 
2877fb33566SCarson Labrado                 if (*propVal > std::numeric_limits<uint16_t>::max())
2887fb33566SCarson Labrado                 {
2897fb33566SCarson Labrado                     BMCWEB_LOG_ERROR << "Port value out of range";
2907fb33566SCarson Labrado                     return;
2917fb33566SCarson Labrado                 }
2927fb33566SCarson Labrado                 url.set_port(static_cast<uint16_t>(*propVal));
2937fb33566SCarson Labrado             }
2947fb33566SCarson Labrado 
2957fb33566SCarson Labrado             else if (prop.first == "AuthType")
2967fb33566SCarson Labrado             {
2977fb33566SCarson Labrado                 const std::string* propVal =
2987fb33566SCarson Labrado                     std::get_if<std::string>(&prop.second);
2997fb33566SCarson Labrado                 if (propVal == nullptr)
3007fb33566SCarson Labrado                 {
3017fb33566SCarson Labrado                     BMCWEB_LOG_ERROR << "Invalid AuthType value";
3027fb33566SCarson Labrado                     return;
3037fb33566SCarson Labrado                 }
3047fb33566SCarson Labrado 
3057fb33566SCarson Labrado                 // For now assume authentication not required to communicate
3067fb33566SCarson Labrado                 // with the satellite BMC
3077fb33566SCarson Labrado                 if (*propVal != "None")
3087fb33566SCarson Labrado                 {
3097fb33566SCarson Labrado                     BMCWEB_LOG_ERROR
3107fb33566SCarson Labrado                         << "Unsupported AuthType value: " << *propVal
3117fb33566SCarson Labrado                         << ", only \"none\" is supported";
3127fb33566SCarson Labrado                     return;
3137fb33566SCarson Labrado                 }
3147fb33566SCarson Labrado                 url.set_scheme("http");
3157fb33566SCarson Labrado             }
3167fb33566SCarson Labrado         } // Finished reading properties
3177fb33566SCarson Labrado 
3187fb33566SCarson Labrado         // Make sure all required config information was made available
3197fb33566SCarson Labrado         if (url.host().empty())
3207fb33566SCarson Labrado         {
3217fb33566SCarson Labrado             BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host";
3227fb33566SCarson Labrado             return;
3237fb33566SCarson Labrado         }
3247fb33566SCarson Labrado 
3257fb33566SCarson Labrado         if (!url.has_port())
3267fb33566SCarson Labrado         {
3277fb33566SCarson Labrado             BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port";
3287fb33566SCarson Labrado             return;
3297fb33566SCarson Labrado         }
3307fb33566SCarson Labrado 
3317fb33566SCarson Labrado         if (!url.has_scheme())
3327fb33566SCarson Labrado         {
3337fb33566SCarson Labrado             BMCWEB_LOG_ERROR << "Satellite config " << name
3347fb33566SCarson Labrado                              << " missing AuthType";
3357fb33566SCarson Labrado             return;
3367fb33566SCarson Labrado         }
3377fb33566SCarson Labrado 
3387fb33566SCarson Labrado         std::string resultString;
3397fb33566SCarson Labrado         auto result = satelliteInfo.insert_or_assign(name, std::move(url));
3407fb33566SCarson Labrado         if (result.second)
3417fb33566SCarson Labrado         {
3427fb33566SCarson Labrado             resultString = "Added new satellite config ";
3437fb33566SCarson Labrado         }
3447fb33566SCarson Labrado         else
3457fb33566SCarson Labrado         {
3467fb33566SCarson Labrado             resultString = "Updated existing satellite config ";
3477fb33566SCarson Labrado         }
3487fb33566SCarson Labrado 
3497fb33566SCarson Labrado         BMCWEB_LOG_DEBUG << resultString << name << " at "
3507fb33566SCarson Labrado                          << result.first->second.scheme() << "://"
3517fb33566SCarson Labrado                          << result.first->second.encoded_host_and_port();
3527fb33566SCarson Labrado     }
3537fb33566SCarson Labrado 
35446a81465SCarson Labrado     enum AggregationType
35546a81465SCarson Labrado     {
35646a81465SCarson Labrado         Collection,
35746a81465SCarson Labrado         Resource,
35846a81465SCarson Labrado     };
35946a81465SCarson Labrado 
36046a81465SCarson Labrado     static void
36146a81465SCarson Labrado         startAggregation(AggregationType isCollection,
36246a81465SCarson Labrado                          const crow::Request& thisReq,
36346a81465SCarson Labrado                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
36446a81465SCarson Labrado     {
365*db18fc98SCarson Labrado         if ((isCollection == AggregationType::Collection) &&
366*db18fc98SCarson Labrado             (thisReq.method() != boost::beast::http::verb::get))
367*db18fc98SCarson Labrado         {
368*db18fc98SCarson Labrado             BMCWEB_LOG_DEBUG
369*db18fc98SCarson Labrado                 << "Only aggregate GET requests to top level collections";
370*db18fc98SCarson Labrado             return;
371*db18fc98SCarson Labrado         }
372*db18fc98SCarson Labrado 
37346a81465SCarson Labrado         // Create a copy of thisReq so we we can still locally process the req
37446a81465SCarson Labrado         std::error_code ec;
37546a81465SCarson Labrado         auto localReq = std::make_shared<crow::Request>(thisReq.req, ec);
37646a81465SCarson Labrado         if (ec)
37746a81465SCarson Labrado         {
37846a81465SCarson Labrado             BMCWEB_LOG_ERROR << "Failed to create copy of request";
37946a81465SCarson Labrado             if (isCollection != AggregationType::Collection)
38046a81465SCarson Labrado             {
38146a81465SCarson Labrado                 messages::internalError(asyncResp->res);
38246a81465SCarson Labrado             }
38346a81465SCarson Labrado             return;
38446a81465SCarson Labrado         }
38546a81465SCarson Labrado 
38646a81465SCarson Labrado         getSatelliteConfigs(std::bind_front(aggregateAndHandle, isCollection,
38746a81465SCarson Labrado                                             localReq, asyncResp));
38846a81465SCarson Labrado     }
38946a81465SCarson Labrado 
390*db18fc98SCarson Labrado     static void findSatellite(
39146a81465SCarson Labrado         const crow::Request& req,
39246a81465SCarson Labrado         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
39346a81465SCarson Labrado         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo,
39446a81465SCarson Labrado         std::string_view memberName)
39546a81465SCarson Labrado     {
39646a81465SCarson Labrado         // Determine if the resource ID begins with a known prefix
39746a81465SCarson Labrado         for (const auto& satellite : satelliteInfo)
39846a81465SCarson Labrado         {
39946a81465SCarson Labrado             std::string targetPrefix = satellite.first;
40046a81465SCarson Labrado             targetPrefix += "_";
40146a81465SCarson Labrado             if (memberName.starts_with(targetPrefix))
40246a81465SCarson Labrado             {
40346a81465SCarson Labrado                 BMCWEB_LOG_DEBUG << "\"" << satellite.first
40446a81465SCarson Labrado                                  << "\" is a known prefix";
40546a81465SCarson Labrado 
40646a81465SCarson Labrado                 // Remove the known prefix from the request's URI and
40746a81465SCarson Labrado                 // then forward to the associated satellite BMC
40846a81465SCarson Labrado                 getInstance().forwardRequest(req, asyncResp, satellite.first,
40946a81465SCarson Labrado                                              satelliteInfo);
41046a81465SCarson Labrado                 return;
41146a81465SCarson Labrado             }
41246a81465SCarson Labrado         }
413*db18fc98SCarson Labrado 
414*db18fc98SCarson Labrado         // We didn't recognize the prefix and need to return a 404
415*db18fc98SCarson Labrado         boost::urls::string_value name = req.urlView.segments().back();
416*db18fc98SCarson Labrado         std::string_view nameStr(name.data(), name.size());
417*db18fc98SCarson Labrado         messages::resourceNotFound(asyncResp->res, "", nameStr);
41846a81465SCarson Labrado     }
41946a81465SCarson Labrado 
42046a81465SCarson Labrado     // Intended to handle an incoming request based on if Redfish Aggregation
42146a81465SCarson Labrado     // is enabled.  Forwards request to satellite BMC if it exists.
42246a81465SCarson Labrado     static void aggregateAndHandle(
42346a81465SCarson Labrado         AggregationType isCollection,
42446a81465SCarson Labrado         const std::shared_ptr<crow::Request>& sharedReq,
42546a81465SCarson Labrado         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
42646a81465SCarson Labrado         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
42746a81465SCarson Labrado     {
42846a81465SCarson Labrado         if (sharedReq == nullptr)
42946a81465SCarson Labrado         {
43046a81465SCarson Labrado             return;
43146a81465SCarson Labrado         }
432*db18fc98SCarson Labrado 
433*db18fc98SCarson Labrado         // No satellite configs means we don't need to keep attempting to
434*db18fc98SCarson Labrado         // aggregate
435*db18fc98SCarson Labrado         if (satelliteInfo.empty())
436*db18fc98SCarson Labrado         {
437*db18fc98SCarson Labrado             // For collections we'll also handle the request locally so we
438*db18fc98SCarson Labrado             // don't need to write an error code
439*db18fc98SCarson Labrado             if (isCollection == AggregationType::Resource)
440*db18fc98SCarson Labrado             {
441*db18fc98SCarson Labrado                 boost::urls::string_value name =
442*db18fc98SCarson Labrado                     sharedReq->urlView.segments().back();
443*db18fc98SCarson Labrado                 std::string_view nameStr(name.data(), name.size());
444*db18fc98SCarson Labrado                 messages::resourceNotFound(asyncResp->res, "", nameStr);
445*db18fc98SCarson Labrado             }
446*db18fc98SCarson Labrado             return;
447*db18fc98SCarson Labrado         }
448*db18fc98SCarson Labrado 
44946a81465SCarson Labrado         const crow::Request& thisReq = *sharedReq;
45046a81465SCarson Labrado         BMCWEB_LOG_DEBUG << "Aggregation is enabled, begin processing of "
45146a81465SCarson Labrado                          << thisReq.target();
45246a81465SCarson Labrado 
45346a81465SCarson Labrado         // We previously determined the request is for a collection.  No need to
45446a81465SCarson Labrado         // check again
45546a81465SCarson Labrado         if (isCollection == AggregationType::Collection)
45646a81465SCarson Labrado         {
45746a81465SCarson Labrado             BMCWEB_LOG_DEBUG << "Aggregating a collection";
4584c30e226SCarson Labrado             // We need to use a specific response handler and send the
4594c30e226SCarson Labrado             // request to all known satellites
4604c30e226SCarson Labrado             getInstance().forwardCollectionRequests(thisReq, asyncResp,
4614c30e226SCarson Labrado                                                     satelliteInfo);
46246a81465SCarson Labrado             return;
46346a81465SCarson Labrado         }
46446a81465SCarson Labrado 
46546a81465SCarson Labrado         std::string updateServiceName;
46646a81465SCarson Labrado         std::string memberName;
46746a81465SCarson Labrado         if (crow::utility::readUrlSegments(
46846a81465SCarson Labrado                 thisReq.urlView, "redfish", "v1", "UpdateService",
46946a81465SCarson Labrado                 std::ref(updateServiceName), std::ref(memberName),
47046a81465SCarson Labrado                 crow::utility::OrMorePaths()))
47146a81465SCarson Labrado         {
47246a81465SCarson Labrado             // Must be FirmwareInventory or SoftwareInventory
473*db18fc98SCarson Labrado             findSatellite(thisReq, asyncResp, satelliteInfo, memberName);
47446a81465SCarson Labrado             return;
47546a81465SCarson Labrado         }
47646a81465SCarson Labrado 
47746a81465SCarson Labrado         std::string collectionName;
47846a81465SCarson Labrado         if (crow::utility::readUrlSegments(
47946a81465SCarson Labrado                 thisReq.urlView, "redfish", "v1", std::ref(collectionName),
48046a81465SCarson Labrado                 std::ref(memberName), crow::utility::OrMorePaths()))
48146a81465SCarson Labrado         {
482*db18fc98SCarson Labrado             findSatellite(thisReq, asyncResp, satelliteInfo, memberName);
483*db18fc98SCarson Labrado             return;
48446a81465SCarson Labrado         }
485*db18fc98SCarson Labrado 
486*db18fc98SCarson Labrado         // We shouldn't reach this point since we should've hit one of the
487*db18fc98SCarson Labrado         // previous exits
488*db18fc98SCarson Labrado         messages::internalError(asyncResp->res);
48946a81465SCarson Labrado     }
49046a81465SCarson Labrado 
49146a81465SCarson Labrado     // Attempt to forward a request to the satellite BMC associated with the
49246a81465SCarson Labrado     // prefix.
49346a81465SCarson Labrado     void forwardRequest(
49446a81465SCarson Labrado         const crow::Request& thisReq,
49546a81465SCarson Labrado         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
49646a81465SCarson Labrado         const std::string& prefix,
49746a81465SCarson Labrado         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
49846a81465SCarson Labrado     {
49946a81465SCarson Labrado         const auto& sat = satelliteInfo.find(prefix);
50046a81465SCarson Labrado         if (sat == satelliteInfo.end())
50146a81465SCarson Labrado         {
50246a81465SCarson Labrado             // Realistically this shouldn't get called since we perform an
50346a81465SCarson Labrado             // earlier check to make sure the prefix exists
50446a81465SCarson Labrado             BMCWEB_LOG_ERROR << "Unrecognized satellite prefix \"" << prefix
50546a81465SCarson Labrado                              << "\"";
50646a81465SCarson Labrado             return;
50746a81465SCarson Labrado         }
50846a81465SCarson Labrado 
50946a81465SCarson Labrado         // We need to strip the prefix from the request's path
51046a81465SCarson Labrado         std::string targetURI(thisReq.target());
51146a81465SCarson Labrado         size_t pos = targetURI.find(prefix + "_");
51246a81465SCarson Labrado         if (pos == std::string::npos)
51346a81465SCarson Labrado         {
51446a81465SCarson Labrado             // If this fails then something went wrong
51546a81465SCarson Labrado             BMCWEB_LOG_ERROR << "Error removing prefix \"" << prefix
51646a81465SCarson Labrado                              << "_\" from request URI";
51746a81465SCarson Labrado             messages::internalError(asyncResp->res);
51846a81465SCarson Labrado             return;
51946a81465SCarson Labrado         }
52046a81465SCarson Labrado         targetURI.erase(pos, prefix.size() + 1);
52146a81465SCarson Labrado 
52246a81465SCarson Labrado         std::function<void(crow::Response&)> cb =
5231c0bb5c6SCarson Labrado             std::bind_front(processResponse, prefix, asyncResp);
52446a81465SCarson Labrado 
52546a81465SCarson Labrado         std::string data = thisReq.req.body();
52646a81465SCarson Labrado         crow::HttpClient::getInstance().sendDataWithCallback(
52746a81465SCarson Labrado             data, id, std::string(sat->second.host()),
528e38778a5SAppaRao Puli             sat->second.port_number(), targetURI, false /*useSSL*/,
529e38778a5SAppaRao Puli             thisReq.fields, thisReq.method(), retryPolicyName, cb);
53046a81465SCarson Labrado     }
53146a81465SCarson Labrado 
5324c30e226SCarson Labrado     // Forward a request for a collection URI to each known satellite BMC
5334c30e226SCarson Labrado     void forwardCollectionRequests(
5344c30e226SCarson Labrado         const crow::Request& thisReq,
5354c30e226SCarson Labrado         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5364c30e226SCarson Labrado         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
5374c30e226SCarson Labrado     {
5384c30e226SCarson Labrado         for (const auto& sat : satelliteInfo)
5394c30e226SCarson Labrado         {
5404c30e226SCarson Labrado             std::function<void(crow::Response&)> cb = std::bind_front(
5414c30e226SCarson Labrado                 processCollectionResponse, sat.first, asyncResp);
5424c30e226SCarson Labrado 
5434c30e226SCarson Labrado             std::string targetURI(thisReq.target());
5444c30e226SCarson Labrado             std::string data = thisReq.req.body();
5454c30e226SCarson Labrado             crow::HttpClient::getInstance().sendDataWithCallback(
5464c30e226SCarson Labrado                 data, id, std::string(sat.second.host()),
547e38778a5SAppaRao Puli                 sat.second.port_number(), targetURI, false /*useSSL*/,
548e38778a5SAppaRao Puli                 thisReq.fields, thisReq.method(), retryPolicyName, cb);
5494c30e226SCarson Labrado         }
5504c30e226SCarson Labrado     }
5514c30e226SCarson Labrado 
55246a81465SCarson Labrado     // Processes the response returned by a satellite BMC and loads its
55346a81465SCarson Labrado     // contents into asyncResp
55446a81465SCarson Labrado     static void
5551c0bb5c6SCarson Labrado         processResponse(std::string_view prefix,
5561c0bb5c6SCarson Labrado                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55746a81465SCarson Labrado                         crow::Response& resp)
55846a81465SCarson Labrado     {
55946a81465SCarson Labrado         // No processing needed if the request wasn't successful
56046a81465SCarson Labrado         if (resp.resultInt() != 200)
56146a81465SCarson Labrado         {
56246a81465SCarson Labrado             BMCWEB_LOG_DEBUG << "No need to parse satellite response";
56346a81465SCarson Labrado             asyncResp->res.stringResponse = std::move(resp.stringResponse);
56446a81465SCarson Labrado             return;
56546a81465SCarson Labrado         }
56646a81465SCarson Labrado 
56746a81465SCarson Labrado         // The resp will not have a json component
56846a81465SCarson Labrado         // We need to create a json from resp's stringResponse
56946a81465SCarson Labrado         if (resp.getHeaderValue("Content-Type") == "application/json")
57046a81465SCarson Labrado         {
57146a81465SCarson Labrado             nlohmann::json jsonVal =
57246a81465SCarson Labrado                 nlohmann::json::parse(resp.body(), nullptr, false);
57346a81465SCarson Labrado             if (jsonVal.is_discarded())
57446a81465SCarson Labrado             {
57546a81465SCarson Labrado                 BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON";
57646a81465SCarson Labrado                 messages::operationFailed(asyncResp->res);
57746a81465SCarson Labrado                 return;
57846a81465SCarson Labrado             }
57946a81465SCarson Labrado 
58046a81465SCarson Labrado             BMCWEB_LOG_DEBUG << "Successfully parsed satellite response";
58146a81465SCarson Labrado 
58246a81465SCarson Labrado             // TODO: For collections we  want to add the satellite responses to
58346a81465SCarson Labrado             // our response rather than just straight overwriting them if our
58446a81465SCarson Labrado             // local handling was successful (i.e. would return a 200).
5851c0bb5c6SCarson Labrado             addPrefixes(jsonVal, prefix);
5861c0bb5c6SCarson Labrado 
5871c0bb5c6SCarson Labrado             BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response";
5881c0bb5c6SCarson Labrado 
58946a81465SCarson Labrado             asyncResp->res.stringResponse.emplace(
59046a81465SCarson Labrado                 boost::beast::http::response<
59146a81465SCarson Labrado                     boost::beast::http::string_body>{});
59246a81465SCarson Labrado             asyncResp->res.result(resp.result());
59346a81465SCarson Labrado             asyncResp->res.jsonValue = std::move(jsonVal);
59446a81465SCarson Labrado 
59546a81465SCarson Labrado             BMCWEB_LOG_DEBUG << "Finished writing asyncResp";
59646a81465SCarson Labrado         }
59746a81465SCarson Labrado         else
59846a81465SCarson Labrado         {
59946a81465SCarson Labrado             if (!resp.body().empty())
60046a81465SCarson Labrado             {
60146a81465SCarson Labrado                 // We received a 200 response without the correct Content-Type
60246a81465SCarson Labrado                 // so return an Operation Failed error
60346a81465SCarson Labrado                 BMCWEB_LOG_ERROR
60446a81465SCarson Labrado                     << "Satellite response must be of type \"application/json\"";
60546a81465SCarson Labrado                 messages::operationFailed(asyncResp->res);
60646a81465SCarson Labrado             }
60746a81465SCarson Labrado         }
60846a81465SCarson Labrado     }
60946a81465SCarson Labrado 
6104c30e226SCarson Labrado     // Processes the collection response returned by a satellite BMC and merges
6114c30e226SCarson Labrado     // its "@odata.id" values
6124c30e226SCarson Labrado     static void processCollectionResponse(
6134c30e226SCarson Labrado         const std::string& prefix,
6144c30e226SCarson Labrado         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
6154c30e226SCarson Labrado         crow::Response& resp)
6164c30e226SCarson Labrado     {
6174c30e226SCarson Labrado         if (resp.resultInt() != 200)
6184c30e226SCarson Labrado         {
6194c30e226SCarson Labrado             BMCWEB_LOG_DEBUG
6204c30e226SCarson Labrado                 << "Collection resource does not exist in satellite BMC \""
6214c30e226SCarson Labrado                 << prefix << "\"";
6224c30e226SCarson Labrado             // Return the error if we haven't had any successes
6234c30e226SCarson Labrado             if (asyncResp->res.resultInt() != 200)
6244c30e226SCarson Labrado             {
6254c30e226SCarson Labrado                 asyncResp->res.stringResponse = std::move(resp.stringResponse);
6264c30e226SCarson Labrado             }
6274c30e226SCarson Labrado             return;
6284c30e226SCarson Labrado         }
6294c30e226SCarson Labrado 
6304c30e226SCarson Labrado         // The resp will not have a json component
6314c30e226SCarson Labrado         // We need to create a json from resp's stringResponse
6324c30e226SCarson Labrado         if (resp.getHeaderValue("Content-Type") == "application/json")
6334c30e226SCarson Labrado         {
6344c30e226SCarson Labrado             nlohmann::json jsonVal =
6354c30e226SCarson Labrado                 nlohmann::json::parse(resp.body(), nullptr, false);
6364c30e226SCarson Labrado             if (jsonVal.is_discarded())
6374c30e226SCarson Labrado             {
6384c30e226SCarson Labrado                 BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON";
6394c30e226SCarson Labrado 
6404c30e226SCarson Labrado                 // Notify the user if doing so won't overwrite a valid response
6414c30e226SCarson Labrado                 if ((asyncResp->res.resultInt() != 200) &&
6424c30e226SCarson Labrado                     (asyncResp->res.resultInt() != 502))
6434c30e226SCarson Labrado                 {
6444c30e226SCarson Labrado                     messages::operationFailed(asyncResp->res);
6454c30e226SCarson Labrado                 }
6464c30e226SCarson Labrado                 return;
6474c30e226SCarson Labrado             }
6484c30e226SCarson Labrado 
6494c30e226SCarson Labrado             BMCWEB_LOG_DEBUG << "Successfully parsed satellite response";
6504c30e226SCarson Labrado 
6514c30e226SCarson Labrado             // Now we need to add the prefix to the URIs contained in the
6524c30e226SCarson Labrado             // response.
6534c30e226SCarson Labrado             addPrefixes(jsonVal, prefix);
6544c30e226SCarson Labrado 
6554c30e226SCarson Labrado             BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response";
6564c30e226SCarson Labrado 
6574c30e226SCarson Labrado             // If this resource collection does not exist on the aggregating bmc
6584c30e226SCarson Labrado             // and has not already been added from processing the response from
6594c30e226SCarson Labrado             // a different satellite then we need to completely overwrite
6604c30e226SCarson Labrado             // asyncResp
6614c30e226SCarson Labrado             if (asyncResp->res.resultInt() != 200)
6624c30e226SCarson Labrado             {
6634c30e226SCarson Labrado                 // We only want to aggregate collections that contain a
6644c30e226SCarson Labrado                 // "Members" array
6654c30e226SCarson Labrado                 if ((!jsonVal.contains("Members")) &&
6664c30e226SCarson Labrado                     (!jsonVal["Members"].is_array()))
6674c30e226SCarson Labrado                 {
6684c30e226SCarson Labrado                     BMCWEB_LOG_DEBUG
6694c30e226SCarson Labrado                         << "Skipping aggregating unsupported resource";
6704c30e226SCarson Labrado                     return;
6714c30e226SCarson Labrado                 }
6724c30e226SCarson Labrado 
6734c30e226SCarson Labrado                 BMCWEB_LOG_DEBUG
6744c30e226SCarson Labrado                     << "Collection does not exist, overwriting asyncResp";
6754c30e226SCarson Labrado                 asyncResp->res.stringResponse.emplace(
6764c30e226SCarson Labrado                     boost::beast::http::response<
6774c30e226SCarson Labrado                         boost::beast::http::string_body>{});
6784c30e226SCarson Labrado                 asyncResp->res.result(resp.result());
6794c30e226SCarson Labrado                 asyncResp->res.jsonValue = std::move(jsonVal);
6804c30e226SCarson Labrado 
6814c30e226SCarson Labrado                 BMCWEB_LOG_DEBUG << "Finished overwriting asyncResp";
6824c30e226SCarson Labrado             }
6834c30e226SCarson Labrado             else
6844c30e226SCarson Labrado             {
6854c30e226SCarson Labrado                 // We only want to aggregate collections that contain a
6864c30e226SCarson Labrado                 // "Members" array
6874c30e226SCarson Labrado                 if ((!asyncResp->res.jsonValue.contains("Members")) &&
6884c30e226SCarson Labrado                     (!asyncResp->res.jsonValue["Members"].is_array()))
6894c30e226SCarson Labrado 
6904c30e226SCarson Labrado                 {
6914c30e226SCarson Labrado                     BMCWEB_LOG_DEBUG
6924c30e226SCarson Labrado                         << "Skipping aggregating unsupported resource";
6934c30e226SCarson Labrado                     return;
6944c30e226SCarson Labrado                 }
6954c30e226SCarson Labrado 
6964c30e226SCarson Labrado                 BMCWEB_LOG_DEBUG << "Adding aggregated resources from \""
6974c30e226SCarson Labrado                                  << prefix << "\" to collection";
6984c30e226SCarson Labrado 
6994c30e226SCarson Labrado                 // TODO: This is a potential race condition with multiple
7004c30e226SCarson Labrado                 // satellites and the aggregating bmc attempting to write to
7014c30e226SCarson Labrado                 // update this array.  May need to cascade calls to the next
7024c30e226SCarson Labrado                 // satellite at the end of this function.
7034c30e226SCarson Labrado                 // This is presumably not a concern when there is only a single
7044c30e226SCarson Labrado                 // satellite since the aggregating bmc should have completed
7054c30e226SCarson Labrado                 // before the response is received from the satellite.
7064c30e226SCarson Labrado 
7074c30e226SCarson Labrado                 auto& members = asyncResp->res.jsonValue["Members"];
7084c30e226SCarson Labrado                 auto& satMembers = jsonVal["Members"];
7094c30e226SCarson Labrado                 for (auto& satMem : satMembers)
7104c30e226SCarson Labrado                 {
7114c30e226SCarson Labrado                     members.push_back(std::move(satMem));
7124c30e226SCarson Labrado                 }
7134c30e226SCarson Labrado                 asyncResp->res.jsonValue["Members@odata.count"] =
7144c30e226SCarson Labrado                     members.size();
7154c30e226SCarson Labrado 
7164c30e226SCarson Labrado                 // TODO: Do we need to sort() after updating the array?
7174c30e226SCarson Labrado             }
7184c30e226SCarson Labrado         }
7194c30e226SCarson Labrado         else
7204c30e226SCarson Labrado         {
7214c30e226SCarson Labrado             BMCWEB_LOG_ERROR << "Received unparsable response from \"" << prefix
7224c30e226SCarson Labrado                              << "\"";
7234c30e226SCarson Labrado             // We received as response that was not a json
7244c30e226SCarson Labrado             // Notify the user only if we did not receive any valid responses,
7254c30e226SCarson Labrado             // if the resource collection does not already exist on the
7264c30e226SCarson Labrado             // aggregating BMC, and if we did not already set this warning due
7274c30e226SCarson Labrado             // to a failure from a different satellite
7284c30e226SCarson Labrado             if ((asyncResp->res.resultInt() != 200) &&
7294c30e226SCarson Labrado                 (asyncResp->res.resultInt() != 502))
7304c30e226SCarson Labrado             {
7314c30e226SCarson Labrado                 messages::operationFailed(asyncResp->res);
7324c30e226SCarson Labrado             }
7334c30e226SCarson Labrado         }
7344c30e226SCarson Labrado     } // End processCollectionResponse()
7354c30e226SCarson Labrado 
7367fb33566SCarson Labrado   public:
7377fb33566SCarson Labrado     RedfishAggregator(const RedfishAggregator&) = delete;
7387fb33566SCarson Labrado     RedfishAggregator& operator=(const RedfishAggregator&) = delete;
7397fb33566SCarson Labrado     RedfishAggregator(RedfishAggregator&&) = delete;
7407fb33566SCarson Labrado     RedfishAggregator& operator=(RedfishAggregator&&) = delete;
7417fb33566SCarson Labrado     ~RedfishAggregator() = default;
7427fb33566SCarson Labrado 
7437fb33566SCarson Labrado     static RedfishAggregator& getInstance()
7447fb33566SCarson Labrado     {
7457fb33566SCarson Labrado         static RedfishAggregator handler;
7467fb33566SCarson Labrado         return handler;
7477fb33566SCarson Labrado     }
74805916cefSCarson Labrado 
74905916cefSCarson Labrado     // Entry point to Redfish Aggregation
75005916cefSCarson Labrado     // Returns Result stating whether or not we still need to locally handle the
75105916cefSCarson Labrado     // request
75205916cefSCarson Labrado     static Result
75305916cefSCarson Labrado         beginAggregation(const crow::Request& thisReq,
75405916cefSCarson Labrado                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
75505916cefSCarson Labrado     {
75605916cefSCarson Labrado         using crow::utility::OrMorePaths;
75705916cefSCarson Labrado         using crow::utility::readUrlSegments;
75805916cefSCarson Labrado         const boost::urls::url_view& url = thisReq.urlView;
75905916cefSCarson Labrado         // UpdateService is the only top level resource that is not a Collection
76005916cefSCarson Labrado         if (readUrlSegments(url, "redfish", "v1", "UpdateService"))
76105916cefSCarson Labrado         {
76205916cefSCarson Labrado             return Result::LocalHandle;
76305916cefSCarson Labrado         }
76446a81465SCarson Labrado         if (readUrlSegments(url, "redfish", "v1", "UpdateService",
76546a81465SCarson Labrado                             "SoftwareInventory") ||
76646a81465SCarson Labrado             readUrlSegments(url, "redfish", "v1", "UpdateService",
76746a81465SCarson Labrado                             "FirmwareInventory"))
76846a81465SCarson Labrado         {
76946a81465SCarson Labrado             startAggregation(AggregationType::Collection, thisReq, asyncResp);
77046a81465SCarson Labrado             return Result::LocalHandle;
77146a81465SCarson Labrado         }
77205916cefSCarson Labrado 
77305916cefSCarson Labrado         // Is the request for a resource collection?:
77405916cefSCarson Labrado         // /redfish/v1/<resource>
77505916cefSCarson Labrado         // e.g. /redfish/v1/Chassis
77605916cefSCarson Labrado         std::string collectionName;
77705916cefSCarson Labrado         if (readUrlSegments(url, "redfish", "v1", std::ref(collectionName)))
77805916cefSCarson Labrado         {
77946a81465SCarson Labrado             startAggregation(AggregationType::Collection, thisReq, asyncResp);
78005916cefSCarson Labrado             return Result::LocalHandle;
78105916cefSCarson Labrado         }
78205916cefSCarson Labrado 
78305916cefSCarson Labrado         // We know that the ID of an aggregated resource will begin with
78405916cefSCarson Labrado         // "5B247A".  For the most part the URI will begin like this:
78505916cefSCarson Labrado         // /redfish/v1/<resource>/<resource ID>
78605916cefSCarson Labrado         // Note, FirmwareInventory and SoftwareInventory are "special" because
78705916cefSCarson Labrado         // they are two levels deep, but still need aggregated
78805916cefSCarson Labrado         // /redfish/v1/UpdateService/FirmwareInventory/<FirmwareInventory ID>
78905916cefSCarson Labrado         // /redfish/v1/UpdateService/SoftwareInventory/<SoftwareInventory ID>
79005916cefSCarson Labrado         std::string memberName;
79105916cefSCarson Labrado         if (readUrlSegments(url, "redfish", "v1", "UpdateService",
79205916cefSCarson Labrado                             "SoftwareInventory", std::ref(memberName),
79305916cefSCarson Labrado                             OrMorePaths()) ||
79405916cefSCarson Labrado             readUrlSegments(url, "redfish", "v1", "UpdateService",
79505916cefSCarson Labrado                             "FirmwareInventory", std::ref(memberName),
79605916cefSCarson Labrado                             OrMorePaths()) ||
79705916cefSCarson Labrado             readUrlSegments(url, "redfish", "v1", std::ref(collectionName),
79805916cefSCarson Labrado                             std::ref(memberName), OrMorePaths()))
79905916cefSCarson Labrado         {
80005916cefSCarson Labrado             if (memberName.starts_with("5B247A"))
80105916cefSCarson Labrado             {
80205916cefSCarson Labrado                 BMCWEB_LOG_DEBUG << "Need to forward a request";
80305916cefSCarson Labrado 
80446a81465SCarson Labrado                 // Extract the prefix from the request's URI, retrieve the
80546a81465SCarson Labrado                 // associated satellite config information, and then forward the
80646a81465SCarson Labrado                 // request to that satellite.
80746a81465SCarson Labrado                 startAggregation(AggregationType::Resource, thisReq, asyncResp);
80805916cefSCarson Labrado                 return Result::NoLocalHandle;
80905916cefSCarson Labrado             }
81005916cefSCarson Labrado             return Result::LocalHandle;
81105916cefSCarson Labrado         }
81205916cefSCarson Labrado 
81305916cefSCarson Labrado         BMCWEB_LOG_DEBUG << "Aggregation not required";
81405916cefSCarson Labrado         return Result::LocalHandle;
81505916cefSCarson Labrado     }
8167fb33566SCarson Labrado };
8177fb33566SCarson Labrado 
8187fb33566SCarson Labrado } // namespace redfish
819