]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "common/errno.h" | |
5 | #include "rgw_rest_realm.h" | |
6 | #include "rgw_rest_s3.h" | |
7 | #include "rgw_rest_config.h" | |
11fdf7f2 | 8 | #include "rgw_zone.h" |
7c673cae | 9 | |
11fdf7f2 TL |
10 | #include "services/svc_zone.h" |
11 | ||
12 | #include "include/ceph_assert.h" | |
7c673cae FG |
13 | |
14 | #define dout_subsys ceph_subsys_rgw | |
15 | ||
16 | // reject 'period push' if we would have to fetch too many intermediate periods | |
17 | static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64; | |
18 | ||
19 | // base period op, shared between Get and Post | |
20 | class RGWOp_Period_Base : public RGWRESTOp { | |
21 | protected: | |
22 | RGWPeriod period; | |
23 | std::ostringstream error_stream; | |
24 | public: | |
25 | int verify_permission() override { return 0; } | |
26 | void send_response() override; | |
27 | }; | |
28 | ||
29 | // reply with the period object on success | |
30 | void RGWOp_Period_Base::send_response() | |
31 | { | |
31f18b77 | 32 | set_req_state_err(s, http_ret, error_stream.str()); |
7c673cae FG |
33 | dump_errno(s); |
34 | ||
35 | if (http_ret < 0) { | |
36 | if (!s->err.message.empty()) { | |
37 | ldout(s->cct, 4) << "Request failed with " << http_ret | |
38 | << ": " << s->err.message << dendl; | |
39 | } | |
40 | end_header(s); | |
41 | return; | |
42 | } | |
43 | ||
44 | encode_json("period", period, s->formatter); | |
45 | end_header(s, NULL, "application/json", s->formatter->get_len()); | |
46 | flusher.flush(); | |
47 | } | |
48 | ||
49 | // GET /admin/realm/period | |
50 | class RGWOp_Period_Get : public RGWOp_Period_Base { | |
51 | public: | |
52 | void execute() override; | |
11fdf7f2 TL |
53 | int check_caps(RGWUserCaps& caps) override { |
54 | return caps.check_cap("zone", RGW_CAP_READ); | |
55 | } | |
56 | int verify_permission() override { | |
57 | return check_caps(s->user->caps); | |
58 | } | |
59 | const char* name() const override { return "get_period"; } | |
7c673cae FG |
60 | }; |
61 | ||
62 | void RGWOp_Period_Get::execute() | |
63 | { | |
64 | string realm_id, realm_name, period_id; | |
65 | epoch_t epoch = 0; | |
66 | RESTArgs::get_string(s, "realm_id", realm_id, &realm_id); | |
67 | RESTArgs::get_string(s, "realm_name", realm_name, &realm_name); | |
68 | RESTArgs::get_string(s, "period_id", period_id, &period_id); | |
69 | RESTArgs::get_uint32(s, "epoch", 0, &epoch); | |
70 | ||
71 | period.set_id(period_id); | |
72 | period.set_epoch(epoch); | |
73 | ||
11fdf7f2 | 74 | http_ret = period.init(store->ctx(), store->svc.sysobj, realm_id, realm_name); |
7c673cae FG |
75 | if (http_ret < 0) |
76 | ldout(store->ctx(), 5) << "failed to read period" << dendl; | |
77 | } | |
78 | ||
79 | // POST /admin/realm/period | |
80 | class RGWOp_Period_Post : public RGWOp_Period_Base { | |
81 | public: | |
82 | void execute() override; | |
11fdf7f2 TL |
83 | int check_caps(RGWUserCaps& caps) override { |
84 | return caps.check_cap("zone", RGW_CAP_WRITE); | |
85 | } | |
86 | int verify_permission() override { | |
87 | return check_caps(s->user->caps); | |
88 | } | |
89 | const char* name() const override { return "post_period"; } | |
7c673cae FG |
90 | }; |
91 | ||
92 | void RGWOp_Period_Post::execute() | |
93 | { | |
94 | auto cct = store->ctx(); | |
95 | ||
96 | // initialize the period without reading from rados | |
11fdf7f2 | 97 | period.init(cct, store->svc.sysobj, false); |
7c673cae FG |
98 | |
99 | // decode the period from input | |
100 | const auto max_size = cct->_conf->rgw_max_put_param_size; | |
101 | bool empty; | |
102 | http_ret = rgw_rest_get_json_input(cct, s, period, max_size, &empty); | |
103 | if (http_ret < 0) { | |
104 | lderr(cct) << "failed to decode period" << dendl; | |
105 | return; | |
106 | } | |
107 | ||
108 | // require period.realm_id to match our realm | |
11fdf7f2 | 109 | if (period.get_realm() != store->svc.zone->get_realm().get_id()) { |
7c673cae | 110 | error_stream << "period with realm id " << period.get_realm() |
11fdf7f2 | 111 | << " doesn't match current realm " << store->svc.zone->get_realm().get_id() << std::endl; |
7c673cae FG |
112 | http_ret = -EINVAL; |
113 | return; | |
114 | } | |
115 | ||
116 | // load the realm and current period from rados; there may be a more recent | |
117 | // period that we haven't restarted with yet. we also don't want to modify | |
118 | // the objects in use by RGWRados | |
119 | RGWRealm realm(period.get_realm()); | |
11fdf7f2 | 120 | http_ret = realm.init(cct, store->svc.sysobj); |
7c673cae FG |
121 | if (http_ret < 0) { |
122 | lderr(cct) << "failed to read current realm: " | |
123 | << cpp_strerror(-http_ret) << dendl; | |
124 | return; | |
125 | } | |
126 | ||
127 | RGWPeriod current_period; | |
11fdf7f2 | 128 | http_ret = current_period.init(cct, store->svc.sysobj, realm.get_id()); |
7c673cae FG |
129 | if (http_ret < 0) { |
130 | lderr(cct) << "failed to read current period: " | |
131 | << cpp_strerror(-http_ret) << dendl; | |
132 | return; | |
133 | } | |
134 | ||
135 | // if period id is empty, handle as 'period commit' | |
136 | if (period.get_id().empty()) { | |
11fdf7f2 | 137 | http_ret = period.commit(store, realm, current_period, error_stream); |
7c673cae FG |
138 | if (http_ret < 0) { |
139 | lderr(cct) << "master zone failed to commit period" << dendl; | |
140 | } | |
141 | return; | |
142 | } | |
143 | ||
144 | // if it's not period commit, nobody is allowed to push to the master zone | |
11fdf7f2 | 145 | if (period.get_master_zone() == store->svc.zone->get_zone_params().get_id()) { |
7c673cae FG |
146 | ldout(cct, 10) << "master zone rejecting period id=" |
147 | << period.get_id() << " epoch=" << period.get_epoch() << dendl; | |
148 | http_ret = -EINVAL; // XXX: error code | |
149 | return; | |
150 | } | |
151 | ||
152 | // write the period to rados | |
153 | http_ret = period.store_info(false); | |
154 | if (http_ret < 0) { | |
155 | lderr(cct) << "failed to store period " << period.get_id() << dendl; | |
156 | return; | |
157 | } | |
224ce89b WB |
158 | // set as latest epoch |
159 | http_ret = period.update_latest_epoch(period.get_epoch()); | |
160 | if (http_ret == -EEXIST) { | |
161 | // already have this epoch (or a more recent one) | |
162 | ldout(cct, 4) << "already have epoch >= " << period.get_epoch() | |
163 | << " for period " << period.get_id() << dendl; | |
164 | http_ret = 0; | |
165 | return; | |
166 | } | |
167 | if (http_ret < 0) { | |
168 | lderr(cct) << "failed to set latest epoch" << dendl; | |
169 | return; | |
170 | } | |
7c673cae FG |
171 | |
172 | // decide whether we can set_current_period() or set_latest_epoch() | |
173 | if (period.get_id() != current_period.get_id()) { | |
174 | auto current_epoch = current_period.get_realm_epoch(); | |
175 | // discard periods in the past | |
176 | if (period.get_realm_epoch() < current_epoch) { | |
177 | ldout(cct, 10) << "discarding period " << period.get_id() | |
178 | << " with realm epoch " << period.get_realm_epoch() | |
179 | << " older than current epoch " << current_epoch << dendl; | |
180 | // return success to ack that we have this period | |
181 | return; | |
182 | } | |
183 | // discard periods too far in the future | |
184 | if (period.get_realm_epoch() > current_epoch + PERIOD_HISTORY_FETCH_MAX) { | |
185 | lderr(cct) << "discarding period " << period.get_id() | |
186 | << " with realm epoch " << period.get_realm_epoch() << " too far in " | |
187 | "the future from current epoch " << current_epoch << dendl; | |
188 | http_ret = -ENOENT; // XXX: error code | |
189 | return; | |
190 | } | |
191 | // attach a copy of the period into the period history | |
192 | auto cursor = store->period_history->attach(RGWPeriod{period}); | |
193 | if (!cursor) { | |
194 | // we're missing some history between the new period and current_period | |
195 | http_ret = cursor.get_error(); | |
196 | lderr(cct) << "failed to collect the periods between current period " | |
197 | << current_period.get_id() << " (realm epoch " << current_epoch | |
198 | << ") and the new period " << period.get_id() | |
199 | << " (realm epoch " << period.get_realm_epoch() | |
200 | << "): " << cpp_strerror(-http_ret) << dendl; | |
201 | return; | |
202 | } | |
203 | if (cursor.has_next()) { | |
204 | // don't switch if we have a newer period in our history | |
205 | ldout(cct, 4) << "attached period " << period.get_id() | |
206 | << " to history, but the history contains newer periods" << dendl; | |
207 | return; | |
208 | } | |
209 | // set as current period | |
210 | http_ret = realm.set_current_period(period); | |
211 | if (http_ret < 0) { | |
212 | lderr(cct) << "failed to update realm's current period" << dendl; | |
213 | return; | |
214 | } | |
215 | ldout(cct, 4) << "period " << period.get_id() | |
216 | << " is newer than current period " << current_period.get_id() | |
217 | << ", updating realm's current period and notifying zone" << dendl; | |
218 | realm.notify_new_period(period); | |
219 | return; | |
220 | } | |
7c673cae | 221 | // reflect the period into our local objects |
224ce89b WB |
222 | http_ret = period.reflect(); |
223 | if (http_ret < 0) { | |
7c673cae FG |
224 | lderr(cct) << "failed to update local objects: " |
225 | << cpp_strerror(-http_ret) << dendl; | |
226 | return; | |
227 | } | |
228 | ldout(cct, 4) << "period epoch " << period.get_epoch() | |
229 | << " is newer than current epoch " << current_period.get_epoch() | |
230 | << ", updating period's latest epoch and notifying zone" << dendl; | |
231 | realm.notify_new_period(period); | |
232 | // update the period history | |
233 | store->period_history->insert(RGWPeriod{period}); | |
234 | } | |
235 | ||
236 | class RGWHandler_Period : public RGWHandler_Auth_S3 { | |
237 | protected: | |
238 | using RGWHandler_Auth_S3::RGWHandler_Auth_S3; | |
239 | ||
240 | RGWOp *op_get() override { return new RGWOp_Period_Get; } | |
241 | RGWOp *op_post() override { return new RGWOp_Period_Post; } | |
242 | }; | |
243 | ||
244 | class RGWRESTMgr_Period : public RGWRESTMgr { | |
245 | public: | |
246 | RGWHandler_REST* get_handler(struct req_state*, | |
247 | const rgw::auth::StrategyRegistry& auth_registry, | |
248 | const std::string&) override { | |
249 | return new RGWHandler_Period(auth_registry); | |
250 | } | |
251 | }; | |
252 | ||
253 | ||
254 | // GET /admin/realm | |
255 | class RGWOp_Realm_Get : public RGWRESTOp { | |
256 | std::unique_ptr<RGWRealm> realm; | |
257 | public: | |
11fdf7f2 TL |
258 | int check_caps(RGWUserCaps& caps) override { |
259 | return caps.check_cap("zone", RGW_CAP_READ); | |
260 | } | |
261 | int verify_permission() override { | |
262 | return check_caps(s->user->caps); | |
263 | } | |
7c673cae FG |
264 | void execute() override; |
265 | void send_response() override; | |
11fdf7f2 | 266 | const char* name() const override { return "get_realm"; } |
7c673cae FG |
267 | }; |
268 | ||
269 | void RGWOp_Realm_Get::execute() | |
270 | { | |
271 | string id; | |
272 | RESTArgs::get_string(s, "id", id, &id); | |
273 | string name; | |
274 | RESTArgs::get_string(s, "name", name, &name); | |
275 | ||
276 | // read realm | |
277 | realm.reset(new RGWRealm(id, name)); | |
11fdf7f2 | 278 | http_ret = realm->init(g_ceph_context, store->svc.sysobj); |
7c673cae FG |
279 | if (http_ret < 0) |
280 | lderr(store->ctx()) << "failed to read realm id=" << id | |
281 | << " name=" << name << dendl; | |
282 | } | |
283 | ||
284 | void RGWOp_Realm_Get::send_response() | |
285 | { | |
286 | set_req_state_err(s, http_ret); | |
287 | dump_errno(s); | |
288 | ||
289 | if (http_ret < 0) { | |
290 | end_header(s); | |
291 | return; | |
292 | } | |
293 | ||
294 | encode_json("realm", *realm, s->formatter); | |
295 | end_header(s, NULL, "application/json", s->formatter->get_len()); | |
296 | flusher.flush(); | |
297 | } | |
298 | ||
494da23a TL |
299 | // GET /admin/realm?list |
300 | class RGWOp_Realm_List : public RGWRESTOp { | |
301 | std::string default_id; | |
302 | std::list<std::string> realms; | |
303 | public: | |
304 | int check_caps(RGWUserCaps& caps) override { | |
305 | return caps.check_cap("zone", RGW_CAP_READ); | |
306 | } | |
307 | int verify_permission() override { | |
308 | return check_caps(s->user->caps); | |
309 | } | |
310 | void execute() override; | |
311 | void send_response() override; | |
312 | const char* name() const override { return "list_realms"; } | |
313 | }; | |
314 | ||
315 | void RGWOp_Realm_List::execute() | |
316 | { | |
317 | { | |
318 | // read default realm | |
319 | RGWRealm realm(store->ctx(), store->svc.sysobj); | |
320 | [[maybe_unused]] int ret = realm.read_default_id(default_id); | |
321 | } | |
322 | http_ret = store->svc.zone->list_realms(realms); | |
323 | if (http_ret < 0) | |
324 | lderr(store->ctx()) << "failed to list realms" << dendl; | |
325 | } | |
326 | ||
327 | void RGWOp_Realm_List::send_response() | |
328 | { | |
329 | set_req_state_err(s, http_ret); | |
330 | dump_errno(s); | |
331 | ||
332 | if (http_ret < 0) { | |
333 | end_header(s); | |
334 | return; | |
335 | } | |
336 | ||
337 | s->formatter->open_object_section("realms_list"); | |
338 | encode_json("default_info", default_id, s->formatter); | |
339 | encode_json("realms", realms, s->formatter); | |
340 | s->formatter->close_section(); | |
341 | end_header(s, NULL, "application/json", s->formatter->get_len()); | |
342 | flusher.flush(); | |
343 | } | |
344 | ||
7c673cae FG |
345 | class RGWHandler_Realm : public RGWHandler_Auth_S3 { |
346 | protected: | |
347 | using RGWHandler_Auth_S3::RGWHandler_Auth_S3; | |
494da23a TL |
348 | RGWOp *op_get() override { |
349 | if (s->info.args.sub_resource_exists("list")) | |
350 | return new RGWOp_Realm_List; | |
351 | return new RGWOp_Realm_Get; | |
352 | } | |
7c673cae FG |
353 | }; |
354 | ||
355 | RGWRESTMgr_Realm::RGWRESTMgr_Realm() | |
356 | { | |
357 | // add the /admin/realm/period resource | |
358 | register_resource("period", new RGWRESTMgr_Period); | |
359 | } | |
360 | ||
361 | RGWHandler_REST* | |
362 | RGWRESTMgr_Realm::get_handler(struct req_state*, | |
363 | const rgw::auth::StrategyRegistry& auth_registry, | |
364 | const std::string&) | |
365 | { | |
366 | return new RGWHandler_Realm(auth_registry); | |
367 | } |