Aggregation: Add satellite only links
Allows the aggregator to insert links to top level collections which are
only supported by the satellite bmc.
Tested:
Links were added to responses for collections which are not currently
supported by BMCWeb. Also verified that top level collections and
satellite resources were still aggregated correctly.
curl localhost/redfish/v1
{
...
"Fabrics": {
"@odata.id": "/redfish/v1/Fabrics"
},
...
}
curl localhost/redfish/v1/UpdateService
{
...
"SoftwareInventory": {
"@odata.id": "/redfish/v1/UpdateService/SoftwareInventory"
}
}
The following $expand queries also returned as expected
curl -s 'localhost/redfish/v1?$expand=.($levels=1)'
curl -s 'localhost/redfish/v1/UpdateService?$expand=.($levels=1)'
Signed-off-by: Carson Labrado <clabrado@google.com>
Change-Id: Ie755f67bd28f81f6677670c09c9a210935ae0af9
diff --git a/redfish-core/include/redfish_aggregator.hpp b/redfish-core/include/redfish_aggregator.hpp
index a6920e8..7cae89a 100644
--- a/redfish-core/include/redfish_aggregator.hpp
+++ b/redfish-core/include/redfish_aggregator.hpp
@@ -527,20 +527,29 @@
enum AggregationType
{
Collection,
+ ContainsSubordinate,
Resource,
};
static void
- startAggregation(AggregationType isCollection,
- const crow::Request& thisReq,
+ startAggregation(AggregationType aggType, const crow::Request& thisReq,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
- if ((isCollection == AggregationType::Collection) &&
- (thisReq.method() != boost::beast::http::verb::get))
+ if (thisReq.method() != boost::beast::http::verb::get)
{
- BMCWEB_LOG_DEBUG
- << "Only aggregate GET requests to top level collections";
- return;
+ if (aggType == AggregationType::Collection)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Only aggregate GET requests to top level collections";
+ return;
+ }
+
+ if (aggType == AggregationType::ContainsSubordinate)
+ {
+ BMCWEB_LOG_DEBUG << "Only aggregate GET requests when uptree of"
+ << " a top level collection";
+ return;
+ }
}
// Create a copy of thisReq so we we can still locally process the req
@@ -549,15 +558,15 @@
if (ec)
{
BMCWEB_LOG_ERROR << "Failed to create copy of request";
- if (isCollection != AggregationType::Collection)
+ if (aggType == AggregationType::Resource)
{
messages::internalError(asyncResp->res);
}
return;
}
- getSatelliteConfigs(std::bind_front(aggregateAndHandle, isCollection,
- localReq, asyncResp));
+ getSatelliteConfigs(
+ std::bind_front(aggregateAndHandle, aggType, localReq, asyncResp));
}
static void findSatellite(
@@ -592,7 +601,7 @@
// Intended to handle an incoming request based on if Redfish Aggregation
// is enabled. Forwards request to satellite BMC if it exists.
static void aggregateAndHandle(
- AggregationType isCollection,
+ AggregationType aggType,
const std::shared_ptr<crow::Request>& sharedReq,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const boost::system::error_code& ec,
@@ -613,9 +622,10 @@
// aggregate
if (satelliteInfo.empty())
{
- // For collections we'll also handle the request locally so we
+ // For collections or resources that can contain a subordinate
+ // top level collection we'll also handle the request locally so we
// don't need to write an error code
- if (isCollection == AggregationType::Resource)
+ if (aggType == AggregationType::Resource)
{
std::string nameStr = sharedReq->url().segments().back();
messages::resourceNotFound(asyncResp->res, "", nameStr);
@@ -629,7 +639,7 @@
// We previously determined the request is for a collection. No need to
// check again
- if (isCollection == AggregationType::Collection)
+ if (aggType == AggregationType::Collection)
{
BMCWEB_LOG_DEBUG << "Aggregating a collection";
// We need to use a specific response handler and send the
@@ -639,6 +649,19 @@
return;
}
+ // We previously determined the request may contain a subordinate
+ // collection. No need to check again
+ if (aggType == AggregationType::ContainsSubordinate)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Aggregating what may have a subordinate collection";
+ // We need to use a specific response handler and send the
+ // request to all known satellites
+ getInstance().forwardContainsSubordinateRequests(thisReq, asyncResp,
+ satelliteInfo);
+ return;
+ }
+
const boost::urls::segments_view urlSegments = thisReq.url().segments();
boost::urls::url currentUrl("/");
boost::urls::segments_view::iterator it = urlSegments.begin();
@@ -728,6 +751,31 @@
}
}
+ // Forward request for a URI that is uptree of a top level collection to
+ // each known satellite BMC
+ void forwardContainsSubordinateRequests(
+ const crow::Request& thisReq,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
+ {
+ for (const auto& sat : satelliteInfo)
+ {
+ std::function<void(crow::Response&)> cb = std::bind_front(
+ processContainsSubordinateResponse, sat.first, asyncResp);
+
+ // will ignore an expanded resource in the response if that resource
+ // is not already supported by the aggregating BMC
+ // TODO: Improve the processing so that we don't have to strip query
+ // params in this specific case
+ std::string targetURI(thisReq.url().path());
+ std::string data = thisReq.req.body();
+ client.sendDataWithCallback(
+ std::move(data), std::string(sat.second.host()),
+ sat.second.port_number(), targetURI, false /*useSSL*/,
+ thisReq.fields(), thisReq.method(), cb);
+ }
+ }
+
public:
explicit RedfishAggregator(boost::asio::io_context& ioc) :
client(ioc,
@@ -1060,12 +1108,12 @@
continue;
}
- BMCWEB_LOG_DEBUG << "Adding link for " << *strValue
- << " from BMC " << prefix;
addedLinks = true;
if (!asyncResp->res.jsonValue.contains(prop.first))
{
// Only add the property if it did not already exist
+ BMCWEB_LOG_DEBUG << "Adding link for " << *strValue
+ << " from BMC " << prefix;
asyncResp->res.jsonValue[prop.first]["@odata.id"] =
*strValue;
continue;
@@ -1214,7 +1262,16 @@
return Result::LocalHandle;
}
- BMCWEB_LOG_DEBUG << "Aggregation not required";
+ // If nothing else then the request could be for a resource which has a
+ // top level collection as a subordinate
+ if (searchCollectionsArray(url.path(), SearchType::ContainsSubordinate))
+ {
+ startAggregation(AggregationType::ContainsSubordinate, thisReq,
+ asyncResp);
+ return Result::LocalHandle;
+ }
+
+ BMCWEB_LOG_DEBUG << "Aggregation not required for " << url.buffer();
return Result::LocalHandle;
}
};