1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "error_messages.hpp" 7 #include "http_request.hpp" 8 #include "http_response.hpp" 9 #include "query.hpp" 10 #include "redfish_aggregator.hpp" 11 #include "registries/privilege_registry.hpp" 12 13 #include <boost/url/format.hpp> 14 #include <nlohmann/json.hpp> 15 16 #include <functional> 17 #include <memory> 18 19 namespace redfish 20 { 21 22 inline void handleAggregationServiceHead( 23 App& app, const crow::Request& req, 24 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 25 { 26 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 27 { 28 return; 29 } 30 asyncResp->res.addHeader( 31 boost::beast::http::field::link, 32 "</redfish/v1/JsonSchemas/AggregationService/AggregationService.json>; rel=describedby"); 33 } 34 35 inline void handleAggregationServiceGet( 36 App& app, const crow::Request& req, 37 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 38 { 39 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 40 { 41 return; 42 } 43 asyncResp->res.addHeader( 44 boost::beast::http::field::link, 45 "</redfish/v1/JsonSchemas/AggregationService/AggregationService.json>; rel=describedby"); 46 nlohmann::json& json = asyncResp->res.jsonValue; 47 json["@odata.id"] = "/redfish/v1/AggregationService"; 48 json["@odata.type"] = "#AggregationService.v1_0_1.AggregationService"; 49 json["Id"] = "AggregationService"; 50 json["Name"] = "Aggregation Service"; 51 json["Description"] = "Aggregation Service"; 52 json["ServiceEnabled"] = true; 53 json["AggregationSources"]["@odata.id"] = 54 "/redfish/v1/AggregationService/AggregationSources"; 55 } 56 57 inline void requestRoutesAggregationService(App& app) 58 { 59 BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/") 60 .privileges(redfish::privileges::headAggregationService) 61 .methods(boost::beast::http::verb::head)( 62 std::bind_front(handleAggregationServiceHead, std::ref(app))); 63 BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/") 64 .privileges(redfish::privileges::getAggregationService) 65 .methods(boost::beast::http::verb::get)( 66 std::bind_front(handleAggregationServiceGet, std::ref(app))); 67 } 68 69 inline void populateAggregationSourceCollection( 70 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 71 const boost::system::error_code& ec, 72 const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 73 { 74 // Something went wrong while querying dbus 75 if (ec) 76 { 77 messages::internalError(asyncResp->res); 78 return; 79 } 80 nlohmann::json::array_t members = nlohmann::json::array(); 81 for (const auto& sat : satelliteInfo) 82 { 83 nlohmann::json::object_t member; 84 member["@odata.id"] = boost::urls::format( 85 "/redfish/v1/AggregationService/AggregationSources/{}", sat.first); 86 members.emplace_back(std::move(member)); 87 } 88 asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 89 asyncResp->res.jsonValue["Members"] = std::move(members); 90 } 91 92 inline void handleAggregationSourceCollectionGet( 93 App& app, const crow::Request& req, 94 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 95 { 96 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 97 { 98 return; 99 } 100 asyncResp->res.addHeader( 101 boost::beast::http::field::link, 102 "</redfish/v1/JsonSchemas/AggregationSourceCollection/AggregationSourceCollection.json>; rel=describedby"); 103 nlohmann::json& json = asyncResp->res.jsonValue; 104 json["@odata.id"] = "/redfish/v1/AggregationService/AggregationSources"; 105 json["@odata.type"] = 106 "#AggregationSourceCollection.AggregationSourceCollection"; 107 json["Name"] = "Aggregation Source Collection"; 108 109 // Query D-Bus for satellite configs and add them to the Members array 110 RedfishAggregator::getSatelliteConfigs( 111 std::bind_front(populateAggregationSourceCollection, asyncResp)); 112 } 113 114 inline void handleAggregationSourceCollectionHead( 115 App& app, const crow::Request& req, 116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 117 { 118 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 119 { 120 return; 121 } 122 asyncResp->res.addHeader( 123 boost::beast::http::field::link, 124 "</redfish/v1/JsonSchemas/AggregationService/AggregationSourceCollection.json>; rel=describedby"); 125 } 126 127 inline void requestRoutesAggregationSourceCollection(App& app) 128 { 129 BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/AggregationSources/") 130 .privileges(redfish::privileges::getAggregationSourceCollection) 131 .methods(boost::beast::http::verb::get)(std::bind_front( 132 handleAggregationSourceCollectionGet, std::ref(app))); 133 134 BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/AggregationSources/") 135 .privileges(redfish::privileges::getAggregationSourceCollection) 136 .methods(boost::beast::http::verb::head)(std::bind_front( 137 handleAggregationSourceCollectionHead, std::ref(app))); 138 } 139 140 inline void populateAggregationSource( 141 const std::string& aggregationSourceId, 142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 143 const boost::system::error_code& ec, 144 const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 145 { 146 asyncResp->res.addHeader( 147 boost::beast::http::field::link, 148 "</redfish/v1/JsonSchemas/AggregationSource/AggregationSource.json>; rel=describedby"); 149 150 // Something went wrong while querying dbus 151 if (ec) 152 { 153 messages::internalError(asyncResp->res); 154 return; 155 } 156 157 const auto& sat = satelliteInfo.find(aggregationSourceId); 158 if (sat == satelliteInfo.end()) 159 { 160 messages::resourceNotFound(asyncResp->res, "AggregationSource", 161 aggregationSourceId); 162 return; 163 } 164 165 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 166 "/redfish/v1/AggregationService/AggregationSources/{}", 167 aggregationSourceId); 168 asyncResp->res.jsonValue["@odata.type"] = 169 "#AggregationSource.v1_3_1.AggregationSource"; 170 asyncResp->res.jsonValue["Id"] = aggregationSourceId; 171 172 // TODO: We may want to change this whenever we support aggregating multiple 173 // satellite BMCs. Otherwise all AggregationSource resources will have the 174 // same "Name". 175 // TODO: We should use the "Name" from the satellite config whenever we add 176 // support for including it in the data returned in satelliteInfo. 177 asyncResp->res.jsonValue["Name"] = "Aggregation source"; 178 std::string hostName(sat->second.encoded_origin()); 179 asyncResp->res.jsonValue["HostName"] = std::move(hostName); 180 181 // The Redfish spec requires Password to be null in responses 182 asyncResp->res.jsonValue["Password"] = nullptr; 183 } 184 185 inline void handleAggregationSourceGet( 186 App& app, const crow::Request& req, 187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 188 const std::string& aggregationSourceId) 189 { 190 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 191 { 192 return; 193 } 194 195 // Query D-Bus for satellite config corresponding to the specified 196 // AggregationSource 197 RedfishAggregator::getSatelliteConfigs(std::bind_front( 198 populateAggregationSource, aggregationSourceId, asyncResp)); 199 } 200 201 inline void handleAggregationSourceHead( 202 App& app, const crow::Request& req, 203 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 204 const std::string& aggregationSourceId) 205 { 206 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 207 { 208 return; 209 } 210 asyncResp->res.addHeader( 211 boost::beast::http::field::link, 212 "</redfish/v1/JsonSchemas/AggregationService/AggregationSource.json>; rel=describedby"); 213 214 // Needed to prevent unused variable error 215 BMCWEB_LOG_DEBUG("Added link header to response from {}", 216 aggregationSourceId); 217 } 218 219 inline void requestRoutesAggregationSource(App& app) 220 { 221 BMCWEB_ROUTE(app, 222 "/redfish/v1/AggregationService/AggregationSources/<str>/") 223 .privileges(redfish::privileges::getAggregationSource) 224 .methods(boost::beast::http::verb::get)( 225 std::bind_front(handleAggregationSourceGet, std::ref(app))); 226 } 227 228 } // namespace redfish 229