]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_realm.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / rgw / rgw_rest_realm.cc
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"
8 #include "rgw_zone.h"
9
10 #include "services/svc_zone.h"
11
12 #include "include/ceph_assert.h"
13
14 #define dout_subsys ceph_subsys_rgw
15
16 // reject 'period push' if we would have to fetch too many intermediate periods
17 static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
18
19 // base period op, shared between Get and Post
20 class 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
30 void RGWOp_Period_Base::send_response()
31 {
32 set_req_state_err(s, http_ret, error_stream.str());
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
50 class RGWOp_Period_Get : public RGWOp_Period_Base {
51 public:
52 void execute() override;
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"; }
60 };
61
62 void 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
74 http_ret = period.init(store->ctx(), store->svc.sysobj, realm_id, realm_name);
75 if (http_ret < 0)
76 ldout(store->ctx(), 5) << "failed to read period" << dendl;
77 }
78
79 // POST /admin/realm/period
80 class RGWOp_Period_Post : public RGWOp_Period_Base {
81 public:
82 void execute() override;
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"; }
90 };
91
92 void RGWOp_Period_Post::execute()
93 {
94 auto cct = store->ctx();
95
96 // initialize the period without reading from rados
97 period.init(cct, store->svc.sysobj, false);
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
109 if (period.get_realm() != store->svc.zone->get_realm().get_id()) {
110 error_stream << "period with realm id " << period.get_realm()
111 << " doesn't match current realm " << store->svc.zone->get_realm().get_id() << std::endl;
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());
120 http_ret = realm.init(cct, store->svc.sysobj);
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;
128 http_ret = current_period.init(cct, store->svc.sysobj, realm.get_id());
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()) {
137 http_ret = period.commit(store, realm, current_period, error_stream);
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
145 if (period.get_master_zone() == store->svc.zone->get_zone_params().get_id()) {
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 }
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 }
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 }
221 // reflect the period into our local objects
222 http_ret = period.reflect();
223 if (http_ret < 0) {
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
236 class 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
244 class 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
255 class RGWOp_Realm_Get : public RGWRESTOp {
256 std::unique_ptr<RGWRealm> realm;
257 public:
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 }
264 void execute() override;
265 void send_response() override;
266 const char* name() const override { return "get_realm"; }
267 };
268
269 void 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));
278 http_ret = realm->init(g_ceph_context, store->svc.sysobj);
279 if (http_ret < 0)
280 lderr(store->ctx()) << "failed to read realm id=" << id
281 << " name=" << name << dendl;
282 }
283
284 void 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
299 class RGWHandler_Realm : public RGWHandler_Auth_S3 {
300 protected:
301 using RGWHandler_Auth_S3::RGWHandler_Auth_S3;
302 RGWOp *op_get() override { return new RGWOp_Realm_Get; }
303 };
304
305 RGWRESTMgr_Realm::RGWRESTMgr_Realm()
306 {
307 // add the /admin/realm/period resource
308 register_resource("period", new RGWRESTMgr_Period);
309 }
310
311 RGWHandler_REST*
312 RGWRESTMgr_Realm::get_handler(struct req_state*,
313 const rgw::auth::StrategyRegistry& auth_registry,
314 const std::string&)
315 {
316 return new RGWHandler_Realm(auth_registry);
317 }