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