17fb33566SCarson Labrado #pragma once 27fb33566SCarson Labrado 3*411e6a11SCarson Labrado #include <boost/algorithm/string/predicate.hpp> 446a81465SCarson Labrado #include <dbus_utility.hpp> 546a81465SCarson Labrado #include <error_messages.hpp> 67fb33566SCarson Labrado #include <http_client.hpp> 746a81465SCarson Labrado #include <http_connection.hpp> 87fb33566SCarson Labrado 97fb33566SCarson Labrado namespace redfish 107fb33566SCarson Labrado { 117fb33566SCarson Labrado 1205916cefSCarson Labrado enum class Result 1305916cefSCarson Labrado { 1405916cefSCarson Labrado LocalHandle, 1505916cefSCarson Labrado NoLocalHandle 1605916cefSCarson Labrado }; 1705916cefSCarson Labrado 181c0bb5c6SCarson Labrado static void addPrefixToItem(nlohmann::json& item, std::string_view prefix) 191c0bb5c6SCarson Labrado { 201c0bb5c6SCarson Labrado std::string* strValue = item.get_ptr<std::string*>(); 211c0bb5c6SCarson Labrado if (strValue == nullptr) 221c0bb5c6SCarson Labrado { 231c0bb5c6SCarson Labrado BMCWEB_LOG_CRITICAL << "Field wasn't a string????"; 241c0bb5c6SCarson Labrado return; 251c0bb5c6SCarson Labrado } 261c0bb5c6SCarson Labrado // Make sure the value is a properly formatted URI 271c0bb5c6SCarson Labrado auto parsed = boost::urls::parse_relative_ref(*strValue); 281c0bb5c6SCarson Labrado if (!parsed) 291c0bb5c6SCarson Labrado { 301c0bb5c6SCarson Labrado BMCWEB_LOG_CRITICAL << "Couldn't parse URI from resource " << *strValue; 311c0bb5c6SCarson Labrado return; 321c0bb5c6SCarson Labrado } 331c0bb5c6SCarson Labrado 341c0bb5c6SCarson Labrado boost::urls::url_view thisUrl = *parsed; 351c0bb5c6SCarson Labrado 36*411e6a11SCarson Labrado // We don't need to aggregate JsonSchemas due to potential issues such as 37*411e6a11SCarson Labrado // version mismatches between aggregator and satellite BMCs. For now 38*411e6a11SCarson Labrado // assume that the aggregator has all the schemas and versions that the 39*411e6a11SCarson Labrado // aggregated server has. 40*411e6a11SCarson Labrado std::string collectionItem; 41*411e6a11SCarson Labrado if (crow::utility::readUrlSegments(thisUrl, "redfish", "v1", "JsonSchemas", 42*411e6a11SCarson Labrado std::ref(collectionItem), 43*411e6a11SCarson Labrado crow::utility::OrMorePaths())) 44*411e6a11SCarson Labrado { 45*411e6a11SCarson Labrado BMCWEB_LOG_DEBUG << "Skipping JsonSchemas URI prefix fixing"; 46*411e6a11SCarson Labrado return; 47*411e6a11SCarson Labrado } 48*411e6a11SCarson Labrado 491c0bb5c6SCarson Labrado // We don't need to add prefixes to these URIs since 50*411e6a11SCarson Labrado // /redfish/v1/UpdateService/ itself is not a collection: 511c0bb5c6SCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory 521c0bb5c6SCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory 531c0bb5c6SCarson Labrado if (crow::utility::readUrlSegments(thisUrl, "redfish", "v1", 541c0bb5c6SCarson Labrado "UpdateService", "FirmwareInventory") || 551c0bb5c6SCarson Labrado crow::utility::readUrlSegments(thisUrl, "redfish", "v1", 561c0bb5c6SCarson Labrado "UpdateService", "SoftwareInventory")) 571c0bb5c6SCarson Labrado { 581c0bb5c6SCarson Labrado BMCWEB_LOG_DEBUG << "Skipping UpdateService URI prefix fixing"; 591c0bb5c6SCarson Labrado return; 601c0bb5c6SCarson Labrado } 611c0bb5c6SCarson Labrado 62*411e6a11SCarson Labrado // We need to add a prefix to FirmwareInventory and SoftwareInventory 63*411e6a11SCarson Labrado // resources: 641c0bb5c6SCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory/<id> 651c0bb5c6SCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory/<id> 661c0bb5c6SCarson Labrado std::string collectionName; 671c0bb5c6SCarson Labrado if (crow::utility::readUrlSegments( 681c0bb5c6SCarson Labrado thisUrl, "redfish", "v1", "UpdateService", std::ref(collectionName), 69*411e6a11SCarson Labrado std::ref(collectionItem), crow::utility::OrMorePaths())) 701c0bb5c6SCarson Labrado { 71*411e6a11SCarson Labrado collectionItem.insert(0, "_"); 72*411e6a11SCarson Labrado collectionItem.insert(0, prefix); 73*411e6a11SCarson Labrado item = crow::utility::replaceUrlSegment(thisUrl, 4, collectionItem); 74*411e6a11SCarson Labrado return; 751c0bb5c6SCarson Labrado } 761c0bb5c6SCarson Labrado 77*411e6a11SCarson Labrado // If we reach here then we need to add a prefix to resource IDs that take 78*411e6a11SCarson Labrado // the general form of "/redfish/v1/<collection>/<id> such as: 79*411e6a11SCarson Labrado // /redfish/v1/Chassis/foo 801c0bb5c6SCarson Labrado if (crow::utility::readUrlSegments( 811c0bb5c6SCarson Labrado thisUrl, "redfish", "v1", std::ref(collectionName), 82*411e6a11SCarson Labrado std::ref(collectionItem), crow::utility::OrMorePaths())) 831c0bb5c6SCarson Labrado { 84*411e6a11SCarson Labrado collectionItem.insert(0, "_"); 85*411e6a11SCarson Labrado collectionItem.insert(0, prefix); 86*411e6a11SCarson Labrado item = crow::utility::replaceUrlSegment(thisUrl, 3, collectionItem); 871c0bb5c6SCarson Labrado } 881c0bb5c6SCarson Labrado } 891c0bb5c6SCarson Labrado 901c0bb5c6SCarson Labrado // We need to attempt to update all URIs under Actions 911c0bb5c6SCarson Labrado static void addPrefixesToActions(nlohmann::json& json, std::string_view prefix) 921c0bb5c6SCarson Labrado { 931c0bb5c6SCarson Labrado nlohmann::json::object_t* object = 941c0bb5c6SCarson Labrado json.get_ptr<nlohmann::json::object_t*>(); 951c0bb5c6SCarson Labrado if (object != nullptr) 961c0bb5c6SCarson Labrado { 971c0bb5c6SCarson Labrado for (std::pair<const std::string, nlohmann::json>& item : *object) 981c0bb5c6SCarson Labrado { 991c0bb5c6SCarson Labrado std::string* strValue = item.second.get_ptr<std::string*>(); 1001c0bb5c6SCarson Labrado if (strValue != nullptr) 1011c0bb5c6SCarson Labrado { 1021c0bb5c6SCarson Labrado addPrefixToItem(item.second, prefix); 1031c0bb5c6SCarson Labrado } 1041c0bb5c6SCarson Labrado else 1051c0bb5c6SCarson Labrado { 1061c0bb5c6SCarson Labrado addPrefixesToActions(item.second, prefix); 1071c0bb5c6SCarson Labrado } 1081c0bb5c6SCarson Labrado } 1091c0bb5c6SCarson Labrado } 1101c0bb5c6SCarson Labrado } 1111c0bb5c6SCarson Labrado 1121c0bb5c6SCarson Labrado // Search the json for all URIs and add the supplied prefix if the URI is for 1131c0bb5c6SCarson Labrado // and aggregated resource. 1141c0bb5c6SCarson Labrado static void addPrefixes(nlohmann::json& json, std::string_view prefix) 1151c0bb5c6SCarson Labrado { 1161c0bb5c6SCarson Labrado nlohmann::json::object_t* object = 1171c0bb5c6SCarson Labrado json.get_ptr<nlohmann::json::object_t*>(); 1181c0bb5c6SCarson Labrado if (object != nullptr) 1191c0bb5c6SCarson Labrado { 1201c0bb5c6SCarson Labrado for (std::pair<const std::string, nlohmann::json>& item : *object) 1211c0bb5c6SCarson Labrado { 1221c0bb5c6SCarson Labrado if (item.first == "Actions") 1231c0bb5c6SCarson Labrado { 1241c0bb5c6SCarson Labrado addPrefixesToActions(item.second, prefix); 1251c0bb5c6SCarson Labrado continue; 1261c0bb5c6SCarson Labrado } 1271c0bb5c6SCarson Labrado 128*411e6a11SCarson Labrado // TODO: Update with the uri naming language from the specification 129*411e6a11SCarson Labrado // when it gets released in 1.17 130*411e6a11SCarson Labrado if ((item.first == "@odata.id") || 131*411e6a11SCarson Labrado boost::algorithm::iends_with(item.first, "uri")) 1321c0bb5c6SCarson Labrado { 1331c0bb5c6SCarson Labrado addPrefixToItem(item.second, prefix); 1341c0bb5c6SCarson Labrado } 1351c0bb5c6SCarson Labrado // Recusively parse the rest of the json 1361c0bb5c6SCarson Labrado addPrefixes(item.second, prefix); 1371c0bb5c6SCarson Labrado } 1381c0bb5c6SCarson Labrado return; 1391c0bb5c6SCarson Labrado } 1401c0bb5c6SCarson Labrado nlohmann::json::array_t* array = json.get_ptr<nlohmann::json::array_t*>(); 1411c0bb5c6SCarson Labrado if (array != nullptr) 1421c0bb5c6SCarson Labrado { 1431c0bb5c6SCarson Labrado for (nlohmann::json& item : *array) 1441c0bb5c6SCarson Labrado { 1451c0bb5c6SCarson Labrado addPrefixes(item, prefix); 1461c0bb5c6SCarson Labrado } 1471c0bb5c6SCarson Labrado } 1481c0bb5c6SCarson Labrado } 1491c0bb5c6SCarson Labrado 1507fb33566SCarson Labrado class RedfishAggregator 1517fb33566SCarson Labrado { 1527fb33566SCarson Labrado private: 153a7a80296SCarson Labrado const std::string retryPolicyName = "RedfishAggregation"; 154ce969437SCarson Labrado const std::string retryPolicyAction = "TerminateAfterRetries"; 155ce969437SCarson Labrado const uint32_t retryAttempts = 1; 156a7a80296SCarson Labrado const uint32_t retryTimeoutInterval = 0; 15746a81465SCarson Labrado const std::string id = "Aggregator"; 158a7a80296SCarson Labrado 1597fb33566SCarson Labrado RedfishAggregator() 1607fb33566SCarson Labrado { 1617fb33566SCarson Labrado getSatelliteConfigs(constructorCallback); 162a7a80296SCarson Labrado 163a7a80296SCarson Labrado // Setup the retry policy to be used by Redfish Aggregation 164a7a80296SCarson Labrado crow::HttpClient::getInstance().setRetryConfig( 165a7a80296SCarson Labrado retryAttempts, retryTimeoutInterval, aggregationRetryHandler, 166a7a80296SCarson Labrado retryPolicyName); 167ce969437SCarson Labrado crow::HttpClient::getInstance().setRetryPolicy(retryPolicyAction, 168ce969437SCarson Labrado retryPolicyName); 1697fb33566SCarson Labrado } 1707fb33566SCarson Labrado 171a7a80296SCarson Labrado static inline boost::system::error_code 172a7a80296SCarson Labrado aggregationRetryHandler(unsigned int respCode) 173a7a80296SCarson Labrado { 174a7a80296SCarson Labrado // As a default, assume 200X is alright. 175a7a80296SCarson Labrado // We don't need to retry on a 404 176a7a80296SCarson Labrado if ((respCode < 200) || ((respCode >= 300) && (respCode != 404))) 177a7a80296SCarson Labrado { 178a7a80296SCarson Labrado return boost::system::errc::make_error_code( 179a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 180a7a80296SCarson Labrado } 181a7a80296SCarson Labrado 182a7a80296SCarson Labrado // Return 0 if the response code is valid 183a7a80296SCarson Labrado return boost::system::errc::make_error_code( 184a7a80296SCarson Labrado boost::system::errc::success); 1859fa6d147SNan Zhou } 186a7a80296SCarson Labrado 1877fb33566SCarson Labrado // Dummy callback used by the Constructor so that it can report the number 1887fb33566SCarson Labrado // of satellite configs when the class is first created 1897fb33566SCarson Labrado static void constructorCallback( 1907fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1917fb33566SCarson Labrado { 1927fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "There were " 1937fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 1947fb33566SCarson Labrado << " satellite configs found at startup"; 1957fb33566SCarson Labrado } 1967fb33566SCarson Labrado 1977fb33566SCarson Labrado // Polls D-Bus to get all available satellite config information 1987fb33566SCarson Labrado // Expects a handler which interacts with the returned configs 1997fb33566SCarson Labrado static void getSatelliteConfigs( 2007fb33566SCarson Labrado const std::function<void( 2017fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>&)>& handler) 2027fb33566SCarson Labrado { 2037fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Gathering satellite configs"; 2047fb33566SCarson Labrado crow::connections::systemBus->async_method_call( 2057fb33566SCarson Labrado [handler](const boost::system::error_code ec, 2067fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects) { 2077fb33566SCarson Labrado if (ec) 2087fb33566SCarson Labrado { 209002d39b4SEd Tanous BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", " 210002d39b4SEd Tanous << ec.message(); 2117fb33566SCarson Labrado return; 2127fb33566SCarson Labrado } 2137fb33566SCarson Labrado 2147fb33566SCarson Labrado // Maps a chosen alias representing a satellite BMC to a url 2157fb33566SCarson Labrado // containing the information required to create a http 2167fb33566SCarson Labrado // connection to the satellite 2177fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url> satelliteInfo; 2187fb33566SCarson Labrado 2197fb33566SCarson Labrado findSatelliteConfigs(objects, satelliteInfo); 2207fb33566SCarson Labrado 2217fb33566SCarson Labrado if (!satelliteInfo.empty()) 2227fb33566SCarson Labrado { 2237fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with " 2247fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 2257fb33566SCarson Labrado << " satellite BMCs"; 2267fb33566SCarson Labrado } 2277fb33566SCarson Labrado else 2287fb33566SCarson Labrado { 2297fb33566SCarson Labrado BMCWEB_LOG_DEBUG 2307fb33566SCarson Labrado << "No satellite BMCs detected. Redfish Aggregation not enabled"; 2317fb33566SCarson Labrado } 2327fb33566SCarson Labrado handler(satelliteInfo); 2337fb33566SCarson Labrado }, 2347fb33566SCarson Labrado "xyz.openbmc_project.EntityManager", "/", 2357fb33566SCarson Labrado "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 2367fb33566SCarson Labrado } 2377fb33566SCarson Labrado 2387fb33566SCarson Labrado // Search D-Bus objects for satellite config objects and add their 2397fb33566SCarson Labrado // information if valid 2407fb33566SCarson Labrado static void findSatelliteConfigs( 2417fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects, 2427fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 2437fb33566SCarson Labrado { 2447fb33566SCarson Labrado for (const auto& objectPath : objects) 2457fb33566SCarson Labrado { 2467fb33566SCarson Labrado for (const auto& interface : objectPath.second) 2477fb33566SCarson Labrado { 2487fb33566SCarson Labrado if (interface.first == 2497fb33566SCarson Labrado "xyz.openbmc_project.Configuration.SatelliteController") 2507fb33566SCarson Labrado { 2517fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Found Satellite Controller at " 2527fb33566SCarson Labrado << objectPath.first.str; 2537fb33566SCarson Labrado 25405916cefSCarson Labrado if (!satelliteInfo.empty()) 25505916cefSCarson Labrado { 25605916cefSCarson Labrado BMCWEB_LOG_ERROR 25705916cefSCarson Labrado << "Redfish Aggregation only supports one satellite!"; 25805916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Clearing all satellite data"; 25905916cefSCarson Labrado satelliteInfo.clear(); 26005916cefSCarson Labrado return; 26105916cefSCarson Labrado } 26205916cefSCarson Labrado 26305916cefSCarson Labrado // For now assume there will only be one satellite config. 26405916cefSCarson Labrado // Assign it the name/prefix "5B247A" 26505916cefSCarson Labrado addSatelliteConfig("5B247A", interface.second, 26605916cefSCarson Labrado satelliteInfo); 2677fb33566SCarson Labrado } 2687fb33566SCarson Labrado } 2697fb33566SCarson Labrado } 2707fb33566SCarson Labrado } 2717fb33566SCarson Labrado 2727fb33566SCarson Labrado // Parse the properties of a satellite config object and add the 2737fb33566SCarson Labrado // configuration if the properties are valid 2747fb33566SCarson Labrado static void addSatelliteConfig( 27505916cefSCarson Labrado const std::string& name, 2767fb33566SCarson Labrado const dbus::utility::DBusPropertiesMap& properties, 2777fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 2787fb33566SCarson Labrado { 2797fb33566SCarson Labrado boost::urls::url url; 2807fb33566SCarson Labrado 2817fb33566SCarson Labrado for (const auto& prop : properties) 2827fb33566SCarson Labrado { 28305916cefSCarson Labrado if (prop.first == "Hostname") 2847fb33566SCarson Labrado { 2857fb33566SCarson Labrado const std::string* propVal = 2867fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 2877fb33566SCarson Labrado if (propVal == nullptr) 2887fb33566SCarson Labrado { 2897fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Hostname value"; 2907fb33566SCarson Labrado return; 2917fb33566SCarson Labrado } 2927fb33566SCarson Labrado url.set_host(*propVal); 2937fb33566SCarson Labrado } 2947fb33566SCarson Labrado 2957fb33566SCarson Labrado else if (prop.first == "Port") 2967fb33566SCarson Labrado { 2977fb33566SCarson Labrado const uint64_t* propVal = std::get_if<uint64_t>(&prop.second); 2987fb33566SCarson Labrado if (propVal == nullptr) 2997fb33566SCarson Labrado { 3007fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Port value"; 3017fb33566SCarson Labrado return; 3027fb33566SCarson Labrado } 3037fb33566SCarson Labrado 3047fb33566SCarson Labrado if (*propVal > std::numeric_limits<uint16_t>::max()) 3057fb33566SCarson Labrado { 3067fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Port value out of range"; 3077fb33566SCarson Labrado return; 3087fb33566SCarson Labrado } 3097fb33566SCarson Labrado url.set_port(static_cast<uint16_t>(*propVal)); 3107fb33566SCarson Labrado } 3117fb33566SCarson Labrado 3127fb33566SCarson Labrado else if (prop.first == "AuthType") 3137fb33566SCarson Labrado { 3147fb33566SCarson Labrado const std::string* propVal = 3157fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 3167fb33566SCarson Labrado if (propVal == nullptr) 3177fb33566SCarson Labrado { 3187fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid AuthType value"; 3197fb33566SCarson Labrado return; 3207fb33566SCarson Labrado } 3217fb33566SCarson Labrado 3227fb33566SCarson Labrado // For now assume authentication not required to communicate 3237fb33566SCarson Labrado // with the satellite BMC 3247fb33566SCarson Labrado if (*propVal != "None") 3257fb33566SCarson Labrado { 3267fb33566SCarson Labrado BMCWEB_LOG_ERROR 3277fb33566SCarson Labrado << "Unsupported AuthType value: " << *propVal 3287fb33566SCarson Labrado << ", only \"none\" is supported"; 3297fb33566SCarson Labrado return; 3307fb33566SCarson Labrado } 3317fb33566SCarson Labrado url.set_scheme("http"); 3327fb33566SCarson Labrado } 3337fb33566SCarson Labrado } // Finished reading properties 3347fb33566SCarson Labrado 3357fb33566SCarson Labrado // Make sure all required config information was made available 3367fb33566SCarson Labrado if (url.host().empty()) 3377fb33566SCarson Labrado { 3387fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host"; 3397fb33566SCarson Labrado return; 3407fb33566SCarson Labrado } 3417fb33566SCarson Labrado 3427fb33566SCarson Labrado if (!url.has_port()) 3437fb33566SCarson Labrado { 3447fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port"; 3457fb33566SCarson Labrado return; 3467fb33566SCarson Labrado } 3477fb33566SCarson Labrado 3487fb33566SCarson Labrado if (!url.has_scheme()) 3497fb33566SCarson Labrado { 3507fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name 3517fb33566SCarson Labrado << " missing AuthType"; 3527fb33566SCarson Labrado return; 3537fb33566SCarson Labrado } 3547fb33566SCarson Labrado 3557fb33566SCarson Labrado std::string resultString; 3567fb33566SCarson Labrado auto result = satelliteInfo.insert_or_assign(name, std::move(url)); 3577fb33566SCarson Labrado if (result.second) 3587fb33566SCarson Labrado { 3597fb33566SCarson Labrado resultString = "Added new satellite config "; 3607fb33566SCarson Labrado } 3617fb33566SCarson Labrado else 3627fb33566SCarson Labrado { 3637fb33566SCarson Labrado resultString = "Updated existing satellite config "; 3647fb33566SCarson Labrado } 3657fb33566SCarson Labrado 3667fb33566SCarson Labrado BMCWEB_LOG_DEBUG << resultString << name << " at " 3677fb33566SCarson Labrado << result.first->second.scheme() << "://" 3687fb33566SCarson Labrado << result.first->second.encoded_host_and_port(); 3697fb33566SCarson Labrado } 3707fb33566SCarson Labrado 37146a81465SCarson Labrado enum AggregationType 37246a81465SCarson Labrado { 37346a81465SCarson Labrado Collection, 37446a81465SCarson Labrado Resource, 37546a81465SCarson Labrado }; 37646a81465SCarson Labrado 37746a81465SCarson Labrado static void 37846a81465SCarson Labrado startAggregation(AggregationType isCollection, 37946a81465SCarson Labrado const crow::Request& thisReq, 38046a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 38146a81465SCarson Labrado { 382db18fc98SCarson Labrado if ((isCollection == AggregationType::Collection) && 383db18fc98SCarson Labrado (thisReq.method() != boost::beast::http::verb::get)) 384db18fc98SCarson Labrado { 385db18fc98SCarson Labrado BMCWEB_LOG_DEBUG 386db18fc98SCarson Labrado << "Only aggregate GET requests to top level collections"; 387db18fc98SCarson Labrado return; 388db18fc98SCarson Labrado } 389db18fc98SCarson Labrado 39046a81465SCarson Labrado // Create a copy of thisReq so we we can still locally process the req 39146a81465SCarson Labrado std::error_code ec; 39246a81465SCarson Labrado auto localReq = std::make_shared<crow::Request>(thisReq.req, ec); 39346a81465SCarson Labrado if (ec) 39446a81465SCarson Labrado { 39546a81465SCarson Labrado BMCWEB_LOG_ERROR << "Failed to create copy of request"; 39646a81465SCarson Labrado if (isCollection != AggregationType::Collection) 39746a81465SCarson Labrado { 39846a81465SCarson Labrado messages::internalError(asyncResp->res); 39946a81465SCarson Labrado } 40046a81465SCarson Labrado return; 40146a81465SCarson Labrado } 40246a81465SCarson Labrado 40346a81465SCarson Labrado getSatelliteConfigs(std::bind_front(aggregateAndHandle, isCollection, 40446a81465SCarson Labrado localReq, asyncResp)); 40546a81465SCarson Labrado } 40646a81465SCarson Labrado 407db18fc98SCarson Labrado static void findSatellite( 40846a81465SCarson Labrado const crow::Request& req, 40946a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 41046a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo, 41146a81465SCarson Labrado std::string_view memberName) 41246a81465SCarson Labrado { 41346a81465SCarson Labrado // Determine if the resource ID begins with a known prefix 41446a81465SCarson Labrado for (const auto& satellite : satelliteInfo) 41546a81465SCarson Labrado { 41646a81465SCarson Labrado std::string targetPrefix = satellite.first; 41746a81465SCarson Labrado targetPrefix += "_"; 41846a81465SCarson Labrado if (memberName.starts_with(targetPrefix)) 41946a81465SCarson Labrado { 42046a81465SCarson Labrado BMCWEB_LOG_DEBUG << "\"" << satellite.first 42146a81465SCarson Labrado << "\" is a known prefix"; 42246a81465SCarson Labrado 42346a81465SCarson Labrado // Remove the known prefix from the request's URI and 42446a81465SCarson Labrado // then forward to the associated satellite BMC 42546a81465SCarson Labrado getInstance().forwardRequest(req, asyncResp, satellite.first, 42646a81465SCarson Labrado satelliteInfo); 42746a81465SCarson Labrado return; 42846a81465SCarson Labrado } 42946a81465SCarson Labrado } 430db18fc98SCarson Labrado 431db18fc98SCarson Labrado // We didn't recognize the prefix and need to return a 404 432db18fc98SCarson Labrado boost::urls::string_value name = req.urlView.segments().back(); 433db18fc98SCarson Labrado std::string_view nameStr(name.data(), name.size()); 434db18fc98SCarson Labrado messages::resourceNotFound(asyncResp->res, "", nameStr); 43546a81465SCarson Labrado } 43646a81465SCarson Labrado 43746a81465SCarson Labrado // Intended to handle an incoming request based on if Redfish Aggregation 43846a81465SCarson Labrado // is enabled. Forwards request to satellite BMC if it exists. 43946a81465SCarson Labrado static void aggregateAndHandle( 44046a81465SCarson Labrado AggregationType isCollection, 44146a81465SCarson Labrado const std::shared_ptr<crow::Request>& sharedReq, 44246a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 44346a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 44446a81465SCarson Labrado { 44546a81465SCarson Labrado if (sharedReq == nullptr) 44646a81465SCarson Labrado { 44746a81465SCarson Labrado return; 44846a81465SCarson Labrado } 449db18fc98SCarson Labrado 450db18fc98SCarson Labrado // No satellite configs means we don't need to keep attempting to 451db18fc98SCarson Labrado // aggregate 452db18fc98SCarson Labrado if (satelliteInfo.empty()) 453db18fc98SCarson Labrado { 454db18fc98SCarson Labrado // For collections we'll also handle the request locally so we 455db18fc98SCarson Labrado // don't need to write an error code 456db18fc98SCarson Labrado if (isCollection == AggregationType::Resource) 457db18fc98SCarson Labrado { 458db18fc98SCarson Labrado boost::urls::string_value name = 459db18fc98SCarson Labrado sharedReq->urlView.segments().back(); 460db18fc98SCarson Labrado std::string_view nameStr(name.data(), name.size()); 461db18fc98SCarson Labrado messages::resourceNotFound(asyncResp->res, "", nameStr); 462db18fc98SCarson Labrado } 463db18fc98SCarson Labrado return; 464db18fc98SCarson Labrado } 465db18fc98SCarson Labrado 46646a81465SCarson Labrado const crow::Request& thisReq = *sharedReq; 46746a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation is enabled, begin processing of " 46846a81465SCarson Labrado << thisReq.target(); 46946a81465SCarson Labrado 47046a81465SCarson Labrado // We previously determined the request is for a collection. No need to 47146a81465SCarson Labrado // check again 47246a81465SCarson Labrado if (isCollection == AggregationType::Collection) 47346a81465SCarson Labrado { 47446a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregating a collection"; 4754c30e226SCarson Labrado // We need to use a specific response handler and send the 4764c30e226SCarson Labrado // request to all known satellites 4774c30e226SCarson Labrado getInstance().forwardCollectionRequests(thisReq, asyncResp, 4784c30e226SCarson Labrado satelliteInfo); 47946a81465SCarson Labrado return; 48046a81465SCarson Labrado } 48146a81465SCarson Labrado 48246a81465SCarson Labrado std::string updateServiceName; 48346a81465SCarson Labrado std::string memberName; 48446a81465SCarson Labrado if (crow::utility::readUrlSegments( 48546a81465SCarson Labrado thisReq.urlView, "redfish", "v1", "UpdateService", 48646a81465SCarson Labrado std::ref(updateServiceName), std::ref(memberName), 48746a81465SCarson Labrado crow::utility::OrMorePaths())) 48846a81465SCarson Labrado { 48946a81465SCarson Labrado // Must be FirmwareInventory or SoftwareInventory 490db18fc98SCarson Labrado findSatellite(thisReq, asyncResp, satelliteInfo, memberName); 49146a81465SCarson Labrado return; 49246a81465SCarson Labrado } 49346a81465SCarson Labrado 49446a81465SCarson Labrado std::string collectionName; 49546a81465SCarson Labrado if (crow::utility::readUrlSegments( 49646a81465SCarson Labrado thisReq.urlView, "redfish", "v1", std::ref(collectionName), 49746a81465SCarson Labrado std::ref(memberName), crow::utility::OrMorePaths())) 49846a81465SCarson Labrado { 499db18fc98SCarson Labrado findSatellite(thisReq, asyncResp, satelliteInfo, memberName); 500db18fc98SCarson Labrado return; 50146a81465SCarson Labrado } 502db18fc98SCarson Labrado 503db18fc98SCarson Labrado // We shouldn't reach this point since we should've hit one of the 504db18fc98SCarson Labrado // previous exits 505db18fc98SCarson Labrado messages::internalError(asyncResp->res); 50646a81465SCarson Labrado } 50746a81465SCarson Labrado 50846a81465SCarson Labrado // Attempt to forward a request to the satellite BMC associated with the 50946a81465SCarson Labrado // prefix. 51046a81465SCarson Labrado void forwardRequest( 51146a81465SCarson Labrado const crow::Request& thisReq, 51246a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 51346a81465SCarson Labrado const std::string& prefix, 51446a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 51546a81465SCarson Labrado { 51646a81465SCarson Labrado const auto& sat = satelliteInfo.find(prefix); 51746a81465SCarson Labrado if (sat == satelliteInfo.end()) 51846a81465SCarson Labrado { 51946a81465SCarson Labrado // Realistically this shouldn't get called since we perform an 52046a81465SCarson Labrado // earlier check to make sure the prefix exists 52146a81465SCarson Labrado BMCWEB_LOG_ERROR << "Unrecognized satellite prefix \"" << prefix 52246a81465SCarson Labrado << "\""; 52346a81465SCarson Labrado return; 52446a81465SCarson Labrado } 52546a81465SCarson Labrado 52646a81465SCarson Labrado // We need to strip the prefix from the request's path 52746a81465SCarson Labrado std::string targetURI(thisReq.target()); 52846a81465SCarson Labrado size_t pos = targetURI.find(prefix + "_"); 52946a81465SCarson Labrado if (pos == std::string::npos) 53046a81465SCarson Labrado { 53146a81465SCarson Labrado // If this fails then something went wrong 53246a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error removing prefix \"" << prefix 53346a81465SCarson Labrado << "_\" from request URI"; 53446a81465SCarson Labrado messages::internalError(asyncResp->res); 53546a81465SCarson Labrado return; 53646a81465SCarson Labrado } 53746a81465SCarson Labrado targetURI.erase(pos, prefix.size() + 1); 53846a81465SCarson Labrado 53946a81465SCarson Labrado std::function<void(crow::Response&)> cb = 5401c0bb5c6SCarson Labrado std::bind_front(processResponse, prefix, asyncResp); 54146a81465SCarson Labrado 54246a81465SCarson Labrado std::string data = thisReq.req.body(); 54346a81465SCarson Labrado crow::HttpClient::getInstance().sendDataWithCallback( 54446a81465SCarson Labrado data, id, std::string(sat->second.host()), 545e38778a5SAppaRao Puli sat->second.port_number(), targetURI, false /*useSSL*/, 546e38778a5SAppaRao Puli thisReq.fields, thisReq.method(), retryPolicyName, cb); 54746a81465SCarson Labrado } 54846a81465SCarson Labrado 5494c30e226SCarson Labrado // Forward a request for a collection URI to each known satellite BMC 5504c30e226SCarson Labrado void forwardCollectionRequests( 5514c30e226SCarson Labrado const crow::Request& thisReq, 5524c30e226SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 5534c30e226SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 5544c30e226SCarson Labrado { 5554c30e226SCarson Labrado for (const auto& sat : satelliteInfo) 5564c30e226SCarson Labrado { 5574c30e226SCarson Labrado std::function<void(crow::Response&)> cb = std::bind_front( 5584c30e226SCarson Labrado processCollectionResponse, sat.first, asyncResp); 5594c30e226SCarson Labrado 5604c30e226SCarson Labrado std::string targetURI(thisReq.target()); 5614c30e226SCarson Labrado std::string data = thisReq.req.body(); 5624c30e226SCarson Labrado crow::HttpClient::getInstance().sendDataWithCallback( 5634c30e226SCarson Labrado data, id, std::string(sat.second.host()), 564e38778a5SAppaRao Puli sat.second.port_number(), targetURI, false /*useSSL*/, 565e38778a5SAppaRao Puli thisReq.fields, thisReq.method(), retryPolicyName, cb); 5664c30e226SCarson Labrado } 5674c30e226SCarson Labrado } 5684c30e226SCarson Labrado 56946a81465SCarson Labrado // Processes the response returned by a satellite BMC and loads its 57046a81465SCarson Labrado // contents into asyncResp 57146a81465SCarson Labrado static void 5721c0bb5c6SCarson Labrado processResponse(std::string_view prefix, 5731c0bb5c6SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 57446a81465SCarson Labrado crow::Response& resp) 57546a81465SCarson Labrado { 57646a81465SCarson Labrado // No processing needed if the request wasn't successful 57746a81465SCarson Labrado if (resp.resultInt() != 200) 57846a81465SCarson Labrado { 57946a81465SCarson Labrado BMCWEB_LOG_DEBUG << "No need to parse satellite response"; 58046a81465SCarson Labrado asyncResp->res.stringResponse = std::move(resp.stringResponse); 58146a81465SCarson Labrado return; 58246a81465SCarson Labrado } 58346a81465SCarson Labrado 58446a81465SCarson Labrado // The resp will not have a json component 58546a81465SCarson Labrado // We need to create a json from resp's stringResponse 58646a81465SCarson Labrado if (resp.getHeaderValue("Content-Type") == "application/json") 58746a81465SCarson Labrado { 58846a81465SCarson Labrado nlohmann::json jsonVal = 58946a81465SCarson Labrado nlohmann::json::parse(resp.body(), nullptr, false); 59046a81465SCarson Labrado if (jsonVal.is_discarded()) 59146a81465SCarson Labrado { 59246a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON"; 59346a81465SCarson Labrado messages::operationFailed(asyncResp->res); 59446a81465SCarson Labrado return; 59546a81465SCarson Labrado } 59646a81465SCarson Labrado 59746a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Successfully parsed satellite response"; 59846a81465SCarson Labrado 59946a81465SCarson Labrado // TODO: For collections we want to add the satellite responses to 60046a81465SCarson Labrado // our response rather than just straight overwriting them if our 60146a81465SCarson Labrado // local handling was successful (i.e. would return a 200). 6021c0bb5c6SCarson Labrado addPrefixes(jsonVal, prefix); 6031c0bb5c6SCarson Labrado 6041c0bb5c6SCarson Labrado BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response"; 6051c0bb5c6SCarson Labrado 60646a81465SCarson Labrado asyncResp->res.stringResponse.emplace( 60746a81465SCarson Labrado boost::beast::http::response< 60846a81465SCarson Labrado boost::beast::http::string_body>{}); 60946a81465SCarson Labrado asyncResp->res.result(resp.result()); 61046a81465SCarson Labrado asyncResp->res.jsonValue = std::move(jsonVal); 61146a81465SCarson Labrado 61246a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Finished writing asyncResp"; 61346a81465SCarson Labrado } 61446a81465SCarson Labrado else 61546a81465SCarson Labrado { 61646a81465SCarson Labrado if (!resp.body().empty()) 61746a81465SCarson Labrado { 61846a81465SCarson Labrado // We received a 200 response without the correct Content-Type 61946a81465SCarson Labrado // so return an Operation Failed error 62046a81465SCarson Labrado BMCWEB_LOG_ERROR 62146a81465SCarson Labrado << "Satellite response must be of type \"application/json\""; 62246a81465SCarson Labrado messages::operationFailed(asyncResp->res); 62346a81465SCarson Labrado } 62446a81465SCarson Labrado } 62546a81465SCarson Labrado } 62646a81465SCarson Labrado 6274c30e226SCarson Labrado // Processes the collection response returned by a satellite BMC and merges 6284c30e226SCarson Labrado // its "@odata.id" values 6294c30e226SCarson Labrado static void processCollectionResponse( 6304c30e226SCarson Labrado const std::string& prefix, 6314c30e226SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 6324c30e226SCarson Labrado crow::Response& resp) 6334c30e226SCarson Labrado { 6344c30e226SCarson Labrado if (resp.resultInt() != 200) 6354c30e226SCarson Labrado { 6364c30e226SCarson Labrado BMCWEB_LOG_DEBUG 6374c30e226SCarson Labrado << "Collection resource does not exist in satellite BMC \"" 6384c30e226SCarson Labrado << prefix << "\""; 6394c30e226SCarson Labrado // Return the error if we haven't had any successes 6404c30e226SCarson Labrado if (asyncResp->res.resultInt() != 200) 6414c30e226SCarson Labrado { 6424c30e226SCarson Labrado asyncResp->res.stringResponse = std::move(resp.stringResponse); 6434c30e226SCarson Labrado } 6444c30e226SCarson Labrado return; 6454c30e226SCarson Labrado } 6464c30e226SCarson Labrado 6474c30e226SCarson Labrado // The resp will not have a json component 6484c30e226SCarson Labrado // We need to create a json from resp's stringResponse 6494c30e226SCarson Labrado if (resp.getHeaderValue("Content-Type") == "application/json") 6504c30e226SCarson Labrado { 6514c30e226SCarson Labrado nlohmann::json jsonVal = 6524c30e226SCarson Labrado nlohmann::json::parse(resp.body(), nullptr, false); 6534c30e226SCarson Labrado if (jsonVal.is_discarded()) 6544c30e226SCarson Labrado { 6554c30e226SCarson Labrado BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON"; 6564c30e226SCarson Labrado 6574c30e226SCarson Labrado // Notify the user if doing so won't overwrite a valid response 6584c30e226SCarson Labrado if ((asyncResp->res.resultInt() != 200) && 6594c30e226SCarson Labrado (asyncResp->res.resultInt() != 502)) 6604c30e226SCarson Labrado { 6614c30e226SCarson Labrado messages::operationFailed(asyncResp->res); 6624c30e226SCarson Labrado } 6634c30e226SCarson Labrado return; 6644c30e226SCarson Labrado } 6654c30e226SCarson Labrado 6664c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Successfully parsed satellite response"; 6674c30e226SCarson Labrado 6684c30e226SCarson Labrado // Now we need to add the prefix to the URIs contained in the 6694c30e226SCarson Labrado // response. 6704c30e226SCarson Labrado addPrefixes(jsonVal, prefix); 6714c30e226SCarson Labrado 6724c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Added prefix to parsed satellite response"; 6734c30e226SCarson Labrado 6744c30e226SCarson Labrado // If this resource collection does not exist on the aggregating bmc 6754c30e226SCarson Labrado // and has not already been added from processing the response from 6764c30e226SCarson Labrado // a different satellite then we need to completely overwrite 6774c30e226SCarson Labrado // asyncResp 6784c30e226SCarson Labrado if (asyncResp->res.resultInt() != 200) 6794c30e226SCarson Labrado { 6804c30e226SCarson Labrado // We only want to aggregate collections that contain a 6814c30e226SCarson Labrado // "Members" array 6824c30e226SCarson Labrado if ((!jsonVal.contains("Members")) && 6834c30e226SCarson Labrado (!jsonVal["Members"].is_array())) 6844c30e226SCarson Labrado { 6854c30e226SCarson Labrado BMCWEB_LOG_DEBUG 6864c30e226SCarson Labrado << "Skipping aggregating unsupported resource"; 6874c30e226SCarson Labrado return; 6884c30e226SCarson Labrado } 6894c30e226SCarson Labrado 6904c30e226SCarson Labrado BMCWEB_LOG_DEBUG 6914c30e226SCarson Labrado << "Collection does not exist, overwriting asyncResp"; 6924c30e226SCarson Labrado asyncResp->res.stringResponse.emplace( 6934c30e226SCarson Labrado boost::beast::http::response< 6944c30e226SCarson Labrado boost::beast::http::string_body>{}); 6954c30e226SCarson Labrado asyncResp->res.result(resp.result()); 6964c30e226SCarson Labrado asyncResp->res.jsonValue = std::move(jsonVal); 6974c30e226SCarson Labrado 6984c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Finished overwriting asyncResp"; 6994c30e226SCarson Labrado } 7004c30e226SCarson Labrado else 7014c30e226SCarson Labrado { 7024c30e226SCarson Labrado // We only want to aggregate collections that contain a 7034c30e226SCarson Labrado // "Members" array 7044c30e226SCarson Labrado if ((!asyncResp->res.jsonValue.contains("Members")) && 7054c30e226SCarson Labrado (!asyncResp->res.jsonValue["Members"].is_array())) 7064c30e226SCarson Labrado 7074c30e226SCarson Labrado { 7084c30e226SCarson Labrado BMCWEB_LOG_DEBUG 7094c30e226SCarson Labrado << "Skipping aggregating unsupported resource"; 7104c30e226SCarson Labrado return; 7114c30e226SCarson Labrado } 7124c30e226SCarson Labrado 7134c30e226SCarson Labrado BMCWEB_LOG_DEBUG << "Adding aggregated resources from \"" 7144c30e226SCarson Labrado << prefix << "\" to collection"; 7154c30e226SCarson Labrado 7164c30e226SCarson Labrado // TODO: This is a potential race condition with multiple 7174c30e226SCarson Labrado // satellites and the aggregating bmc attempting to write to 7184c30e226SCarson Labrado // update this array. May need to cascade calls to the next 7194c30e226SCarson Labrado // satellite at the end of this function. 7204c30e226SCarson Labrado // This is presumably not a concern when there is only a single 7214c30e226SCarson Labrado // satellite since the aggregating bmc should have completed 7224c30e226SCarson Labrado // before the response is received from the satellite. 7234c30e226SCarson Labrado 7244c30e226SCarson Labrado auto& members = asyncResp->res.jsonValue["Members"]; 7254c30e226SCarson Labrado auto& satMembers = jsonVal["Members"]; 7264c30e226SCarson Labrado for (auto& satMem : satMembers) 7274c30e226SCarson Labrado { 7284c30e226SCarson Labrado members.push_back(std::move(satMem)); 7294c30e226SCarson Labrado } 7304c30e226SCarson Labrado asyncResp->res.jsonValue["Members@odata.count"] = 7314c30e226SCarson Labrado members.size(); 7324c30e226SCarson Labrado 7334c30e226SCarson Labrado // TODO: Do we need to sort() after updating the array? 7344c30e226SCarson Labrado } 7354c30e226SCarson Labrado } 7364c30e226SCarson Labrado else 7374c30e226SCarson Labrado { 7384c30e226SCarson Labrado BMCWEB_LOG_ERROR << "Received unparsable response from \"" << prefix 7394c30e226SCarson Labrado << "\""; 7404c30e226SCarson Labrado // We received as response that was not a json 7414c30e226SCarson Labrado // Notify the user only if we did not receive any valid responses, 7424c30e226SCarson Labrado // if the resource collection does not already exist on the 7434c30e226SCarson Labrado // aggregating BMC, and if we did not already set this warning due 7444c30e226SCarson Labrado // to a failure from a different satellite 7454c30e226SCarson Labrado if ((asyncResp->res.resultInt() != 200) && 7464c30e226SCarson Labrado (asyncResp->res.resultInt() != 502)) 7474c30e226SCarson Labrado { 7484c30e226SCarson Labrado messages::operationFailed(asyncResp->res); 7494c30e226SCarson Labrado } 7504c30e226SCarson Labrado } 7514c30e226SCarson Labrado } // End processCollectionResponse() 7524c30e226SCarson Labrado 7537fb33566SCarson Labrado public: 7547fb33566SCarson Labrado RedfishAggregator(const RedfishAggregator&) = delete; 7557fb33566SCarson Labrado RedfishAggregator& operator=(const RedfishAggregator&) = delete; 7567fb33566SCarson Labrado RedfishAggregator(RedfishAggregator&&) = delete; 7577fb33566SCarson Labrado RedfishAggregator& operator=(RedfishAggregator&&) = delete; 7587fb33566SCarson Labrado ~RedfishAggregator() = default; 7597fb33566SCarson Labrado 7607fb33566SCarson Labrado static RedfishAggregator& getInstance() 7617fb33566SCarson Labrado { 7627fb33566SCarson Labrado static RedfishAggregator handler; 7637fb33566SCarson Labrado return handler; 7647fb33566SCarson Labrado } 76505916cefSCarson Labrado 76605916cefSCarson Labrado // Entry point to Redfish Aggregation 76705916cefSCarson Labrado // Returns Result stating whether or not we still need to locally handle the 76805916cefSCarson Labrado // request 76905916cefSCarson Labrado static Result 77005916cefSCarson Labrado beginAggregation(const crow::Request& thisReq, 77105916cefSCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 77205916cefSCarson Labrado { 77305916cefSCarson Labrado using crow::utility::OrMorePaths; 77405916cefSCarson Labrado using crow::utility::readUrlSegments; 77505916cefSCarson Labrado const boost::urls::url_view& url = thisReq.urlView; 77605916cefSCarson Labrado // UpdateService is the only top level resource that is not a Collection 77705916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService")) 77805916cefSCarson Labrado { 77905916cefSCarson Labrado return Result::LocalHandle; 78005916cefSCarson Labrado } 781*411e6a11SCarson Labrado 782*411e6a11SCarson Labrado // We don't need to aggregate JsonSchemas due to potential issues such 783*411e6a11SCarson Labrado // as version mismatches between aggregator and satellite BMCs. For 784*411e6a11SCarson Labrado // now assume that the aggregator has all the schemas and versions that 785*411e6a11SCarson Labrado // the aggregated server has. 786*411e6a11SCarson Labrado if (crow::utility::readUrlSegments(url, "redfish", "v1", "JsonSchemas", 787*411e6a11SCarson Labrado crow::utility::OrMorePaths())) 788*411e6a11SCarson Labrado { 789*411e6a11SCarson Labrado return Result::LocalHandle; 790*411e6a11SCarson Labrado } 791*411e6a11SCarson Labrado 79246a81465SCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 79346a81465SCarson Labrado "SoftwareInventory") || 79446a81465SCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 79546a81465SCarson Labrado "FirmwareInventory")) 79646a81465SCarson Labrado { 79746a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 79846a81465SCarson Labrado return Result::LocalHandle; 79946a81465SCarson Labrado } 80005916cefSCarson Labrado 80105916cefSCarson Labrado // Is the request for a resource collection?: 80205916cefSCarson Labrado // /redfish/v1/<resource> 80305916cefSCarson Labrado // e.g. /redfish/v1/Chassis 80405916cefSCarson Labrado std::string collectionName; 80505916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", std::ref(collectionName))) 80605916cefSCarson Labrado { 80746a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 80805916cefSCarson Labrado return Result::LocalHandle; 80905916cefSCarson Labrado } 81005916cefSCarson Labrado 81105916cefSCarson Labrado // We know that the ID of an aggregated resource will begin with 81205916cefSCarson Labrado // "5B247A". For the most part the URI will begin like this: 81305916cefSCarson Labrado // /redfish/v1/<resource>/<resource ID> 81405916cefSCarson Labrado // Note, FirmwareInventory and SoftwareInventory are "special" because 81505916cefSCarson Labrado // they are two levels deep, but still need aggregated 81605916cefSCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory/<FirmwareInventory ID> 81705916cefSCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory/<SoftwareInventory ID> 81805916cefSCarson Labrado std::string memberName; 81905916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 82005916cefSCarson Labrado "SoftwareInventory", std::ref(memberName), 82105916cefSCarson Labrado OrMorePaths()) || 82205916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 82305916cefSCarson Labrado "FirmwareInventory", std::ref(memberName), 82405916cefSCarson Labrado OrMorePaths()) || 82505916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", std::ref(collectionName), 82605916cefSCarson Labrado std::ref(memberName), OrMorePaths())) 82705916cefSCarson Labrado { 82805916cefSCarson Labrado if (memberName.starts_with("5B247A")) 82905916cefSCarson Labrado { 83005916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Need to forward a request"; 83105916cefSCarson Labrado 83246a81465SCarson Labrado // Extract the prefix from the request's URI, retrieve the 83346a81465SCarson Labrado // associated satellite config information, and then forward the 83446a81465SCarson Labrado // request to that satellite. 83546a81465SCarson Labrado startAggregation(AggregationType::Resource, thisReq, asyncResp); 83605916cefSCarson Labrado return Result::NoLocalHandle; 83705916cefSCarson Labrado } 83805916cefSCarson Labrado return Result::LocalHandle; 83905916cefSCarson Labrado } 84005916cefSCarson Labrado 84105916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation not required"; 84205916cefSCarson Labrado return Result::LocalHandle; 84305916cefSCarson Labrado } 8447fb33566SCarson Labrado }; 8457fb33566SCarson Labrado 8467fb33566SCarson Labrado } // namespace redfish 847