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