]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_realm.cc
add subtree-ish sources for 12.0.3
[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
9 #include "include/assert.h"
10
11 #define dout_subsys ceph_subsys_rgw
12
13 // reject 'period push' if we would have to fetch too many intermediate periods
14 static const uint32_t PERIOD_HISTORY_FETCH_MAX = 64;
15
16 // base period op, shared between Get and Post
17 class RGWOp_Period_Base : public RGWRESTOp {
18 protected:
19 RGWPeriod period;
20 std::ostringstream error_stream;
21 public:
22 int verify_permission() override { return 0; }
23 void send_response() override;
24 };
25
26 // reply with the period object on success
27 void RGWOp_Period_Base::send_response()
28 {
29 s->err.message = error_stream.str();
30
31 set_req_state_err(s, http_ret);
32 dump_errno(s);
33
34 if (http_ret < 0) {
35 if (!s->err.message.empty()) {
36 ldout(s->cct, 4) << "Request failed with " << http_ret
37 << ": " << s->err.message << dendl;
38 }
39 end_header(s);
40 return;
41 }
42
43 encode_json("period", period, s->formatter);
44 end_header(s, NULL, "application/json", s->formatter->get_len());
45 flusher.flush();
46 }
47
48 // GET /admin/realm/period
49 class RGWOp_Period_Get : public RGWOp_Period_Base {
50 public:
51 void execute() override;
52 const string name() override { return "get_period"; }
53 };
54
55 void RGWOp_Period_Get::execute()
56 {
57 string realm_id, realm_name, period_id;
58 epoch_t epoch = 0;
59 RESTArgs::get_string(s, "realm_id", realm_id, &realm_id);
60 RESTArgs::get_string(s, "realm_name", realm_name, &realm_name);
61 RESTArgs::get_string(s, "period_id", period_id, &period_id);
62 RESTArgs::get_uint32(s, "epoch", 0, &epoch);
63
64 period.set_id(period_id);
65 period.set_epoch(epoch);
66
67 http_ret = period.init(store->ctx(), store, realm_id, realm_name);
68 if (http_ret < 0)
69 ldout(store->ctx(), 5) << "failed to read period" << dendl;
70 }
71
72 // POST /admin/realm/period
73 class RGWOp_Period_Post : public RGWOp_Period_Base {
74 public:
75 void execute() override;
76 const string name() override { return "post_period"; }
77 };
78
79 void RGWOp_Period_Post::execute()
80 {
81 auto cct = store->ctx();
82
83 // initialize the period without reading from rados
84 period.init(cct, store, false);
85
86 // decode the period from input
87 const auto max_size = cct->_conf->rgw_max_put_param_size;
88 bool empty;
89 http_ret = rgw_rest_get_json_input(cct, s, period, max_size, &empty);
90 if (http_ret < 0) {
91 lderr(cct) << "failed to decode period" << dendl;
92 return;
93 }
94
95 // require period.realm_id to match our realm
96 if (period.get_realm() != store->realm.get_id()) {
97 error_stream << "period with realm id " << period.get_realm()
98 << " doesn't match current realm " << store->realm.get_id() << std::endl;
99 http_ret = -EINVAL;
100 return;
101 }
102
103 // load the realm and current period from rados; there may be a more recent
104 // period that we haven't restarted with yet. we also don't want to modify
105 // the objects in use by RGWRados
106 RGWRealm realm(period.get_realm());
107 http_ret = realm.init(cct, store);
108 if (http_ret < 0) {
109 lderr(cct) << "failed to read current realm: "
110 << cpp_strerror(-http_ret) << dendl;
111 return;
112 }
113
114 RGWPeriod current_period;
115 http_ret = current_period.init(cct, store, realm.get_id());
116 if (http_ret < 0) {
117 lderr(cct) << "failed to read current period: "
118 << cpp_strerror(-http_ret) << dendl;
119 return;
120 }
121
122 // if period id is empty, handle as 'period commit'
123 if (period.get_id().empty()) {
124 http_ret = period.commit(realm, current_period, error_stream);
125 if (http_ret < 0) {
126 lderr(cct) << "master zone failed to commit period" << dendl;
127 }
128 return;
129 }
130
131 // if it's not period commit, nobody is allowed to push to the master zone
132 if (period.get_master_zone() == store->get_zone_params().get_id()) {
133 ldout(cct, 10) << "master zone rejecting period id="
134 << period.get_id() << " epoch=" << period.get_epoch() << dendl;
135 http_ret = -EINVAL; // XXX: error code
136 return;
137 }
138
139 // write the period to rados
140 http_ret = period.store_info(false);
141 if (http_ret < 0) {
142 lderr(cct) << "failed to store period " << period.get_id() << dendl;
143 return;
144 }
145
146 // decide whether we can set_current_period() or set_latest_epoch()
147 if (period.get_id() != current_period.get_id()) {
148 auto current_epoch = current_period.get_realm_epoch();
149 // discard periods in the past
150 if (period.get_realm_epoch() < current_epoch) {
151 ldout(cct, 10) << "discarding period " << period.get_id()
152 << " with realm epoch " << period.get_realm_epoch()
153 << " older than current epoch " << current_epoch << dendl;
154 // return success to ack that we have this period
155 return;
156 }
157 // discard periods too far in the future
158 if (period.get_realm_epoch() > current_epoch + PERIOD_HISTORY_FETCH_MAX) {
159 lderr(cct) << "discarding period " << period.get_id()
160 << " with realm epoch " << period.get_realm_epoch() << " too far in "
161 "the future from current epoch " << current_epoch << dendl;
162 http_ret = -ENOENT; // XXX: error code
163 return;
164 }
165 // attach a copy of the period into the period history
166 auto cursor = store->period_history->attach(RGWPeriod{period});
167 if (!cursor) {
168 // we're missing some history between the new period and current_period
169 http_ret = cursor.get_error();
170 lderr(cct) << "failed to collect the periods between current period "
171 << current_period.get_id() << " (realm epoch " << current_epoch
172 << ") and the new period " << period.get_id()
173 << " (realm epoch " << period.get_realm_epoch()
174 << "): " << cpp_strerror(-http_ret) << dendl;
175 return;
176 }
177 if (cursor.has_next()) {
178 // don't switch if we have a newer period in our history
179 ldout(cct, 4) << "attached period " << period.get_id()
180 << " to history, but the history contains newer periods" << dendl;
181 return;
182 }
183 // set as current period
184 http_ret = realm.set_current_period(period);
185 if (http_ret < 0) {
186 lderr(cct) << "failed to update realm's current period" << dendl;
187 return;
188 }
189 ldout(cct, 4) << "period " << period.get_id()
190 << " is newer than current period " << current_period.get_id()
191 << ", updating realm's current period and notifying zone" << dendl;
192 realm.notify_new_period(period);
193 return;
194 }
195
196 if (period.get_epoch() <= current_period.get_epoch()) {
197 lderr(cct) << "period epoch " << period.get_epoch() << " is not newer "
198 "than current epoch " << current_period.get_epoch()
199 << ", discarding update" << dendl;
200 return;
201 }
202 // set as latest epoch
203 http_ret = period.set_latest_epoch(period.get_epoch());
204 if (http_ret < 0) {
205 lderr(cct) << "failed to set latest epoch" << dendl;
206 return;
207 }
208 // reflect the period into our local objects
209 http_ret = period.reflect();
210 if (http_ret < 0) {
211 lderr(cct) << "failed to update local objects: "
212 << cpp_strerror(-http_ret) << dendl;
213 return;
214 }
215 ldout(cct, 4) << "period epoch " << period.get_epoch()
216 << " is newer than current epoch " << current_period.get_epoch()
217 << ", updating period's latest epoch and notifying zone" << dendl;
218 realm.notify_new_period(period);
219 // update the period history
220 store->period_history->insert(RGWPeriod{period});
221 }
222
223 class RGWHandler_Period : public RGWHandler_Auth_S3 {
224 protected:
225 using RGWHandler_Auth_S3::RGWHandler_Auth_S3;
226
227 RGWOp *op_get() override { return new RGWOp_Period_Get; }
228 RGWOp *op_post() override { return new RGWOp_Period_Post; }
229 };
230
231 class RGWRESTMgr_Period : public RGWRESTMgr {
232 public:
233 RGWHandler_REST* get_handler(struct req_state*,
234 const rgw::auth::StrategyRegistry& auth_registry,
235 const std::string&) override {
236 return new RGWHandler_Period(auth_registry);
237 }
238 };
239
240
241 // GET /admin/realm
242 class RGWOp_Realm_Get : public RGWRESTOp {
243 std::unique_ptr<RGWRealm> realm;
244 public:
245 int verify_permission() override { return 0; }
246 void execute() override;
247 void send_response() override;
248 const string name() override { return "get_realm"; }
249 };
250
251 void RGWOp_Realm_Get::execute()
252 {
253 string id;
254 RESTArgs::get_string(s, "id", id, &id);
255 string name;
256 RESTArgs::get_string(s, "name", name, &name);
257
258 // read realm
259 realm.reset(new RGWRealm(id, name));
260 http_ret = realm->init(g_ceph_context, store);
261 if (http_ret < 0)
262 lderr(store->ctx()) << "failed to read realm id=" << id
263 << " name=" << name << dendl;
264 }
265
266 void RGWOp_Realm_Get::send_response()
267 {
268 set_req_state_err(s, http_ret);
269 dump_errno(s);
270
271 if (http_ret < 0) {
272 end_header(s);
273 return;
274 }
275
276 encode_json("realm", *realm, s->formatter);
277 end_header(s, NULL, "application/json", s->formatter->get_len());
278 flusher.flush();
279 }
280
281 class RGWHandler_Realm : public RGWHandler_Auth_S3 {
282 protected:
283 using RGWHandler_Auth_S3::RGWHandler_Auth_S3;
284 RGWOp *op_get() override { return new RGWOp_Realm_Get; }
285 };
286
287 RGWRESTMgr_Realm::RGWRESTMgr_Realm()
288 {
289 // add the /admin/realm/period resource
290 register_resource("period", new RGWRESTMgr_Period);
291 }
292
293 RGWHandler_REST*
294 RGWRESTMgr_Realm::get_handler(struct req_state*,
295 const rgw::auth::StrategyRegistry& auth_registry,
296 const std::string&)
297 {
298 return new RGWHandler_Realm(auth_registry);
299 }