17fb33566SCarson Labrado #pragma once 27fb33566SCarson Labrado 33ccb3adbSEd Tanous #include "aggregation_utils.hpp" 43ccb3adbSEd Tanous #include "dbus_utility.hpp" 53ccb3adbSEd Tanous #include "error_messages.hpp" 63ccb3adbSEd Tanous #include "http_client.hpp" 73ccb3adbSEd Tanous #include "http_connection.hpp" 83ccb3adbSEd Tanous 9411e6a11SCarson Labrado #include <boost/algorithm/string/predicate.hpp> 107fb33566SCarson Labrado 117e8890c5SCarson Labrado #include <array> 127e8890c5SCarson Labrado 137fb33566SCarson Labrado namespace redfish 147fb33566SCarson Labrado { 157fb33566SCarson Labrado 1605916cefSCarson Labrado enum class Result 1705916cefSCarson Labrado { 1805916cefSCarson Labrado LocalHandle, 1905916cefSCarson Labrado NoLocalHandle 2005916cefSCarson Labrado }; 2105916cefSCarson Labrado 227e8890c5SCarson Labrado // clang-format off 237e8890c5SCarson Labrado // These are all of the properties as of version 2022.2 of the Redfish Resource 247e8890c5SCarson Labrado // and Schema Guide whose Type is "string (URI)" and the name does not end in a 257e8890c5SCarson Labrado // case-insensitive form of "uri". That version of the schema is associated 267e8890c5SCarson Labrado // with version 1.16.0 of the Redfish Specification. Going forward, new URI 277e8890c5SCarson Labrado // properties should end in URI so this list should not need to be maintained as 287e8890c5SCarson Labrado // the spec is updated. NOTE: These have been pre-sorted in order to be 297e8890c5SCarson Labrado // compatible with binary search 307e8890c5SCarson Labrado constexpr std::array nonUriProperties{ 317e8890c5SCarson Labrado "@Redfish.ActionInfo", 327e8890c5SCarson Labrado // "@odata.context", // We can't fix /redfish/v1/$metadata URIs 337e8890c5SCarson Labrado "@odata.id", 347e8890c5SCarson Labrado // "Destination", // Only used by EventService and won't be a Redfish URI 357e8890c5SCarson Labrado // "HostName", // Isn't actually a Redfish URI 367e8890c5SCarson Labrado "Image", 377e8890c5SCarson Labrado "MetricProperty", 38*32d7d8ebSCarson Labrado // "OriginOfCondition", // Is URI when in request, but is object in response 397e8890c5SCarson Labrado "TaskMonitor", 407e8890c5SCarson Labrado "target", // normal string, but target URI for POST to invoke an action 417e8890c5SCarson Labrado }; 427e8890c5SCarson Labrado // clang-format on 437e8890c5SCarson Labrado 447e8890c5SCarson Labrado // Determines if the passed property contains a URI. Those property names 457e8890c5SCarson Labrado // either end with a case-insensitive version of "uri" or are specifically 467e8890c5SCarson Labrado // defined in the above array. 477e8890c5SCarson Labrado inline bool isPropertyUri(const std::string_view propertyName) 487e8890c5SCarson Labrado { 497e8890c5SCarson Labrado return boost::iends_with(propertyName, "uri") || 507e8890c5SCarson Labrado std::binary_search(nonUriProperties.begin(), nonUriProperties.end(), 517e8890c5SCarson Labrado propertyName); 527e8890c5SCarson Labrado } 537e8890c5SCarson Labrado 541c0bb5c6SCarson Labrado static void addPrefixToItem(nlohmann::json& item, std::string_view prefix) 551c0bb5c6SCarson Labrado { 561c0bb5c6SCarson Labrado std::string* strValue = item.get_ptr<std::string*>(); 571c0bb5c6SCarson Labrado if (strValue == nullptr) 581c0bb5c6SCarson Labrado { 591c0bb5c6SCarson Labrado BMCWEB_LOG_CRITICAL << "Field wasn't a string????"; 601c0bb5c6SCarson Labrado return; 611c0bb5c6SCarson Labrado } 621c0bb5c6SCarson Labrado // Make sure the value is a properly formatted URI 631c0bb5c6SCarson Labrado auto parsed = boost::urls::parse_relative_ref(*strValue); 641c0bb5c6SCarson Labrado if (!parsed) 651c0bb5c6SCarson Labrado { 661c0bb5c6SCarson Labrado BMCWEB_LOG_CRITICAL << "Couldn't parse URI from resource " << *strValue; 671c0bb5c6SCarson Labrado return; 681c0bb5c6SCarson Labrado } 691c0bb5c6SCarson Labrado 701c0bb5c6SCarson Labrado boost::urls::url_view thisUrl = *parsed; 711c0bb5c6SCarson Labrado 72411e6a11SCarson Labrado // We don't need to aggregate JsonSchemas due to potential issues such as 73411e6a11SCarson Labrado // version mismatches between aggregator and satellite BMCs. For now 74411e6a11SCarson Labrado // assume that the aggregator has all the schemas and versions that the 75411e6a11SCarson Labrado // aggregated server has. 76411e6a11SCarson Labrado if (crow::utility::readUrlSegments(thisUrl, "redfish", "v1", "JsonSchemas", 77411e6a11SCarson Labrado crow::utility::OrMorePaths())) 78411e6a11SCarson Labrado { 79411e6a11SCarson Labrado BMCWEB_LOG_DEBUG << "Skipping JsonSchemas URI prefix fixing"; 80411e6a11SCarson Labrado return; 81411e6a11SCarson Labrado } 82411e6a11SCarson Labrado 8311987af6SCarson Labrado // The first two segments should be "/redfish/v1". We need to check that 8411987af6SCarson Labrado // before we can search topCollections 8511987af6SCarson Labrado if (!crow::utility::readUrlSegments(thisUrl, "redfish", "v1", 8611987af6SCarson Labrado crow::utility::OrMorePaths())) 871c0bb5c6SCarson Labrado { 881c0bb5c6SCarson Labrado return; 891c0bb5c6SCarson Labrado } 901c0bb5c6SCarson Labrado 9111987af6SCarson Labrado // Check array adding a segment each time until collection is identified 9211987af6SCarson Labrado // Add prefix to segment after the collection 9311987af6SCarson Labrado const boost::urls::segments_view urlSegments = thisUrl.segments(); 9411987af6SCarson Labrado bool addedPrefix = false; 9511987af6SCarson Labrado boost::urls::url url("/"); 9611987af6SCarson Labrado boost::urls::segments_view::iterator it = urlSegments.begin(); 9711987af6SCarson Labrado const boost::urls::segments_view::const_iterator end = urlSegments.end(); 9811987af6SCarson Labrado 9911987af6SCarson Labrado // Skip past the leading "/redfish/v1" 10011987af6SCarson Labrado it++; 10111987af6SCarson Labrado it++; 10211987af6SCarson Labrado for (; it != end; it++) 1031c0bb5c6SCarson Labrado { 10411987af6SCarson Labrado // Trailing "/" will result in an empty segment. In that case we need 10511987af6SCarson Labrado // to return so we don't apply a prefix to top level collections such 10611987af6SCarson Labrado // as "/redfish/v1/Chassis/" 10711987af6SCarson Labrado if ((*it).empty()) 10811987af6SCarson Labrado { 109411e6a11SCarson Labrado return; 1101c0bb5c6SCarson Labrado } 1111c0bb5c6SCarson Labrado 11211987af6SCarson Labrado if (std::binary_search(topCollections.begin(), topCollections.end(), 11311987af6SCarson Labrado url.buffer())) 1141c0bb5c6SCarson Labrado { 11511987af6SCarson Labrado std::string collectionItem(prefix); 11611987af6SCarson Labrado collectionItem += "_" + (*it); 11711987af6SCarson Labrado url.segments().push_back(collectionItem); 11811987af6SCarson Labrado it++; 11911987af6SCarson Labrado addedPrefix = true; 12011987af6SCarson Labrado break; 12111987af6SCarson Labrado } 12211987af6SCarson Labrado 12311987af6SCarson Labrado url.segments().push_back(*it); 12411987af6SCarson Labrado } 12511987af6SCarson Labrado 12611987af6SCarson Labrado // Finish constructing the URL here (if needed) to avoid additional checks 12711987af6SCarson Labrado for (; it != end; it++) 12811987af6SCarson Labrado { 12911987af6SCarson Labrado url.segments().push_back(*it); 13011987af6SCarson Labrado } 13111987af6SCarson Labrado 13211987af6SCarson Labrado if (addedPrefix) 13311987af6SCarson Labrado { 13411987af6SCarson Labrado url.segments().insert(url.segments().begin(), {"redfish", "v1"}); 13511987af6SCarson Labrado item = url; 1361c0bb5c6SCarson Labrado } 1371c0bb5c6SCarson Labrado } 1381c0bb5c6SCarson Labrado 1391c0bb5c6SCarson Labrado // Search the json for all URIs and add the supplied prefix if the URI is for 1407e8890c5SCarson Labrado // an aggregated resource. 1411c0bb5c6SCarson Labrado static void addPrefixes(nlohmann::json& json, std::string_view prefix) 1421c0bb5c6SCarson Labrado { 1431c0bb5c6SCarson Labrado nlohmann::json::object_t* object = 1441c0bb5c6SCarson Labrado json.get_ptr<nlohmann::json::object_t*>(); 1451c0bb5c6SCarson Labrado if (object != nullptr) 1461c0bb5c6SCarson Labrado { 1471c0bb5c6SCarson Labrado for (std::pair<const std::string, nlohmann::json>& item : *object) 1481c0bb5c6SCarson Labrado { 1497e8890c5SCarson Labrado if (isPropertyUri(item.first)) 1501c0bb5c6SCarson Labrado { 1517e8890c5SCarson Labrado addPrefixToItem(item.second, prefix); 1521c0bb5c6SCarson Labrado continue; 1531c0bb5c6SCarson Labrado } 1541c0bb5c6SCarson Labrado 1551c0bb5c6SCarson Labrado // Recusively parse the rest of the json 1561c0bb5c6SCarson Labrado addPrefixes(item.second, prefix); 1571c0bb5c6SCarson Labrado } 1581c0bb5c6SCarson Labrado return; 1591c0bb5c6SCarson Labrado } 1601c0bb5c6SCarson Labrado nlohmann::json::array_t* array = json.get_ptr<nlohmann::json::array_t*>(); 1611c0bb5c6SCarson Labrado if (array != nullptr) 1621c0bb5c6SCarson Labrado { 1631c0bb5c6SCarson Labrado for (nlohmann::json& item : *array) 1641c0bb5c6SCarson Labrado { 1651c0bb5c6SCarson Labrado addPrefixes(item, prefix); 1661c0bb5c6SCarson Labrado } 1671c0bb5c6SCarson Labrado } 1681c0bb5c6SCarson Labrado } 1691c0bb5c6SCarson Labrado 1707fb33566SCarson Labrado class RedfishAggregator 1717fb33566SCarson Labrado { 1727fb33566SCarson Labrado private: 173a7a80296SCarson Labrado const std::string retryPolicyName = "RedfishAggregation"; 174ce969437SCarson Labrado const std::string retryPolicyAction = "TerminateAfterRetries"; 175ce969437SCarson Labrado const uint32_t retryAttempts = 1; 176a7a80296SCarson Labrado const uint32_t retryTimeoutInterval = 0; 17746a81465SCarson Labrado const std::string id = "Aggregator"; 178a7a80296SCarson Labrado 1797fb33566SCarson Labrado RedfishAggregator() 1807fb33566SCarson Labrado { 1817fb33566SCarson Labrado getSatelliteConfigs(constructorCallback); 182a7a80296SCarson Labrado 183a7a80296SCarson Labrado // Setup the retry policy to be used by Redfish Aggregation 184a7a80296SCarson Labrado crow::HttpClient::getInstance().setRetryConfig( 185a7a80296SCarson Labrado retryAttempts, retryTimeoutInterval, aggregationRetryHandler, 186a7a80296SCarson Labrado retryPolicyName); 187ce969437SCarson Labrado crow::HttpClient::getInstance().setRetryPolicy(retryPolicyAction, 188ce969437SCarson Labrado retryPolicyName); 1897fb33566SCarson Labrado } 1907fb33566SCarson Labrado 191a7a80296SCarson Labrado static inline boost::system::error_code 192a7a80296SCarson Labrado aggregationRetryHandler(unsigned int respCode) 193a7a80296SCarson Labrado { 194*32d7d8ebSCarson Labrado // Allow all response codes because we want to surface any satellite 195*32d7d8ebSCarson Labrado // issue to the client 196*32d7d8ebSCarson Labrado BMCWEB_LOG_DEBUG << "Received " << respCode 197*32d7d8ebSCarson Labrado << " response from satellite"; 198a7a80296SCarson Labrado return boost::system::errc::make_error_code( 199a7a80296SCarson Labrado boost::system::errc::success); 2009fa6d147SNan Zhou } 201a7a80296SCarson Labrado 2027fb33566SCarson Labrado // Dummy callback used by the Constructor so that it can report the number 2037fb33566SCarson Labrado // of satellite configs when the class is first created 2047fb33566SCarson Labrado static void constructorCallback( 2057fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 2067fb33566SCarson Labrado { 2077fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "There were " 2087fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 2097fb33566SCarson Labrado << " satellite configs found at startup"; 2107fb33566SCarson Labrado } 2117fb33566SCarson Labrado 2127fb33566SCarson Labrado // Polls D-Bus to get all available satellite config information 2137fb33566SCarson Labrado // Expects a handler which interacts with the returned configs 2147fb33566SCarson Labrado static void getSatelliteConfigs( 2157fb33566SCarson Labrado const std::function<void( 2167fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>&)>& handler) 2177fb33566SCarson Labrado { 2187fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Gathering satellite configs"; 2197fb33566SCarson Labrado crow::connections::systemBus->async_method_call( 2207fb33566SCarson Labrado [handler](const boost::system::error_code ec, 2217fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects) { 2227fb33566SCarson Labrado if (ec) 2237fb33566SCarson Labrado { 224002d39b4SEd Tanous BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", " 225002d39b4SEd Tanous << ec.message(); 2267fb33566SCarson Labrado return; 2277fb33566SCarson Labrado } 2287fb33566SCarson Labrado 2297fb33566SCarson Labrado // Maps a chosen alias representing a satellite BMC to a url 2307fb33566SCarson Labrado // containing the information required to create a http 2317fb33566SCarson Labrado // connection to the satellite 2327fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url> satelliteInfo; 2337fb33566SCarson Labrado 2347fb33566SCarson Labrado findSatelliteConfigs(objects, satelliteInfo); 2357fb33566SCarson Labrado 2367fb33566SCarson Labrado if (!satelliteInfo.empty()) 2377fb33566SCarson Labrado { 2387fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with " 2397fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 2407fb33566SCarson Labrado << " satellite BMCs"; 2417fb33566SCarson Labrado } 2427fb33566SCarson Labrado else 2437fb33566SCarson Labrado { 2447fb33566SCarson Labrado BMCWEB_LOG_DEBUG 2457fb33566SCarson Labrado << "No satellite BMCs detected. Redfish Aggregation not enabled"; 2467fb33566SCarson Labrado } 2477fb33566SCarson Labrado handler(satelliteInfo); 2487fb33566SCarson Labrado }, 249c106b67aSNan Zhou "xyz.openbmc_project.EntityManager", 250c106b67aSNan Zhou "/xyz/openbmc_project/inventory", 2517fb33566SCarson Labrado "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 2527fb33566SCarson Labrado } 2537fb33566SCarson Labrado 2547fb33566SCarson Labrado // Search D-Bus objects for satellite config objects and add their 2557fb33566SCarson Labrado // information if valid 2567fb33566SCarson Labrado static void findSatelliteConfigs( 2577fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects, 2587fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 2597fb33566SCarson Labrado { 2607fb33566SCarson Labrado for (const auto& objectPath : objects) 2617fb33566SCarson Labrado { 2627fb33566SCarson Labrado for (const auto& interface : objectPath.second) 2637fb33566SCarson Labrado { 2647fb33566SCarson Labrado if (interface.first == 2657fb33566SCarson Labrado "xyz.openbmc_project.Configuration.SatelliteController") 2667fb33566SCarson Labrado { 2677fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Found Satellite Controller at " 2687fb33566SCarson Labrado << objectPath.first.str; 2697fb33566SCarson Labrado 27005916cefSCarson Labrado if (!satelliteInfo.empty()) 27105916cefSCarson Labrado { 27205916cefSCarson Labrado BMCWEB_LOG_ERROR 27305916cefSCarson Labrado << "Redfish Aggregation only supports one satellite!"; 27405916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Clearing all satellite data"; 27505916cefSCarson Labrado satelliteInfo.clear(); 27605916cefSCarson Labrado return; 27705916cefSCarson Labrado } 27805916cefSCarson Labrado 27905916cefSCarson Labrado // For now assume there will only be one satellite config. 28005916cefSCarson Labrado // Assign it the name/prefix "5B247A" 28105916cefSCarson Labrado addSatelliteConfig("5B247A", interface.second, 28205916cefSCarson Labrado satelliteInfo); 2837fb33566SCarson Labrado } 2847fb33566SCarson Labrado } 2857fb33566SCarson Labrado } 2867fb33566SCarson Labrado } 2877fb33566SCarson Labrado 2887fb33566SCarson Labrado // Parse the properties of a satellite config object and add the 2897fb33566SCarson Labrado // configuration if the properties are valid 2907fb33566SCarson Labrado static void addSatelliteConfig( 29105916cefSCarson Labrado const std::string& name, 2927fb33566SCarson Labrado const dbus::utility::DBusPropertiesMap& properties, 2937fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 2947fb33566SCarson Labrado { 2957fb33566SCarson Labrado boost::urls::url url; 2967fb33566SCarson Labrado 2977fb33566SCarson Labrado for (const auto& prop : properties) 2987fb33566SCarson Labrado { 29905916cefSCarson Labrado if (prop.first == "Hostname") 3007fb33566SCarson Labrado { 3017fb33566SCarson Labrado const std::string* propVal = 3027fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 3037fb33566SCarson Labrado if (propVal == nullptr) 3047fb33566SCarson Labrado { 3057fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Hostname value"; 3067fb33566SCarson Labrado return; 3077fb33566SCarson Labrado } 3087fb33566SCarson Labrado url.set_host(*propVal); 3097fb33566SCarson Labrado } 3107fb33566SCarson Labrado 3117fb33566SCarson Labrado else if (prop.first == "Port") 3127fb33566SCarson Labrado { 3137fb33566SCarson Labrado const uint64_t* propVal = std::get_if<uint64_t>(&prop.second); 3147fb33566SCarson Labrado if (propVal == nullptr) 3157fb33566SCarson Labrado { 3167fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Port value"; 3177fb33566SCarson Labrado return; 3187fb33566SCarson Labrado } 3197fb33566SCarson Labrado 3207fb33566SCarson Labrado if (*propVal > std::numeric_limits<uint16_t>::max()) 3217fb33566SCarson Labrado { 3227fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Port value out of range"; 3237fb33566SCarson Labrado return; 3247fb33566SCarson Labrado } 325079360aeSEd Tanous url.set_port(std::to_string(static_cast<uint16_t>(*propVal))); 3267fb33566SCarson Labrado } 3277fb33566SCarson Labrado 3287fb33566SCarson Labrado else if (prop.first == "AuthType") 3297fb33566SCarson Labrado { 3307fb33566SCarson Labrado const std::string* propVal = 3317fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 3327fb33566SCarson Labrado if (propVal == nullptr) 3337fb33566SCarson Labrado { 3347fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid AuthType value"; 3357fb33566SCarson Labrado return; 3367fb33566SCarson Labrado } 3377fb33566SCarson Labrado 3387fb33566SCarson Labrado // For now assume authentication not required to communicate 3397fb33566SCarson Labrado // with the satellite BMC 3407fb33566SCarson Labrado if (*propVal != "None") 3417fb33566SCarson Labrado { 3427fb33566SCarson Labrado BMCWEB_LOG_ERROR 3437fb33566SCarson Labrado << "Unsupported AuthType value: " << *propVal 3447fb33566SCarson Labrado << ", only \"none\" is supported"; 3457fb33566SCarson Labrado return; 3467fb33566SCarson Labrado } 3477fb33566SCarson Labrado url.set_scheme("http"); 3487fb33566SCarson Labrado } 3497fb33566SCarson Labrado } // Finished reading properties 3507fb33566SCarson Labrado 3517fb33566SCarson Labrado // Make sure all required config information was made available 3527fb33566SCarson Labrado if (url.host().empty()) 3537fb33566SCarson Labrado { 3547fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host"; 3557fb33566SCarson Labrado return; 3567fb33566SCarson Labrado } 3577fb33566SCarson Labrado 3587fb33566SCarson Labrado if (!url.has_port()) 3597fb33566SCarson Labrado { 3607fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port"; 3617fb33566SCarson Labrado return; 3627fb33566SCarson Labrado } 3637fb33566SCarson Labrado 3647fb33566SCarson Labrado if (!url.has_scheme()) 3657fb33566SCarson Labrado { 3667fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name 3677fb33566SCarson Labrado << " missing AuthType"; 3687fb33566SCarson Labrado return; 3697fb33566SCarson Labrado } 3707fb33566SCarson Labrado 3717fb33566SCarson Labrado std::string resultString; 3727fb33566SCarson Labrado auto result = satelliteInfo.insert_or_assign(name, std::move(url)); 3737fb33566SCarson Labrado if (result.second) 3747fb33566SCarson Labrado { 3757fb33566SCarson Labrado resultString = "Added new satellite config "; 3767fb33566SCarson Labrado } 3777fb33566SCarson Labrado else 3787fb33566SCarson Labrado { 3797fb33566SCarson Labrado resultString = "Updated existing satellite config "; 3807fb33566SCarson Labrado } 3817fb33566SCarson Labrado 3827fb33566SCarson Labrado BMCWEB_LOG_DEBUG << resultString << name << " at " 3837fb33566SCarson Labrado << result.first->second.scheme() << "://" 3847fb33566SCarson Labrado << result.first->second.encoded_host_and_port(); 3857fb33566SCarson Labrado } 3867fb33566SCarson Labrado 38746a81465SCarson Labrado enum AggregationType 38846a81465SCarson Labrado { 38946a81465SCarson Labrado Collection, 39046a81465SCarson Labrado Resource, 39146a81465SCarson Labrado }; 39246a81465SCarson Labrado 39346a81465SCarson Labrado static void 39446a81465SCarson Labrado startAggregation(AggregationType isCollection, 39546a81465SCarson Labrado const crow::Request& thisReq, 39646a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 39746a81465SCarson Labrado { 398db18fc98SCarson Labrado if ((isCollection == AggregationType::Collection) && 399db18fc98SCarson Labrado (thisReq.method() != boost::beast::http::verb::get)) 400db18fc98SCarson Labrado { 401db18fc98SCarson Labrado BMCWEB_LOG_DEBUG 402db18fc98SCarson Labrado << "Only aggregate GET requests to top level collections"; 403db18fc98SCarson Labrado return; 404db18fc98SCarson Labrado } 405db18fc98SCarson Labrado 40646a81465SCarson Labrado // Create a copy of thisReq so we we can still locally process the req 40746a81465SCarson Labrado std::error_code ec; 40846a81465SCarson Labrado auto localReq = std::make_shared<crow::Request>(thisReq.req, ec); 40946a81465SCarson Labrado if (ec) 41046a81465SCarson Labrado { 41146a81465SCarson Labrado BMCWEB_LOG_ERROR << "Failed to create copy of request"; 41246a81465SCarson Labrado if (isCollection != AggregationType::Collection) 41346a81465SCarson Labrado { 41446a81465SCarson Labrado messages::internalError(asyncResp->res); 41546a81465SCarson Labrado } 41646a81465SCarson Labrado return; 41746a81465SCarson Labrado } 41846a81465SCarson Labrado 41946a81465SCarson Labrado getSatelliteConfigs(std::bind_front(aggregateAndHandle, isCollection, 42046a81465SCarson Labrado localReq, asyncResp)); 42146a81465SCarson Labrado } 42246a81465SCarson Labrado 423db18fc98SCarson Labrado static void findSatellite( 42446a81465SCarson Labrado const crow::Request& req, 42546a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 42646a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo, 42746a81465SCarson Labrado std::string_view memberName) 42846a81465SCarson Labrado { 42946a81465SCarson Labrado // Determine if the resource ID begins with a known prefix 43046a81465SCarson Labrado for (const auto& satellite : satelliteInfo) 43146a81465SCarson Labrado { 43246a81465SCarson Labrado std::string targetPrefix = satellite.first; 43346a81465SCarson Labrado targetPrefix += "_"; 43446a81465SCarson Labrado if (memberName.starts_with(targetPrefix)) 43546a81465SCarson Labrado { 43646a81465SCarson Labrado BMCWEB_LOG_DEBUG << "\"" << satellite.first 43746a81465SCarson Labrado << "\" is a known prefix"; 43846a81465SCarson Labrado 43946a81465SCarson Labrado // Remove the known prefix from the request's URI and 44046a81465SCarson Labrado // then forward to the associated satellite BMC 44146a81465SCarson Labrado getInstance().forwardRequest(req, asyncResp, satellite.first, 44246a81465SCarson Labrado satelliteInfo); 44346a81465SCarson Labrado return; 44446a81465SCarson Labrado } 44546a81465SCarson Labrado } 446db18fc98SCarson Labrado 447db18fc98SCarson Labrado // We didn't recognize the prefix and need to return a 404 44893f7a0d6SEd Tanous std::string nameStr = req.urlView.segments().back(); 449db18fc98SCarson Labrado messages::resourceNotFound(asyncResp->res, "", nameStr); 45046a81465SCarson Labrado } 45146a81465SCarson Labrado 45246a81465SCarson Labrado // Intended to handle an incoming request based on if Redfish Aggregation 45346a81465SCarson Labrado // is enabled. Forwards request to satellite BMC if it exists. 45446a81465SCarson Labrado static void aggregateAndHandle( 45546a81465SCarson Labrado AggregationType isCollection, 45646a81465SCarson Labrado const std::shared_ptr<crow::Request>& sharedReq, 45746a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 45846a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 45946a81465SCarson Labrado { 46046a81465SCarson Labrado if (sharedReq == nullptr) 46146a81465SCarson Labrado { 46246a81465SCarson Labrado return; 46346a81465SCarson Labrado } 464db18fc98SCarson Labrado 465db18fc98SCarson Labrado // No satellite configs means we don't need to keep attempting to 466db18fc98SCarson Labrado // aggregate 467db18fc98SCarson Labrado if (satelliteInfo.empty()) 468db18fc98SCarson Labrado { 469db18fc98SCarson Labrado // For collections we'll also handle the request locally so we 470db18fc98SCarson Labrado // don't need to write an error code 471db18fc98SCarson Labrado if (isCollection == AggregationType::Resource) 472db18fc98SCarson Labrado { 47393f7a0d6SEd Tanous std::string nameStr = sharedReq->urlView.segments().back(); 474db18fc98SCarson Labrado messages::resourceNotFound(asyncResp->res, "", nameStr); 475db18fc98SCarson Labrado } 476db18fc98SCarson Labrado return; 477db18fc98SCarson Labrado } 478db18fc98SCarson Labrado 47946a81465SCarson Labrado const crow::Request& thisReq = *sharedReq; 48046a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation is enabled, begin processing of " 48146a81465SCarson Labrado << thisReq.target(); 48246a81465SCarson Labrado 48346a81465SCarson Labrado // We previously determined the request is for a collection. No need to 48446a81465SCarson Labrado // check again 48546a81465SCarson Labrado if (isCollection == AggregationType::Collection) 48646a81465SCarson Labrado { 48746a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregating a collection"; 4884c30e226SCarson Labrado // We need to use a specific response handler and send the 4894c30e226SCarson Labrado // request to all known satellites 4904c30e226SCarson Labrado getInstance().forwardCollectionRequests(thisReq, asyncResp, 4914c30e226SCarson Labrado satelliteInfo); 49246a81465SCarson Labrado return; 49346a81465SCarson Labrado } 49446a81465SCarson Labrado 49546a81465SCarson Labrado std::string updateServiceName; 49646a81465SCarson Labrado std::string memberName; 49746a81465SCarson Labrado if (crow::utility::readUrlSegments( 49846a81465SCarson Labrado thisReq.urlView, "redfish", "v1", "UpdateService", 49946a81465SCarson Labrado std::ref(updateServiceName), std::ref(memberName), 50046a81465SCarson Labrado crow::utility::OrMorePaths())) 50146a81465SCarson Labrado { 50246a81465SCarson Labrado // Must be FirmwareInventory or SoftwareInventory 503db18fc98SCarson Labrado findSatellite(thisReq, asyncResp, satelliteInfo, memberName); 50446a81465SCarson Labrado return; 50546a81465SCarson Labrado } 50646a81465SCarson Labrado 50746a81465SCarson Labrado std::string collectionName; 50846a81465SCarson Labrado if (crow::utility::readUrlSegments( 50946a81465SCarson Labrado thisReq.urlView, "redfish", "v1", std::ref(collectionName), 51046a81465SCarson Labrado std::ref(memberName), crow::utility::OrMorePaths())) 51146a81465SCarson Labrado { 512db18fc98SCarson Labrado findSatellite(thisReq, asyncResp, satelliteInfo, memberName); 513db18fc98SCarson Labrado return; 51446a81465SCarson Labrado } 515db18fc98SCarson Labrado 516db18fc98SCarson Labrado // We shouldn't reach this point since we should've hit one of the 517db18fc98SCarson Labrado // previous exits 518db18fc98SCarson Labrado messages::internalError(asyncResp->res); 51946a81465SCarson Labrado } 52046a81465SCarson Labrado 52146a81465SCarson Labrado // Attempt to forward a request to the satellite BMC associated with the 52246a81465SCarson Labrado // prefix. 52346a81465SCarson Labrado void forwardRequest( 52446a81465SCarson Labrado const crow::Request& thisReq, 52546a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 52646a81465SCarson Labrado const std::string& prefix, 52746a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 52846a81465SCarson Labrado { 52946a81465SCarson Labrado const auto& sat = satelliteInfo.find(prefix); 53046a81465SCarson Labrado if (sat == satelliteInfo.end()) 53146a81465SCarson Labrado { 53246a81465SCarson Labrado // Realistically this shouldn't get called since we perform an 53346a81465SCarson Labrado // earlier check to make sure the prefix exists 53446a81465SCarson Labrado BMCWEB_LOG_ERROR << "Unrecognized satellite prefix \"" << prefix 53546a81465SCarson Labrado << "\""; 53646a81465SCarson Labrado return; 53746a81465SCarson Labrado } 53846a81465SCarson Labrado 53946a81465SCarson Labrado // We need to strip the prefix from the request's path 54046a81465SCarson Labrado std::string targetURI(thisReq.target()); 54146a81465SCarson Labrado size_t pos = targetURI.find(prefix + "_"); 54246a81465SCarson Labrado if (pos == std::string::npos) 54346a81465SCarson Labrado { 54446a81465SCarson Labrado // If this fails then something went wrong 54546a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error removing prefix \"" << prefix 54646a81465SCarson Labrado << "_\" from request URI"; 54746a81465SCarson Labrado messages::internalError(asyncResp->res); 54846a81465SCarson Labrado return; 54946a81465SCarson Labrado } 55046a81465SCarson Labrado targetURI.erase(pos, prefix.size() + 1); 55146a81465SCarson Labrado 55246a81465SCarson Labrado std::function<void(crow::Response&)> cb = 5531c0bb5c6SCarson Labrado std::bind_front(processResponse, prefix, asyncResp); 55446a81465SCarson Labrado 55546a81465SCarson Labrado std::string data = thisReq.req.body(); 55646a81465SCarson Labrado crow::HttpClient::getInstance().sendDataWithCallback( 55746a81465SCarson Labrado data, id, std::string(sat->second.host()), 558e38778a5SAppaRao Puli sat->second.port_number(), targetURI, false /*useSSL*/, 559e38778a5SAppaRao Puli thisReq.fields, thisReq.method(), retryPolicyName, cb); 56046a81465SCarson Labrado } 56146a81465SCarson Labrado 5624c30e226SCarson Labrado // Forward a request for a collection URI to each known satellite BMC 5634c30e226SCarson Labrado void forwardCollectionRequests( 5644c30e226SCarson Labrado const crow::Request& thisReq, 5654c30e226SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 5664c30e226SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 5674c30e226SCarson Labrado { 5684c30e226SCarson Labrado for (const auto& sat : satelliteInfo) 5694c30e226SCarson Labrado { 5704c30e226SCarson Labrado std::function<void(crow::Response&)> cb = std::bind_front( 5714c30e226SCarson Labrado processCollectionResponse, sat.first, asyncResp); 5724c30e226SCarson Labrado 5734c30e226SCarson Labrado std::string targetURI(thisReq.target()); 5744c30e226SCarson Labrado std::string data = thisReq.req.body(); 5754c30e226SCarson Labrado crow::HttpClient::getInstance().sendDataWithCallback( 5764c30e226SCarson Labrado data, id, std::string(sat.second.host()), 577e38778a5SAppaRao Puli sat.second.port_number(), targetURI, false /*useSSL*/, 578e38778a5SAppaRao Puli thisReq.fields, thisReq.method(), retryPolicyName, cb); 5794c30e226SCarson Labrado } 5804c30e226SCarson Labrado } 5814c30e226SCarson Labrado 582*32d7d8ebSCarson Labrado public: 583*32d7d8ebSCarson Labrado RedfishAggregator(const RedfishAggregator&) = delete; 584*32d7d8ebSCarson Labrado RedfishAggregator& operator=(const RedfishAggregator&) = delete; 585*32d7d8ebSCarson Labrado RedfishAggregator(RedfishAggregator&&) = delete; 586*32d7d8ebSCarson Labrado RedfishAggregator& operator=(RedfishAggregator&&) = delete; 587*32d7d8ebSCarson Labrado ~RedfishAggregator() = default; 588*32d7d8ebSCarson Labrado 589*32d7d8ebSCarson Labrado static RedfishAggregator& getInstance() 590*32d7d8ebSCarson Labrado { 591*32d7d8ebSCarson Labrado static RedfishAggregator handler; 592*32d7d8ebSCarson Labrado return handler; 593*32d7d8ebSCarson Labrado } 594*32d7d8ebSCarson Labrado 59546a81465SCarson Labrado // Processes the response returned by a satellite BMC and loads its 59646a81465SCarson Labrado // contents into asyncResp 59746a81465SCarson Labrado static void 5981c0bb5c6SCarson Labrado processResponse(std::string_view prefix, 5991c0bb5c6SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 60046a81465SCarson Labrado crow::Response& resp) 60146a81465SCarson Labrado { 602*32d7d8ebSCarson Labrado // We want to attempt prefix fixing regardless of response code 60346a81465SCarson Labrado // The resp will not have a json component 60446a81465SCarson Labrado // We need to create a json from resp's stringResponse 60546a81465SCarson Labrado if (resp.getHeaderValue("Content-Type") == "application/json") 60646a81465SCarson Labrado { 60746a81465SCarson Labrado nlohmann::json jsonVal = 60846a81465SCarson Labrado nlohmann::json::parse(resp.body(), nullptr, false); 60946a81465SCarson Labrado if (jsonVal.is_discarded()) 61046a81465SCarson Labrado { 61146a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON"; 61246a81465SCarson Labrado messages::operationFailed(asyncResp->res); 61346a81465SCarson Labrado return; 61446a81465SCarson Labrado } 61546a81465SCarson Labrado 61646a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Successfully parsed satellite response"; 61746a81465SCarson Labrado 6181c0bb5c6SCarson Labrado addPrefixes(jsonVal, prefix); 6191c0bb5c6SCarson Labrado 6201c0bb5c6SCarson Labrado BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response"; 6211c0bb5c6SCarson Labrado 62246a81465SCarson Labrado asyncResp->res.result(resp.result()); 62346a81465SCarson Labrado asyncResp->res.jsonValue = std::move(jsonVal); 62446a81465SCarson Labrado 62546a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Finished writing asyncResp"; 62646a81465SCarson Labrado } 62746a81465SCarson Labrado else 62846a81465SCarson Labrado { 62946a81465SCarson Labrado if (!resp.body().empty()) 63046a81465SCarson Labrado { 631*32d7d8ebSCarson Labrado // We received a valid response without the correct 632*32d7d8ebSCarson Labrado // Content-Type so return an Operation Failed error 63346a81465SCarson Labrado BMCWEB_LOG_ERROR 63446a81465SCarson Labrado << "Satellite response must be of type \"application/json\""; 63546a81465SCarson Labrado messages::operationFailed(asyncResp->res); 63646a81465SCarson Labrado } 63746a81465SCarson Labrado } 63846a81465SCarson Labrado } 63946a81465SCarson Labrado 6404c30e226SCarson Labrado // Processes the collection response returned by a satellite BMC and merges 6414c30e226SCarson Labrado // its "@odata.id" values 6424c30e226SCarson Labrado static void processCollectionResponse( 6434c30e226SCarson Labrado const std::string& prefix, 6444c30e226SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6454c30e226SCarson Labrado crow::Response& resp) 6464c30e226SCarson Labrado { 6474c30e226SCarson Labrado if (resp.resultInt() != 200) 6484c30e226SCarson Labrado { 6494c30e226SCarson Labrado BMCWEB_LOG_DEBUG 6504c30e226SCarson Labrado << "Collection resource does not exist in satellite BMC \"" 6514c30e226SCarson Labrado << prefix << "\""; 6524c30e226SCarson Labrado // Return the error if we haven't had any successes 6534c30e226SCarson Labrado if (asyncResp->res.resultInt() != 200) 6544c30e226SCarson Labrado { 6554c30e226SCarson Labrado asyncResp->res.stringResponse = std::move(resp.stringResponse); 6564c30e226SCarson Labrado } 6574c30e226SCarson Labrado return; 6584c30e226SCarson Labrado } 6594c30e226SCarson Labrado 6604c30e226SCarson Labrado // The resp will not have a json component 6614c30e226SCarson Labrado // We need to create a json from resp's stringResponse 6624c30e226SCarson Labrado if (resp.getHeaderValue("Content-Type") == "application/json") 6634c30e226SCarson Labrado { 6644c30e226SCarson Labrado nlohmann::json jsonVal = 6654c30e226SCarson Labrado nlohmann::json::parse(resp.body(), nullptr, false); 6664c30e226SCarson Labrado if (jsonVal.is_discarded()) 6674c30e226SCarson Labrado { 6684c30e226SCarson Labrado BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON"; 6694c30e226SCarson Labrado 6704c30e226SCarson Labrado // Notify the user if doing so won't overwrite a valid response 6714c30e226SCarson Labrado if ((asyncResp->res.resultInt() != 200) && 6724c30e226SCarson Labrado (asyncResp->res.resultInt() != 502)) 6734c30e226SCarson Labrado { 6744c30e226SCarson Labrado messages::operationFailed(asyncResp->res); 6754c30e226SCarson Labrado } 6764c30e226SCarson Labrado return; 6774c30e226SCarson Labrado } 6784c30e226SCarson Labrado 6794c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Successfully parsed satellite response"; 6804c30e226SCarson Labrado 6814c30e226SCarson Labrado // Now we need to add the prefix to the URIs contained in the 6824c30e226SCarson Labrado // response. 6834c30e226SCarson Labrado addPrefixes(jsonVal, prefix); 6844c30e226SCarson Labrado 6854c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response"; 6864c30e226SCarson Labrado 6874c30e226SCarson Labrado // If this resource collection does not exist on the aggregating bmc 6884c30e226SCarson Labrado // and has not already been added from processing the response from 6894c30e226SCarson Labrado // a different satellite then we need to completely overwrite 6904c30e226SCarson Labrado // asyncResp 6914c30e226SCarson Labrado if (asyncResp->res.resultInt() != 200) 6924c30e226SCarson Labrado { 6934c30e226SCarson Labrado // We only want to aggregate collections that contain a 6944c30e226SCarson Labrado // "Members" array 6954c30e226SCarson Labrado if ((!jsonVal.contains("Members")) && 6964c30e226SCarson Labrado (!jsonVal["Members"].is_array())) 6974c30e226SCarson Labrado { 6984c30e226SCarson Labrado BMCWEB_LOG_DEBUG 6994c30e226SCarson Labrado << "Skipping aggregating unsupported resource"; 7004c30e226SCarson Labrado return; 7014c30e226SCarson Labrado } 7024c30e226SCarson Labrado 7034c30e226SCarson Labrado BMCWEB_LOG_DEBUG 7044c30e226SCarson Labrado << "Collection does not exist, overwriting asyncResp"; 7054c30e226SCarson Labrado asyncResp->res.result(resp.result()); 7064c30e226SCarson Labrado asyncResp->res.jsonValue = std::move(jsonVal); 7074c30e226SCarson Labrado 7084c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Finished overwriting asyncResp"; 7094c30e226SCarson Labrado } 7104c30e226SCarson Labrado else 7114c30e226SCarson Labrado { 7124c30e226SCarson Labrado // We only want to aggregate collections that contain a 7134c30e226SCarson Labrado // "Members" array 7144c30e226SCarson Labrado if ((!asyncResp->res.jsonValue.contains("Members")) && 7154c30e226SCarson Labrado (!asyncResp->res.jsonValue["Members"].is_array())) 7164c30e226SCarson Labrado 7174c30e226SCarson Labrado { 7184c30e226SCarson Labrado BMCWEB_LOG_DEBUG 7194c30e226SCarson Labrado << "Skipping aggregating unsupported resource"; 7204c30e226SCarson Labrado return; 7214c30e226SCarson Labrado } 7224c30e226SCarson Labrado 7234c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Adding aggregated resources from \"" 7244c30e226SCarson Labrado << prefix << "\" to collection"; 7254c30e226SCarson Labrado 7264c30e226SCarson Labrado // TODO: This is a potential race condition with multiple 7274c30e226SCarson Labrado // satellites and the aggregating bmc attempting to write to 7284c30e226SCarson Labrado // update this array. May need to cascade calls to the next 7294c30e226SCarson Labrado // satellite at the end of this function. 7304c30e226SCarson Labrado // This is presumably not a concern when there is only a single 7314c30e226SCarson Labrado // satellite since the aggregating bmc should have completed 7324c30e226SCarson Labrado // before the response is received from the satellite. 7334c30e226SCarson Labrado 7344c30e226SCarson Labrado auto& members = asyncResp->res.jsonValue["Members"]; 7354c30e226SCarson Labrado auto& satMembers = jsonVal["Members"]; 7364c30e226SCarson Labrado for (auto& satMem : satMembers) 7374c30e226SCarson Labrado { 7384c30e226SCarson Labrado members.push_back(std::move(satMem)); 7394c30e226SCarson Labrado } 7404c30e226SCarson Labrado asyncResp->res.jsonValue["Members@odata.count"] = 7414c30e226SCarson Labrado members.size(); 7424c30e226SCarson Labrado 7434c30e226SCarson Labrado // TODO: Do we need to sort() after updating the array? 7444c30e226SCarson Labrado } 7454c30e226SCarson Labrado } 7464c30e226SCarson Labrado else 7474c30e226SCarson Labrado { 7484c30e226SCarson Labrado BMCWEB_LOG_ERROR << "Received unparsable response from \"" << prefix 7494c30e226SCarson Labrado << "\""; 7504c30e226SCarson Labrado // We received as response that was not a json 7514c30e226SCarson Labrado // Notify the user only if we did not receive any valid responses, 7524c30e226SCarson Labrado // if the resource collection does not already exist on the 7534c30e226SCarson Labrado // aggregating BMC, and if we did not already set this warning due 7544c30e226SCarson Labrado // to a failure from a different satellite 7554c30e226SCarson Labrado if ((asyncResp->res.resultInt() != 200) && 7564c30e226SCarson Labrado (asyncResp->res.resultInt() != 502)) 7574c30e226SCarson Labrado { 7584c30e226SCarson Labrado messages::operationFailed(asyncResp->res); 7594c30e226SCarson Labrado } 7604c30e226SCarson Labrado } 7614c30e226SCarson Labrado } // End processCollectionResponse() 7624c30e226SCarson Labrado 76305916cefSCarson Labrado // Entry point to Redfish Aggregation 76405916cefSCarson Labrado // Returns Result stating whether or not we still need to locally handle the 76505916cefSCarson Labrado // request 76605916cefSCarson Labrado static Result 76705916cefSCarson Labrado beginAggregation(const crow::Request& thisReq, 76805916cefSCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 76905916cefSCarson Labrado { 77005916cefSCarson Labrado using crow::utility::OrMorePaths; 77105916cefSCarson Labrado using crow::utility::readUrlSegments; 77211987af6SCarson Labrado const boost::urls::url_view url = thisReq.urlView; 77305916cefSCarson Labrado // UpdateService is the only top level resource that is not a Collection 77405916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService")) 77505916cefSCarson Labrado { 77605916cefSCarson Labrado return Result::LocalHandle; 77705916cefSCarson Labrado } 778411e6a11SCarson Labrado 779411e6a11SCarson Labrado // We don't need to aggregate JsonSchemas due to potential issues such 780411e6a11SCarson Labrado // as version mismatches between aggregator and satellite BMCs. For 781411e6a11SCarson Labrado // now assume that the aggregator has all the schemas and versions that 782411e6a11SCarson Labrado // the aggregated server has. 783411e6a11SCarson Labrado if (crow::utility::readUrlSegments(url, "redfish", "v1", "JsonSchemas", 784411e6a11SCarson Labrado crow::utility::OrMorePaths())) 785411e6a11SCarson Labrado { 786411e6a11SCarson Labrado return Result::LocalHandle; 787411e6a11SCarson Labrado } 788411e6a11SCarson Labrado 78946a81465SCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 79046a81465SCarson Labrado "SoftwareInventory") || 79146a81465SCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 79246a81465SCarson Labrado "FirmwareInventory")) 79346a81465SCarson Labrado { 79446a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 79546a81465SCarson Labrado return Result::LocalHandle; 79646a81465SCarson Labrado } 79705916cefSCarson Labrado 79805916cefSCarson Labrado // Is the request for a resource collection?: 79905916cefSCarson Labrado // /redfish/v1/<resource> 80005916cefSCarson Labrado // e.g. /redfish/v1/Chassis 80105916cefSCarson Labrado std::string collectionName; 80205916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", std::ref(collectionName))) 80305916cefSCarson Labrado { 80446a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 80505916cefSCarson Labrado return Result::LocalHandle; 80605916cefSCarson Labrado } 80705916cefSCarson Labrado 80805916cefSCarson Labrado // We know that the ID of an aggregated resource will begin with 80905916cefSCarson Labrado // "5B247A". For the most part the URI will begin like this: 81005916cefSCarson Labrado // /redfish/v1/<resource>/<resource ID> 81105916cefSCarson Labrado // Note, FirmwareInventory and SoftwareInventory are "special" because 81205916cefSCarson Labrado // they are two levels deep, but still need aggregated 81305916cefSCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory/<FirmwareInventory ID> 81405916cefSCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory/<SoftwareInventory ID> 81505916cefSCarson Labrado std::string memberName; 81605916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 81705916cefSCarson Labrado "SoftwareInventory", std::ref(memberName), 81805916cefSCarson Labrado OrMorePaths()) || 81905916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 82005916cefSCarson Labrado "FirmwareInventory", std::ref(memberName), 82105916cefSCarson Labrado OrMorePaths()) || 82205916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", std::ref(collectionName), 82305916cefSCarson Labrado std::ref(memberName), OrMorePaths())) 82405916cefSCarson Labrado { 82505916cefSCarson Labrado if (memberName.starts_with("5B247A")) 82605916cefSCarson Labrado { 82705916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Need to forward a request"; 82805916cefSCarson Labrado 82946a81465SCarson Labrado // Extract the prefix from the request's URI, retrieve the 83046a81465SCarson Labrado // associated satellite config information, and then forward the 83146a81465SCarson Labrado // request to that satellite. 83246a81465SCarson Labrado startAggregation(AggregationType::Resource, thisReq, asyncResp); 83305916cefSCarson Labrado return Result::NoLocalHandle; 83405916cefSCarson Labrado } 83505916cefSCarson Labrado return Result::LocalHandle; 83605916cefSCarson Labrado } 83705916cefSCarson Labrado 83805916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation not required"; 83905916cefSCarson Labrado return Result::LocalHandle; 84005916cefSCarson Labrado } 8417fb33566SCarson Labrado }; 8427fb33566SCarson Labrado 8437fb33566SCarson Labrado } // namespace redfish 844