140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 36c068982SEd Tanous #pragma once 46c068982SEd Tanous 56c068982SEd Tanous #include "app.hpp" 6d7857201SEd Tanous #include "async_resp.hpp" 76c068982SEd Tanous #include "error_messages.hpp" 86c068982SEd Tanous #include "http_request.hpp" 96c068982SEd Tanous #include "http_response.hpp" 10d7857201SEd Tanous #include "logging.hpp" 11*66620686SEd Tanous #include "ossl_random.hpp" 126c068982SEd Tanous #include "query.hpp" 138b2521a5SCarson Labrado #include "redfish_aggregator.hpp" 146c068982SEd Tanous #include "registries/privilege_registry.hpp" 15*66620686SEd Tanous #include "utility.hpp" 16*66620686SEd Tanous #include "utils/json_utils.hpp" 176c068982SEd Tanous 18d7857201SEd Tanous #include <boost/beast/http/field.hpp> 19d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 20*66620686SEd Tanous #include <boost/system/result.hpp> 21ef4c65b7SEd Tanous #include <boost/url/format.hpp> 22*66620686SEd Tanous #include <boost/url/parse.hpp> 23d7857201SEd Tanous #include <boost/url/url.hpp> 246c068982SEd Tanous #include <nlohmann/json.hpp> 256c068982SEd Tanous 26*66620686SEd Tanous #include <cstddef> 276c068982SEd Tanous #include <functional> 286c068982SEd Tanous #include <memory> 29d7857201SEd Tanous #include <unordered_map> 30d7857201SEd Tanous #include <utility> 316c068982SEd Tanous 326c068982SEd Tanous namespace redfish 336c068982SEd Tanous { 346c068982SEd Tanous 356c068982SEd Tanous inline void handleAggregationServiceHead( 366c068982SEd Tanous App& app, const crow::Request& req, 376c068982SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 386c068982SEd Tanous { 396c068982SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 406c068982SEd Tanous { 416c068982SEd Tanous return; 426c068982SEd Tanous } 436c068982SEd Tanous asyncResp->res.addHeader( 446c068982SEd Tanous boost::beast::http::field::link, 456c068982SEd Tanous "</redfish/v1/JsonSchemas/AggregationService/AggregationService.json>; rel=describedby"); 466c068982SEd Tanous } 476c068982SEd Tanous 486c068982SEd Tanous inline void handleAggregationServiceGet( 496c068982SEd Tanous App& app, const crow::Request& req, 506c068982SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 516c068982SEd Tanous { 526c068982SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 536c068982SEd Tanous { 546c068982SEd Tanous return; 556c068982SEd Tanous } 566c068982SEd Tanous asyncResp->res.addHeader( 576c068982SEd Tanous boost::beast::http::field::link, 586c068982SEd Tanous "</redfish/v1/JsonSchemas/AggregationService/AggregationService.json>; rel=describedby"); 596c068982SEd Tanous nlohmann::json& json = asyncResp->res.jsonValue; 606c068982SEd Tanous json["@odata.id"] = "/redfish/v1/AggregationService"; 616c068982SEd Tanous json["@odata.type"] = "#AggregationService.v1_0_1.AggregationService"; 626c068982SEd Tanous json["Id"] = "AggregationService"; 636c068982SEd Tanous json["Name"] = "Aggregation Service"; 646c068982SEd Tanous json["Description"] = "Aggregation Service"; 656c068982SEd Tanous json["ServiceEnabled"] = true; 665315c1b1SCarson Labrado json["AggregationSources"]["@odata.id"] = 675315c1b1SCarson Labrado "/redfish/v1/AggregationService/AggregationSources"; 686c068982SEd Tanous } 696c068982SEd Tanous 708b2521a5SCarson Labrado inline void requestRoutesAggregationService(App& app) 716c068982SEd Tanous { 726c068982SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/") 736c068982SEd Tanous .privileges(redfish::privileges::headAggregationService) 746c068982SEd Tanous .methods(boost::beast::http::verb::head)( 756c068982SEd Tanous std::bind_front(handleAggregationServiceHead, std::ref(app))); 766c068982SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/") 776c068982SEd Tanous .privileges(redfish::privileges::getAggregationService) 786c068982SEd Tanous .methods(boost::beast::http::verb::get)( 796c068982SEd Tanous std::bind_front(handleAggregationServiceGet, std::ref(app))); 806c068982SEd Tanous } 816c068982SEd Tanous 828b2521a5SCarson Labrado inline void populateAggregationSourceCollection( 838b2521a5SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 8481c4e330SEd Tanous const boost::system::error_code& ec, 858b2521a5SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 868b2521a5SCarson Labrado { 878b2521a5SCarson Labrado // Something went wrong while querying dbus 888b2521a5SCarson Labrado if (ec) 898b2521a5SCarson Labrado { 908b2521a5SCarson Labrado messages::internalError(asyncResp->res); 918b2521a5SCarson Labrado return; 928b2521a5SCarson Labrado } 9382b286fbSEd Tanous nlohmann::json::array_t members; 948b2521a5SCarson Labrado for (const auto& sat : satelliteInfo) 958b2521a5SCarson Labrado { 968b2521a5SCarson Labrado nlohmann::json::object_t member; 97ef4c65b7SEd Tanous member["@odata.id"] = boost::urls::format( 98ef4c65b7SEd Tanous "/redfish/v1/AggregationService/AggregationSources/{}", sat.first); 99ad539545SPatrick Williams members.emplace_back(std::move(member)); 1008b2521a5SCarson Labrado } 1018b2521a5SCarson Labrado asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 1028b2521a5SCarson Labrado asyncResp->res.jsonValue["Members"] = std::move(members); 1038b2521a5SCarson Labrado } 1048b2521a5SCarson Labrado 1058b2521a5SCarson Labrado inline void handleAggregationSourceCollectionGet( 1065315c1b1SCarson Labrado App& app, const crow::Request& req, 1075315c1b1SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1085315c1b1SCarson Labrado { 1095315c1b1SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1105315c1b1SCarson Labrado { 1115315c1b1SCarson Labrado return; 1125315c1b1SCarson Labrado } 1135315c1b1SCarson Labrado asyncResp->res.addHeader( 1145315c1b1SCarson Labrado boost::beast::http::field::link, 1155315c1b1SCarson Labrado "</redfish/v1/JsonSchemas/AggregationSourceCollection/AggregationSourceCollection.json>; rel=describedby"); 1165315c1b1SCarson Labrado nlohmann::json& json = asyncResp->res.jsonValue; 1175315c1b1SCarson Labrado json["@odata.id"] = "/redfish/v1/AggregationService/AggregationSources"; 1185315c1b1SCarson Labrado json["@odata.type"] = 1195315c1b1SCarson Labrado "#AggregationSourceCollection.AggregationSourceCollection"; 1205315c1b1SCarson Labrado json["Name"] = "Aggregation Source Collection"; 1215315c1b1SCarson Labrado 1228b2521a5SCarson Labrado // Query D-Bus for satellite configs and add them to the Members array 123*66620686SEd Tanous RedfishAggregator::getInstance().getSatelliteConfigs( 1248b2521a5SCarson Labrado std::bind_front(populateAggregationSourceCollection, asyncResp)); 1255315c1b1SCarson Labrado } 1265315c1b1SCarson Labrado 1278b2521a5SCarson Labrado inline void handleAggregationSourceCollectionHead( 1288b2521a5SCarson Labrado App& app, const crow::Request& req, 1298b2521a5SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1308b2521a5SCarson Labrado { 1318b2521a5SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1328b2521a5SCarson Labrado { 1338b2521a5SCarson Labrado return; 1348b2521a5SCarson Labrado } 1358b2521a5SCarson Labrado asyncResp->res.addHeader( 1368b2521a5SCarson Labrado boost::beast::http::field::link, 1378b2521a5SCarson Labrado "</redfish/v1/JsonSchemas/AggregationService/AggregationSourceCollection.json>; rel=describedby"); 1388b2521a5SCarson Labrado } 1398b2521a5SCarson Labrado 1408b2521a5SCarson Labrado inline void requestRoutesAggregationSourceCollection(App& app) 1415315c1b1SCarson Labrado { 1425315c1b1SCarson Labrado BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/AggregationSources/") 1438b2521a5SCarson Labrado .privileges(redfish::privileges::getAggregationSourceCollection) 1448b2521a5SCarson Labrado .methods(boost::beast::http::verb::get)(std::bind_front( 1458b2521a5SCarson Labrado handleAggregationSourceCollectionGet, std::ref(app))); 1468b2521a5SCarson Labrado 1478b2521a5SCarson Labrado BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/AggregationSources/") 1488b2521a5SCarson Labrado .privileges(redfish::privileges::getAggregationSourceCollection) 1498b2521a5SCarson Labrado .methods(boost::beast::http::verb::head)(std::bind_front( 1508b2521a5SCarson Labrado handleAggregationSourceCollectionHead, std::ref(app))); 1518b2521a5SCarson Labrado } 1528b2521a5SCarson Labrado 1538b2521a5SCarson Labrado inline void populateAggregationSource( 1548b2521a5SCarson Labrado const std::string& aggregationSourceId, 1558b2521a5SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 15681c4e330SEd Tanous const boost::system::error_code& ec, 1578b2521a5SCarson Labrado const std::unordered_map<std::string, boost::urls::url>& satelliteInfo) 1588b2521a5SCarson Labrado { 1598b2521a5SCarson Labrado asyncResp->res.addHeader( 1608b2521a5SCarson Labrado boost::beast::http::field::link, 1618b2521a5SCarson Labrado "</redfish/v1/JsonSchemas/AggregationSource/AggregationSource.json>; rel=describedby"); 1628b2521a5SCarson Labrado 1638b2521a5SCarson Labrado // Something went wrong while querying dbus 1648b2521a5SCarson Labrado if (ec) 1658b2521a5SCarson Labrado { 1668b2521a5SCarson Labrado messages::internalError(asyncResp->res); 1678b2521a5SCarson Labrado return; 1688b2521a5SCarson Labrado } 1698b2521a5SCarson Labrado 1708b2521a5SCarson Labrado const auto& sat = satelliteInfo.find(aggregationSourceId); 1718b2521a5SCarson Labrado if (sat == satelliteInfo.end()) 1728b2521a5SCarson Labrado { 1738b2521a5SCarson Labrado messages::resourceNotFound(asyncResp->res, "AggregationSource", 1748b2521a5SCarson Labrado aggregationSourceId); 1758b2521a5SCarson Labrado return; 1768b2521a5SCarson Labrado } 1778b2521a5SCarson Labrado 178ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 179ef4c65b7SEd Tanous "/redfish/v1/AggregationService/AggregationSources/{}", 180ef4c65b7SEd Tanous aggregationSourceId); 1818b2521a5SCarson Labrado asyncResp->res.jsonValue["@odata.type"] = 1828b2521a5SCarson Labrado "#AggregationSource.v1_3_1.AggregationSource"; 1838b2521a5SCarson Labrado asyncResp->res.jsonValue["Id"] = aggregationSourceId; 1848b2521a5SCarson Labrado 1858b2521a5SCarson Labrado // TODO: We may want to change this whenever we support aggregating multiple 1868b2521a5SCarson Labrado // satellite BMCs. Otherwise all AggregationSource resources will have the 1878b2521a5SCarson Labrado // same "Name". 1888b2521a5SCarson Labrado // TODO: We should use the "Name" from the satellite config whenever we add 1898b2521a5SCarson Labrado // support for including it in the data returned in satelliteInfo. 1908b2521a5SCarson Labrado asyncResp->res.jsonValue["Name"] = "Aggregation source"; 1918b2521a5SCarson Labrado std::string hostName(sat->second.encoded_origin()); 1928b2521a5SCarson Labrado asyncResp->res.jsonValue["HostName"] = std::move(hostName); 1938b2521a5SCarson Labrado 1948b2521a5SCarson Labrado // The Redfish spec requires Password to be null in responses 1958b2521a5SCarson Labrado asyncResp->res.jsonValue["Password"] = nullptr; 1968b2521a5SCarson Labrado } 1978b2521a5SCarson Labrado 1988b2521a5SCarson Labrado inline void handleAggregationSourceGet( 1998b2521a5SCarson Labrado App& app, const crow::Request& req, 2008b2521a5SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2018b2521a5SCarson Labrado const std::string& aggregationSourceId) 2028b2521a5SCarson Labrado { 2038b2521a5SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2048b2521a5SCarson Labrado { 2058b2521a5SCarson Labrado return; 2068b2521a5SCarson Labrado } 2078b2521a5SCarson Labrado 2088b2521a5SCarson Labrado // Query D-Bus for satellite config corresponding to the specified 2098b2521a5SCarson Labrado // AggregationSource 210*66620686SEd Tanous RedfishAggregator::getInstance().getSatelliteConfigs(std::bind_front( 2118b2521a5SCarson Labrado populateAggregationSource, aggregationSourceId, asyncResp)); 2128b2521a5SCarson Labrado } 2138b2521a5SCarson Labrado 2148b2521a5SCarson Labrado inline void handleAggregationSourceHead( 2158b2521a5SCarson Labrado App& app, const crow::Request& req, 2168b2521a5SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2178b2521a5SCarson Labrado const std::string& aggregationSourceId) 2188b2521a5SCarson Labrado { 2198b2521a5SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2208b2521a5SCarson Labrado { 2218b2521a5SCarson Labrado return; 2228b2521a5SCarson Labrado } 2238b2521a5SCarson Labrado asyncResp->res.addHeader( 2248b2521a5SCarson Labrado boost::beast::http::field::link, 2258b2521a5SCarson Labrado "</redfish/v1/JsonSchemas/AggregationService/AggregationSource.json>; rel=describedby"); 2268b2521a5SCarson Labrado 2278b2521a5SCarson Labrado // Needed to prevent unused variable error 22862598e31SEd Tanous BMCWEB_LOG_DEBUG("Added link header to response from {}", 22962598e31SEd Tanous aggregationSourceId); 2308b2521a5SCarson Labrado } 2318b2521a5SCarson Labrado 232*66620686SEd Tanous inline void handleAggregationSourceCollectionPost( 233*66620686SEd Tanous App& app, const crow::Request& req, 234*66620686SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 235*66620686SEd Tanous { 236*66620686SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 237*66620686SEd Tanous { 238*66620686SEd Tanous return; 239*66620686SEd Tanous } 240*66620686SEd Tanous std::string hostname; 241*66620686SEd Tanous if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostname)) 242*66620686SEd Tanous { 243*66620686SEd Tanous return; 244*66620686SEd Tanous } 245*66620686SEd Tanous 246*66620686SEd Tanous boost::system::result<boost::urls::url> url = 247*66620686SEd Tanous boost::urls::parse_absolute_uri(hostname); 248*66620686SEd Tanous if (!url) 249*66620686SEd Tanous { 250*66620686SEd Tanous messages::propertyValueIncorrect(asyncResp->res, hostname, "HostName"); 251*66620686SEd Tanous return; 252*66620686SEd Tanous } 253*66620686SEd Tanous url->normalize(); 254*66620686SEd Tanous if (url->scheme() != "http" && url->scheme() != "https") 255*66620686SEd Tanous { 256*66620686SEd Tanous messages::propertyValueIncorrect(asyncResp->res, hostname, "HostName"); 257*66620686SEd Tanous return; 258*66620686SEd Tanous } 259*66620686SEd Tanous crow::utility::setPortDefaults(*url); 260*66620686SEd Tanous 261*66620686SEd Tanous std::string prefix = bmcweb::getRandomIdOfLength(8); 262*66620686SEd Tanous RedfishAggregator::getInstance().currentAggregationSources.emplace( 263*66620686SEd Tanous prefix, *url); 264*66620686SEd Tanous 265*66620686SEd Tanous asyncResp->res.addHeader( 266*66620686SEd Tanous boost::beast::http::field::location, 267*66620686SEd Tanous boost::urls::format("/redfish/v1/AggregationSources/{}", prefix) 268*66620686SEd Tanous .buffer()); 269*66620686SEd Tanous 270*66620686SEd Tanous messages::created(asyncResp->res); 271*66620686SEd Tanous } 272*66620686SEd Tanous 273*66620686SEd Tanous inline void handleAggregationSourceDelete( 274*66620686SEd Tanous App& app, const crow::Request& req, 275*66620686SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 276*66620686SEd Tanous const std::string& aggregationSourceId) 277*66620686SEd Tanous { 278*66620686SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 279*66620686SEd Tanous { 280*66620686SEd Tanous return; 281*66620686SEd Tanous } 282*66620686SEd Tanous asyncResp->res.addHeader( 283*66620686SEd Tanous boost::beast::http::field::link, 284*66620686SEd Tanous "</redfish/v1/JsonSchemas/AggregationService/AggregationSource.json>; rel=describedby"); 285*66620686SEd Tanous 286*66620686SEd Tanous size_t deleted = 287*66620686SEd Tanous RedfishAggregator::getInstance().currentAggregationSources.erase( 288*66620686SEd Tanous aggregationSourceId); 289*66620686SEd Tanous if (deleted == 0) 290*66620686SEd Tanous { 291*66620686SEd Tanous messages::resourceNotFound(asyncResp->res, "AggregationSource", 292*66620686SEd Tanous aggregationSourceId); 293*66620686SEd Tanous return; 294*66620686SEd Tanous } 295*66620686SEd Tanous 296*66620686SEd Tanous messages::success(asyncResp->res); 297*66620686SEd Tanous } 298*66620686SEd Tanous 2998b2521a5SCarson Labrado inline void requestRoutesAggregationSource(App& app) 3008b2521a5SCarson Labrado { 3018b2521a5SCarson Labrado BMCWEB_ROUTE(app, 3028b2521a5SCarson Labrado "/redfish/v1/AggregationService/AggregationSources/<str>/") 3038b2521a5SCarson Labrado .privileges(redfish::privileges::getAggregationSource) 30441024f39SEd Tanous .methods(boost::beast::http::verb::head)( 30541024f39SEd Tanous std::bind_front(handleAggregationSourceHead, std::ref(app))); 30641024f39SEd Tanous 30741024f39SEd Tanous BMCWEB_ROUTE(app, 30841024f39SEd Tanous "/redfish/v1/AggregationService/AggregationSources/<str>/") 30941024f39SEd Tanous .privileges(redfish::privileges::getAggregationSource) 3105315c1b1SCarson Labrado .methods(boost::beast::http::verb::get)( 3118b2521a5SCarson Labrado std::bind_front(handleAggregationSourceGet, std::ref(app))); 312*66620686SEd Tanous 313*66620686SEd Tanous BMCWEB_ROUTE(app, 314*66620686SEd Tanous "/redfish/v1/AggregationService/AggregationSources/<str>/") 315*66620686SEd Tanous .privileges(redfish::privileges::deleteAggregationSource) 316*66620686SEd Tanous .methods(boost::beast::http::verb::delete_)( 317*66620686SEd Tanous std::bind_front(handleAggregationSourceDelete, std::ref(app))); 318*66620686SEd Tanous 319*66620686SEd Tanous BMCWEB_ROUTE(app, 320*66620686SEd Tanous "/redfish/v1/AggregationService/AggregationSources/<str>/") 321*66620686SEd Tanous .privileges(redfish::privileges::headAggregationSource) 322*66620686SEd Tanous .methods(boost::beast::http::verb::head)( 323*66620686SEd Tanous std::bind_front(handleAggregationSourceHead, std::ref(app))); 324*66620686SEd Tanous 325*66620686SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/AggregationService/AggregationSources/") 326*66620686SEd Tanous .privileges(redfish::privileges::postAggregationSourceCollection) 327*66620686SEd Tanous .methods(boost::beast::http::verb::post)(std::bind_front( 328*66620686SEd Tanous handleAggregationSourceCollectionPost, std::ref(app))); 3295315c1b1SCarson Labrado } 3305315c1b1SCarson Labrado 3316c068982SEd Tanous } // namespace redfish 332