17fb33566SCarson Labrado #pragma once 27fb33566SCarson Labrado 3*46a81465SCarson Labrado #include <dbus_utility.hpp> 4*46a81465SCarson Labrado #include <error_messages.hpp> 57fb33566SCarson Labrado #include <http_client.hpp> 6*46a81465SCarson Labrado #include <http_connection.hpp> 77fb33566SCarson Labrado 87fb33566SCarson Labrado namespace redfish 97fb33566SCarson Labrado { 107fb33566SCarson Labrado 1105916cefSCarson Labrado enum class Result 1205916cefSCarson Labrado { 1305916cefSCarson Labrado LocalHandle, 1405916cefSCarson Labrado NoLocalHandle 1505916cefSCarson Labrado }; 1605916cefSCarson Labrado 177fb33566SCarson Labrado class RedfishAggregator 187fb33566SCarson Labrado { 197fb33566SCarson Labrado private: 20a7a80296SCarson Labrado const std::string retryPolicyName = "RedfishAggregation"; 21a7a80296SCarson Labrado const uint32_t retryAttempts = 5; 22a7a80296SCarson Labrado const uint32_t retryTimeoutInterval = 0; 23*46a81465SCarson Labrado const std::string id = "Aggregator"; 24a7a80296SCarson Labrado 257fb33566SCarson Labrado RedfishAggregator() 267fb33566SCarson Labrado { 277fb33566SCarson Labrado getSatelliteConfigs(constructorCallback); 28a7a80296SCarson Labrado 29a7a80296SCarson Labrado // Setup the retry policy to be used by Redfish Aggregation 30a7a80296SCarson Labrado crow::HttpClient::getInstance().setRetryConfig( 31a7a80296SCarson Labrado retryAttempts, retryTimeoutInterval, aggregationRetryHandler, 32a7a80296SCarson Labrado retryPolicyName); 337fb33566SCarson Labrado } 347fb33566SCarson Labrado 35a7a80296SCarson Labrado static inline boost::system::error_code 36a7a80296SCarson Labrado aggregationRetryHandler(unsigned int respCode) 37a7a80296SCarson Labrado { 38a7a80296SCarson Labrado // As a default, assume 200X is alright. 39a7a80296SCarson Labrado // We don't need to retry on a 404 40a7a80296SCarson Labrado if ((respCode < 200) || ((respCode >= 300) && (respCode != 404))) 41a7a80296SCarson Labrado { 42a7a80296SCarson Labrado return boost::system::errc::make_error_code( 43a7a80296SCarson Labrado boost::system::errc::result_out_of_range); 44a7a80296SCarson Labrado } 45a7a80296SCarson Labrado 46a7a80296SCarson Labrado // Return 0 if the response code is valid 47a7a80296SCarson Labrado return boost::system::errc::make_error_code( 48a7a80296SCarson Labrado boost::system::errc::success); 499fa6d147SNan Zhou } 50a7a80296SCarson Labrado 517fb33566SCarson Labrado // Dummy callback used by the Constructor so that it can report the number 527fb33566SCarson Labrado // of satellite configs when the class is first created 537fb33566SCarson Labrado static void constructorCallback( 547fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 557fb33566SCarson Labrado { 567fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "There were " 577fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 587fb33566SCarson Labrado << " satellite configs found at startup"; 597fb33566SCarson Labrado } 607fb33566SCarson Labrado 617fb33566SCarson Labrado // Polls D-Bus to get all available satellite config information 627fb33566SCarson Labrado // Expects a handler which interacts with the returned configs 637fb33566SCarson Labrado static void getSatelliteConfigs( 647fb33566SCarson Labrado const std::function<void( 657fb33566SCarson Labrado const std::unordered_map<std::string, boost::urls::url>&)>& handler) 667fb33566SCarson Labrado { 677fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Gathering satellite configs"; 687fb33566SCarson Labrado crow::connections::systemBus->async_method_call( 697fb33566SCarson Labrado [handler](const boost::system::error_code ec, 707fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects) { 717fb33566SCarson Labrado if (ec) 727fb33566SCarson Labrado { 73002d39b4SEd Tanous BMCWEB_LOG_ERROR << "DBUS response error " << ec.value() << ", " 74002d39b4SEd Tanous << ec.message(); 757fb33566SCarson Labrado return; 767fb33566SCarson Labrado } 777fb33566SCarson Labrado 787fb33566SCarson Labrado // Maps a chosen alias representing a satellite BMC to a url 797fb33566SCarson Labrado // containing the information required to create a http 807fb33566SCarson Labrado // connection to the satellite 817fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url> satelliteInfo; 827fb33566SCarson Labrado 837fb33566SCarson Labrado findSatelliteConfigs(objects, satelliteInfo); 847fb33566SCarson Labrado 857fb33566SCarson Labrado if (!satelliteInfo.empty()) 867fb33566SCarson Labrado { 877fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with " 887fb33566SCarson Labrado << std::to_string(satelliteInfo.size()) 897fb33566SCarson Labrado << " satellite BMCs"; 907fb33566SCarson Labrado } 917fb33566SCarson Labrado else 927fb33566SCarson Labrado { 937fb33566SCarson Labrado BMCWEB_LOG_DEBUG 947fb33566SCarson Labrado << "No satellite BMCs detected. Redfish Aggregation not enabled"; 957fb33566SCarson Labrado } 967fb33566SCarson Labrado handler(satelliteInfo); 977fb33566SCarson Labrado }, 987fb33566SCarson Labrado "xyz.openbmc_project.EntityManager", "/", 997fb33566SCarson Labrado "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1007fb33566SCarson Labrado } 1017fb33566SCarson Labrado 1027fb33566SCarson Labrado // Search D-Bus objects for satellite config objects and add their 1037fb33566SCarson Labrado // information if valid 1047fb33566SCarson Labrado static void findSatelliteConfigs( 1057fb33566SCarson Labrado const dbus::utility::ManagedObjectType& objects, 1067fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1077fb33566SCarson Labrado { 1087fb33566SCarson Labrado for (const auto& objectPath : objects) 1097fb33566SCarson Labrado { 1107fb33566SCarson Labrado for (const auto& interface : objectPath.second) 1117fb33566SCarson Labrado { 1127fb33566SCarson Labrado if (interface.first == 1137fb33566SCarson Labrado "xyz.openbmc_project.Configuration.SatelliteController") 1147fb33566SCarson Labrado { 1157fb33566SCarson Labrado BMCWEB_LOG_DEBUG << "Found Satellite Controller at " 1167fb33566SCarson Labrado << objectPath.first.str; 1177fb33566SCarson Labrado 11805916cefSCarson Labrado if (!satelliteInfo.empty()) 11905916cefSCarson Labrado { 12005916cefSCarson Labrado BMCWEB_LOG_ERROR 12105916cefSCarson Labrado << "Redfish Aggregation only supports one satellite!"; 12205916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Clearing all satellite data"; 12305916cefSCarson Labrado satelliteInfo.clear(); 12405916cefSCarson Labrado return; 12505916cefSCarson Labrado } 12605916cefSCarson Labrado 12705916cefSCarson Labrado // For now assume there will only be one satellite config. 12805916cefSCarson Labrado // Assign it the name/prefix "5B247A" 12905916cefSCarson Labrado addSatelliteConfig("5B247A", interface.second, 13005916cefSCarson Labrado satelliteInfo); 1317fb33566SCarson Labrado } 1327fb33566SCarson Labrado } 1337fb33566SCarson Labrado } 1347fb33566SCarson Labrado } 1357fb33566SCarson Labrado 1367fb33566SCarson Labrado // Parse the properties of a satellite config object and add the 1377fb33566SCarson Labrado // configuration if the properties are valid 1387fb33566SCarson Labrado static void addSatelliteConfig( 13905916cefSCarson Labrado const std::string& name, 1407fb33566SCarson Labrado const dbus::utility::DBusPropertiesMap& properties, 1417fb33566SCarson Labrado std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1427fb33566SCarson Labrado { 1437fb33566SCarson Labrado boost::urls::url url; 1447fb33566SCarson Labrado 1457fb33566SCarson Labrado for (const auto& prop : properties) 1467fb33566SCarson Labrado { 14705916cefSCarson Labrado if (prop.first == "Hostname") 1487fb33566SCarson Labrado { 1497fb33566SCarson Labrado const std::string* propVal = 1507fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1517fb33566SCarson Labrado if (propVal == nullptr) 1527fb33566SCarson Labrado { 1537fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Hostname value"; 1547fb33566SCarson Labrado return; 1557fb33566SCarson Labrado } 1567fb33566SCarson Labrado url.set_host(*propVal); 1577fb33566SCarson Labrado } 1587fb33566SCarson Labrado 1597fb33566SCarson Labrado else if (prop.first == "Port") 1607fb33566SCarson Labrado { 1617fb33566SCarson Labrado const uint64_t* propVal = std::get_if<uint64_t>(&prop.second); 1627fb33566SCarson Labrado if (propVal == nullptr) 1637fb33566SCarson Labrado { 1647fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid Port value"; 1657fb33566SCarson Labrado return; 1667fb33566SCarson Labrado } 1677fb33566SCarson Labrado 1687fb33566SCarson Labrado if (*propVal > std::numeric_limits<uint16_t>::max()) 1697fb33566SCarson Labrado { 1707fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Port value out of range"; 1717fb33566SCarson Labrado return; 1727fb33566SCarson Labrado } 1737fb33566SCarson Labrado url.set_port(static_cast<uint16_t>(*propVal)); 1747fb33566SCarson Labrado } 1757fb33566SCarson Labrado 1767fb33566SCarson Labrado else if (prop.first == "AuthType") 1777fb33566SCarson Labrado { 1787fb33566SCarson Labrado const std::string* propVal = 1797fb33566SCarson Labrado std::get_if<std::string>(&prop.second); 1807fb33566SCarson Labrado if (propVal == nullptr) 1817fb33566SCarson Labrado { 1827fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Invalid AuthType value"; 1837fb33566SCarson Labrado return; 1847fb33566SCarson Labrado } 1857fb33566SCarson Labrado 1867fb33566SCarson Labrado // For now assume authentication not required to communicate 1877fb33566SCarson Labrado // with the satellite BMC 1887fb33566SCarson Labrado if (*propVal != "None") 1897fb33566SCarson Labrado { 1907fb33566SCarson Labrado BMCWEB_LOG_ERROR 1917fb33566SCarson Labrado << "Unsupported AuthType value: " << *propVal 1927fb33566SCarson Labrado << ", only \"none\" is supported"; 1937fb33566SCarson Labrado return; 1947fb33566SCarson Labrado } 1957fb33566SCarson Labrado url.set_scheme("http"); 1967fb33566SCarson Labrado } 1977fb33566SCarson Labrado } // Finished reading properties 1987fb33566SCarson Labrado 1997fb33566SCarson Labrado // Make sure all required config information was made available 2007fb33566SCarson Labrado if (url.host().empty()) 2017fb33566SCarson Labrado { 2027fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host"; 2037fb33566SCarson Labrado return; 2047fb33566SCarson Labrado } 2057fb33566SCarson Labrado 2067fb33566SCarson Labrado if (!url.has_port()) 2077fb33566SCarson Labrado { 2087fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port"; 2097fb33566SCarson Labrado return; 2107fb33566SCarson Labrado } 2117fb33566SCarson Labrado 2127fb33566SCarson Labrado if (!url.has_scheme()) 2137fb33566SCarson Labrado { 2147fb33566SCarson Labrado BMCWEB_LOG_ERROR << "Satellite config " << name 2157fb33566SCarson Labrado << " missing AuthType"; 2167fb33566SCarson Labrado return; 2177fb33566SCarson Labrado } 2187fb33566SCarson Labrado 2197fb33566SCarson Labrado std::string resultString; 2207fb33566SCarson Labrado auto result = satelliteInfo.insert_or_assign(name, std::move(url)); 2217fb33566SCarson Labrado if (result.second) 2227fb33566SCarson Labrado { 2237fb33566SCarson Labrado resultString = "Added new satellite config "; 2247fb33566SCarson Labrado } 2257fb33566SCarson Labrado else 2267fb33566SCarson Labrado { 2277fb33566SCarson Labrado resultString = "Updated existing satellite config "; 2287fb33566SCarson Labrado } 2297fb33566SCarson Labrado 2307fb33566SCarson Labrado BMCWEB_LOG_DEBUG << resultString << name << " at " 2317fb33566SCarson Labrado << result.first->second.scheme() << "://" 2327fb33566SCarson Labrado << result.first->second.encoded_host_and_port(); 2337fb33566SCarson Labrado } 2347fb33566SCarson Labrado 235*46a81465SCarson Labrado enum AggregationType 236*46a81465SCarson Labrado { 237*46a81465SCarson Labrado Collection, 238*46a81465SCarson Labrado Resource, 239*46a81465SCarson Labrado }; 240*46a81465SCarson Labrado 241*46a81465SCarson Labrado static void 242*46a81465SCarson Labrado startAggregation(AggregationType isCollection, 243*46a81465SCarson Labrado const crow::Request& thisReq, 244*46a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 245*46a81465SCarson Labrado { 246*46a81465SCarson Labrado // Create a copy of thisReq so we we can still locally process the req 247*46a81465SCarson Labrado std::error_code ec; 248*46a81465SCarson Labrado auto localReq = std::make_shared<crow::Request>(thisReq.req, ec); 249*46a81465SCarson Labrado if (ec) 250*46a81465SCarson Labrado { 251*46a81465SCarson Labrado BMCWEB_LOG_ERROR << "Failed to create copy of request"; 252*46a81465SCarson Labrado if (isCollection != AggregationType::Collection) 253*46a81465SCarson Labrado { 254*46a81465SCarson Labrado messages::internalError(asyncResp->res); 255*46a81465SCarson Labrado } 256*46a81465SCarson Labrado return; 257*46a81465SCarson Labrado } 258*46a81465SCarson Labrado 259*46a81465SCarson Labrado getSatelliteConfigs(std::bind_front(aggregateAndHandle, isCollection, 260*46a81465SCarson Labrado localReq, asyncResp)); 261*46a81465SCarson Labrado } 262*46a81465SCarson Labrado 263*46a81465SCarson Labrado static void findSatelite( 264*46a81465SCarson Labrado const crow::Request& req, 265*46a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 266*46a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo, 267*46a81465SCarson Labrado std::string_view memberName) 268*46a81465SCarson Labrado { 269*46a81465SCarson Labrado // Determine if the resource ID begins with a known prefix 270*46a81465SCarson Labrado for (const auto& satellite : satelliteInfo) 271*46a81465SCarson Labrado { 272*46a81465SCarson Labrado std::string targetPrefix = satellite.first; 273*46a81465SCarson Labrado targetPrefix += "_"; 274*46a81465SCarson Labrado if (memberName.starts_with(targetPrefix)) 275*46a81465SCarson Labrado { 276*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "\"" << satellite.first 277*46a81465SCarson Labrado << "\" is a known prefix"; 278*46a81465SCarson Labrado 279*46a81465SCarson Labrado // Remove the known prefix from the request's URI and 280*46a81465SCarson Labrado // then forward to the associated satellite BMC 281*46a81465SCarson Labrado getInstance().forwardRequest(req, asyncResp, satellite.first, 282*46a81465SCarson Labrado satelliteInfo); 283*46a81465SCarson Labrado return; 284*46a81465SCarson Labrado } 285*46a81465SCarson Labrado } 286*46a81465SCarson Labrado } 287*46a81465SCarson Labrado 288*46a81465SCarson Labrado // Intended to handle an incoming request based on if Redfish Aggregation 289*46a81465SCarson Labrado // is enabled. Forwards request to satellite BMC if it exists. 290*46a81465SCarson Labrado static void aggregateAndHandle( 291*46a81465SCarson Labrado AggregationType isCollection, 292*46a81465SCarson Labrado const std::shared_ptr<crow::Request>& sharedReq, 293*46a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 294*46a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 295*46a81465SCarson Labrado { 296*46a81465SCarson Labrado if (sharedReq == nullptr) 297*46a81465SCarson Labrado { 298*46a81465SCarson Labrado return; 299*46a81465SCarson Labrado } 300*46a81465SCarson Labrado const crow::Request& thisReq = *sharedReq; 301*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation is enabled, begin processing of " 302*46a81465SCarson Labrado << thisReq.target(); 303*46a81465SCarson Labrado 304*46a81465SCarson Labrado // We previously determined the request is for a collection. No need to 305*46a81465SCarson Labrado // check again 306*46a81465SCarson Labrado if (isCollection == AggregationType::Collection) 307*46a81465SCarson Labrado { 308*46a81465SCarson Labrado // TODO: This should instead be handled so that we can 309*46a81465SCarson Labrado // aggregate the satellite resource collections 310*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Aggregating a collection"; 311*46a81465SCarson Labrado return; 312*46a81465SCarson Labrado } 313*46a81465SCarson Labrado 314*46a81465SCarson Labrado std::string updateServiceName; 315*46a81465SCarson Labrado std::string memberName; 316*46a81465SCarson Labrado if (crow::utility::readUrlSegments( 317*46a81465SCarson Labrado thisReq.urlView, "redfish", "v1", "UpdateService", 318*46a81465SCarson Labrado std::ref(updateServiceName), std::ref(memberName), 319*46a81465SCarson Labrado crow::utility::OrMorePaths())) 320*46a81465SCarson Labrado { 321*46a81465SCarson Labrado // Must be FirmwareInventory or SoftwareInventory 322*46a81465SCarson Labrado findSatelite(thisReq, asyncResp, satelliteInfo, memberName); 323*46a81465SCarson Labrado return; 324*46a81465SCarson Labrado } 325*46a81465SCarson Labrado 326*46a81465SCarson Labrado std::string collectionName; 327*46a81465SCarson Labrado if (crow::utility::readUrlSegments( 328*46a81465SCarson Labrado thisReq.urlView, "redfish", "v1", std::ref(collectionName), 329*46a81465SCarson Labrado std::ref(memberName), crow::utility::OrMorePaths())) 330*46a81465SCarson Labrado { 331*46a81465SCarson Labrado findSatelite(thisReq, asyncResp, satelliteInfo, memberName); 332*46a81465SCarson Labrado } 333*46a81465SCarson Labrado } 334*46a81465SCarson Labrado 335*46a81465SCarson Labrado // Attempt to forward a request to the satellite BMC associated with the 336*46a81465SCarson Labrado // prefix. 337*46a81465SCarson Labrado void forwardRequest( 338*46a81465SCarson Labrado const crow::Request& thisReq, 339*46a81465SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 340*46a81465SCarson Labrado const std::string& prefix, 341*46a81465SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 342*46a81465SCarson Labrado { 343*46a81465SCarson Labrado const auto& sat = satelliteInfo.find(prefix); 344*46a81465SCarson Labrado if (sat == satelliteInfo.end()) 345*46a81465SCarson Labrado { 346*46a81465SCarson Labrado // Realistically this shouldn't get called since we perform an 347*46a81465SCarson Labrado // earlier check to make sure the prefix exists 348*46a81465SCarson Labrado BMCWEB_LOG_ERROR << "Unrecognized satellite prefix \"" << prefix 349*46a81465SCarson Labrado << "\""; 350*46a81465SCarson Labrado return; 351*46a81465SCarson Labrado } 352*46a81465SCarson Labrado 353*46a81465SCarson Labrado // We need to strip the prefix from the request's path 354*46a81465SCarson Labrado std::string targetURI(thisReq.target()); 355*46a81465SCarson Labrado size_t pos = targetURI.find(prefix + "_"); 356*46a81465SCarson Labrado if (pos == std::string::npos) 357*46a81465SCarson Labrado { 358*46a81465SCarson Labrado // If this fails then something went wrong 359*46a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error removing prefix \"" << prefix 360*46a81465SCarson Labrado << "_\" from request URI"; 361*46a81465SCarson Labrado messages::internalError(asyncResp->res); 362*46a81465SCarson Labrado return; 363*46a81465SCarson Labrado } 364*46a81465SCarson Labrado targetURI.erase(pos, prefix.size() + 1); 365*46a81465SCarson Labrado 366*46a81465SCarson Labrado std::function<void(crow::Response&)> cb = 367*46a81465SCarson Labrado std::bind_front(processResponse, asyncResp); 368*46a81465SCarson Labrado 369*46a81465SCarson Labrado std::string data = thisReq.req.body(); 370*46a81465SCarson Labrado crow::HttpClient::getInstance().sendDataWithCallback( 371*46a81465SCarson Labrado data, id, std::string(sat->second.host()), 372*46a81465SCarson Labrado sat->second.port_number(), targetURI, thisReq.fields, 373*46a81465SCarson Labrado thisReq.method(), retryPolicyName, cb); 374*46a81465SCarson Labrado } 375*46a81465SCarson Labrado 376*46a81465SCarson Labrado // Processes the response returned by a satellite BMC and loads its 377*46a81465SCarson Labrado // contents into asyncResp 378*46a81465SCarson Labrado static void 379*46a81465SCarson Labrado processResponse(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 380*46a81465SCarson Labrado crow::Response& resp) 381*46a81465SCarson Labrado { 382*46a81465SCarson Labrado // No processing needed if the request wasn't successful 383*46a81465SCarson Labrado if (resp.resultInt() != 200) 384*46a81465SCarson Labrado { 385*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "No need to parse satellite response"; 386*46a81465SCarson Labrado asyncResp->res.stringResponse = std::move(resp.stringResponse); 387*46a81465SCarson Labrado return; 388*46a81465SCarson Labrado } 389*46a81465SCarson Labrado 390*46a81465SCarson Labrado // The resp will not have a json component 391*46a81465SCarson Labrado // We need to create a json from resp's stringResponse 392*46a81465SCarson Labrado if (resp.getHeaderValue("Content-Type") == "application/json") 393*46a81465SCarson Labrado { 394*46a81465SCarson Labrado nlohmann::json jsonVal = 395*46a81465SCarson Labrado nlohmann::json::parse(resp.body(), nullptr, false); 396*46a81465SCarson Labrado if (jsonVal.is_discarded()) 397*46a81465SCarson Labrado { 398*46a81465SCarson Labrado BMCWEB_LOG_ERROR << "Error parsing satellite response as JSON"; 399*46a81465SCarson Labrado messages::operationFailed(asyncResp->res); 400*46a81465SCarson Labrado return; 401*46a81465SCarson Labrado } 402*46a81465SCarson Labrado 403*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Successfully parsed satellite response"; 404*46a81465SCarson Labrado 405*46a81465SCarson Labrado // TODO: For collections we want to add the satellite responses to 406*46a81465SCarson Labrado // our response rather than just straight overwriting them if our 407*46a81465SCarson Labrado // local handling was successful (i.e. would return a 200). 408*46a81465SCarson Labrado 409*46a81465SCarson Labrado asyncResp->res.stringResponse.emplace( 410*46a81465SCarson Labrado boost::beast::http::response< 411*46a81465SCarson Labrado boost::beast::http::string_body>{}); 412*46a81465SCarson Labrado asyncResp->res.result(resp.result()); 413*46a81465SCarson Labrado asyncResp->res.jsonValue = std::move(jsonVal); 414*46a81465SCarson Labrado 415*46a81465SCarson Labrado BMCWEB_LOG_DEBUG << "Finished writing asyncResp"; 416*46a81465SCarson Labrado // TODO: Need to fix the URIs in the response so that they include 417*46a81465SCarson Labrado // the prefix 418*46a81465SCarson Labrado } 419*46a81465SCarson Labrado else 420*46a81465SCarson Labrado { 421*46a81465SCarson Labrado if (!resp.body().empty()) 422*46a81465SCarson Labrado { 423*46a81465SCarson Labrado // We received a 200 response without the correct Content-Type 424*46a81465SCarson Labrado // so return an Operation Failed error 425*46a81465SCarson Labrado BMCWEB_LOG_ERROR 426*46a81465SCarson Labrado << "Satellite response must be of type \"application/json\""; 427*46a81465SCarson Labrado messages::operationFailed(asyncResp->res); 428*46a81465SCarson Labrado } 429*46a81465SCarson Labrado } 430*46a81465SCarson Labrado } 431*46a81465SCarson Labrado 4327fb33566SCarson Labrado public: 4337fb33566SCarson Labrado RedfishAggregator(const RedfishAggregator&) = delete; 4347fb33566SCarson Labrado RedfishAggregator& operator=(const RedfishAggregator&) = delete; 4357fb33566SCarson Labrado RedfishAggregator(RedfishAggregator&&) = delete; 4367fb33566SCarson Labrado RedfishAggregator& operator=(RedfishAggregator&&) = delete; 4377fb33566SCarson Labrado ~RedfishAggregator() = default; 4387fb33566SCarson Labrado 4397fb33566SCarson Labrado static RedfishAggregator& getInstance() 4407fb33566SCarson Labrado { 4417fb33566SCarson Labrado static RedfishAggregator handler; 4427fb33566SCarson Labrado return handler; 4437fb33566SCarson Labrado } 44405916cefSCarson Labrado 44505916cefSCarson Labrado // Entry point to Redfish Aggregation 44605916cefSCarson Labrado // Returns Result stating whether or not we still need to locally handle the 44705916cefSCarson Labrado // request 44805916cefSCarson Labrado static Result 44905916cefSCarson Labrado beginAggregation(const crow::Request& thisReq, 45005916cefSCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 45105916cefSCarson Labrado { 45205916cefSCarson Labrado using crow::utility::OrMorePaths; 45305916cefSCarson Labrado using crow::utility::readUrlSegments; 45405916cefSCarson Labrado const boost::urls::url_view& url = thisReq.urlView; 45505916cefSCarson Labrado // UpdateService is the only top level resource that is not a Collection 45605916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService")) 45705916cefSCarson Labrado { 45805916cefSCarson Labrado return Result::LocalHandle; 45905916cefSCarson Labrado } 460*46a81465SCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 461*46a81465SCarson Labrado "SoftwareInventory") || 462*46a81465SCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 463*46a81465SCarson Labrado "FirmwareInventory")) 464*46a81465SCarson Labrado { 465*46a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 466*46a81465SCarson Labrado return Result::LocalHandle; 467*46a81465SCarson Labrado } 46805916cefSCarson Labrado 46905916cefSCarson Labrado // Is the request for a resource collection?: 47005916cefSCarson Labrado // /redfish/v1/<resource> 47105916cefSCarson Labrado // e.g. /redfish/v1/Chassis 47205916cefSCarson Labrado std::string collectionName; 47305916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", std::ref(collectionName))) 47405916cefSCarson Labrado { 475*46a81465SCarson Labrado startAggregation(AggregationType::Collection, thisReq, asyncResp); 47605916cefSCarson Labrado return Result::LocalHandle; 47705916cefSCarson Labrado } 47805916cefSCarson Labrado 47905916cefSCarson Labrado // We know that the ID of an aggregated resource will begin with 48005916cefSCarson Labrado // "5B247A". For the most part the URI will begin like this: 48105916cefSCarson Labrado // /redfish/v1/<resource>/<resource ID> 48205916cefSCarson Labrado // Note, FirmwareInventory and SoftwareInventory are "special" because 48305916cefSCarson Labrado // they are two levels deep, but still need aggregated 48405916cefSCarson Labrado // /redfish/v1/UpdateService/FirmwareInventory/<FirmwareInventory ID> 48505916cefSCarson Labrado // /redfish/v1/UpdateService/SoftwareInventory/<SoftwareInventory ID> 48605916cefSCarson Labrado std::string memberName; 48705916cefSCarson Labrado if (readUrlSegments(url, "redfish", "v1", "UpdateService", 48805916cefSCarson Labrado "SoftwareInventory", std::ref(memberName), 48905916cefSCarson Labrado OrMorePaths()) || 49005916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", "UpdateService", 49105916cefSCarson Labrado "FirmwareInventory", std::ref(memberName), 49205916cefSCarson Labrado OrMorePaths()) || 49305916cefSCarson Labrado readUrlSegments(url, "redfish", "v1", std::ref(collectionName), 49405916cefSCarson Labrado std::ref(memberName), OrMorePaths())) 49505916cefSCarson Labrado { 49605916cefSCarson Labrado if (memberName.starts_with("5B247A")) 49705916cefSCarson Labrado { 49805916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Need to forward a request"; 49905916cefSCarson Labrado 500*46a81465SCarson Labrado // Extract the prefix from the request's URI, retrieve the 501*46a81465SCarson Labrado // associated satellite config information, and then forward the 502*46a81465SCarson Labrado // request to that satellite. 503*46a81465SCarson Labrado startAggregation(AggregationType::Resource, thisReq, asyncResp); 50405916cefSCarson Labrado return Result::NoLocalHandle; 50505916cefSCarson Labrado } 50605916cefSCarson Labrado return Result::LocalHandle; 50705916cefSCarson Labrado } 50805916cefSCarson Labrado 50905916cefSCarson Labrado BMCWEB_LOG_DEBUG << "Aggregation not required"; 51005916cefSCarson Labrado return Result::LocalHandle; 51105916cefSCarson Labrado } 5127fb33566SCarson Labrado }; 5137fb33566SCarson Labrado 5147fb33566SCarson Labrado } // namespace redfish 515