]>
Commit | Line | Data |
---|---|---|
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 | |
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 | { | |
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 | |
47 | class RGWOp_Period_Get : public RGWOp_Period_Base { | |
48 | public: | |
49 | void execute() override; | |
50 | const string name() override { return "get_period"; } | |
51 | }; | |
52 | ||
53 | void 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 | |
71 | class RGWOp_Period_Post : public RGWOp_Period_Base { | |
72 | public: | |
73 | void execute() override; | |
74 | const string name() override { return "post_period"; } | |
75 | }; | |
76 | ||
77 | void 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 | ||
221 | class 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 | ||
229 | class 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 | |
240 | class RGWOp_Realm_Get : public RGWRESTOp { | |
241 | std::unique_ptr<RGWRealm> realm; | |
242 | public: | |
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 | ||
249 | void 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 | ||
264 | void 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 | ||
279 | class RGWHandler_Realm : public RGWHandler_Auth_S3 { | |
280 | protected: | |
281 | using RGWHandler_Auth_S3::RGWHandler_Auth_S3; | |
282 | RGWOp *op_get() override { return new RGWOp_Realm_Get; } | |
283 | }; | |
284 | ||
285 | RGWRESTMgr_Realm::RGWRESTMgr_Realm() | |
286 | { | |
287 | // add the /admin/realm/period resource | |
288 | register_resource("period", new RGWRESTMgr_Period); | |
289 | } | |
290 | ||
291 | RGWHandler_REST* | |
292 | RGWRESTMgr_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 | } |