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
handleAggregationServiceHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
handleAggregationServiceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
requestRoutesAggregationService(App & app)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
populateAggregationSourceCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::unordered_map<std::string,boost::urls::url> & satelliteInfo)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
handleAggregationSourceCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
handleAggregationSourceCollectionHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
requestRoutesAggregationSourceCollection(App & app)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
populateAggregationSource(const std::string & aggregationSourceId,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::unordered_map<std::string,boost::urls::url> & satelliteInfo)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
handleAggregationSourceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & aggregationSourceId)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
handleAggregationSourceHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & aggregationSourceId)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
requestRoutesAggregationSource(App & app)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