]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest_realm.cc
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / rgw / rgw_rest_realm.cc
CommitLineData
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
17static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
18
19// base period op, shared between Get and Post
20class 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
30void 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
50class 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
62void 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
80class 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
92void 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
236class 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
244class 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
255class RGWOp_Realm_Get : public RGWRESTOp {
256 std::unique_ptr<RGWRealm> realm;
257public:
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
269void 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
284void 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
300class RGWOp_Realm_List : public RGWRESTOp {
301 std::string default_id;
302 std::list<std::string> realms;
303public:
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
315void 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
327void 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
345class RGWHandler_Realm : public RGWHandler_Auth_S3 {
346protected:
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
355RGWRESTMgr_Realm::RGWRESTMgr_Realm()
356{
357 // add the /admin/realm/period resource
358 register_resource("period", new RGWRESTMgr_Period);
359}
360
361RGWHandler_REST*
362RGWRESTMgr_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}