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