xref: /openbmc/bmcweb/features/redfish/include/redfish_aggregator.hpp (revision 46a81465f991637341b0a877b3a027db58a3d330)
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