]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/driver/rados/rgw_rest_realm.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rgw / driver / rados / rgw_rest_realm.cc
CommitLineData
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
18using namespace std;
19
7c673cae
FG
20// reject 'period push' if we would have to fetch too many intermediate periods
21static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
22
23// base period op, shared between Get and Post
24class 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
34void 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
54class 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 66void 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
84class 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 97void 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
243class 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
251class 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
263class RGWOp_Realm_Get : public RGWRESTOp {
264 std::unique_ptr<RGWRealm> realm;
265public:
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 277void 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
292void 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
308class RGWOp_Realm_List : public RGWRESTOp {
309 std::string default_id;
310 std::list<std::string> realms;
311public:
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 323void 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
335void 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
353class RGWHandler_Realm : public RGWHandler_Auth_S3 {
354protected:
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
363RGWRESTMgr_Realm::RGWRESTMgr_Realm()
364{
365 // add the /admin/realm/period resource
366 register_resource("period", new RGWRESTMgr_Period);
367}
368
369RGWHandler_REST*
1e59de90
TL
370RGWRESTMgr_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}