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