]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest_realm.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / rgw / 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
18// reject 'period push' if we would have to fetch too many intermediate periods
19static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
20
21// base period op, shared between Get and Post
22class 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
32void 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
52class 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 64void 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
82class 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 94void 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
240class 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
248class 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
260class RGWOp_Realm_Get : public RGWRESTOp {
261 std::unique_ptr<RGWRealm> realm;
262public:
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 274void 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
289void 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
305class RGWOp_Realm_List : public RGWRESTOp {
306 std::string default_id;
307 std::list<std::string> realms;
308public:
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 320void 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
332void 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
350class RGWHandler_Realm : public RGWHandler_Auth_S3 {
351protected:
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
360RGWRESTMgr_Realm::RGWRESTMgr_Realm()
361{
362 // add the /admin/realm/period resource
363 register_resource("period", new RGWRESTMgr_Period);
364}
365
366RGWHandler_REST*
f67539c2
TL
367RGWRESTMgr_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}