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