1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "common/errno.h"
5 #include "rgw_rest_realm.h"
6 #include "rgw_rest_s3.h"
7 #include "rgw_rest_config.h"
10 #include "services/svc_zone.h"
12 #include "include/ceph_assert.h"
14 #define dout_subsys ceph_subsys_rgw
16 // reject 'period push' if we would have to fetch too many intermediate periods
17 static const uint32_t PERIOD_HISTORY_FETCH_MAX
= 64;
19 // base period op, shared between Get and Post
20 class RGWOp_Period_Base
: public RGWRESTOp
{
23 std::ostringstream error_stream
;
25 int verify_permission() override
{ return 0; }
26 void send_response() override
;
29 // reply with the period object on success
30 void RGWOp_Period_Base::send_response()
32 set_req_state_err(s
, http_ret
, error_stream
.str());
36 if (!s
->err
.message
.empty()) {
37 ldout(s
->cct
, 4) << "Request failed with " << http_ret
38 << ": " << s
->err
.message
<< dendl
;
44 encode_json("period", period
, s
->formatter
);
45 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
49 // GET /admin/realm/period
50 class RGWOp_Period_Get
: public RGWOp_Period_Base
{
52 void execute() override
;
53 int check_caps(RGWUserCaps
& caps
) override
{
54 return caps
.check_cap("zone", RGW_CAP_READ
);
56 int verify_permission() override
{
57 return check_caps(s
->user
->caps
);
59 const char* name() const override
{ return "get_period"; }
62 void RGWOp_Period_Get::execute()
64 string realm_id
, realm_name
, period_id
;
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
);
71 period
.set_id(period_id
);
72 period
.set_epoch(epoch
);
74 http_ret
= period
.init(store
->ctx(), store
->svc
.sysobj
, realm_id
, realm_name
);
76 ldout(store
->ctx(), 5) << "failed to read period" << dendl
;
79 // POST /admin/realm/period
80 class RGWOp_Period_Post
: public RGWOp_Period_Base
{
82 void execute() override
;
83 int check_caps(RGWUserCaps
& caps
) override
{
84 return caps
.check_cap("zone", RGW_CAP_WRITE
);
86 int verify_permission() override
{
87 return check_caps(s
->user
->caps
);
89 const char* name() const override
{ return "post_period"; }
92 void RGWOp_Period_Post::execute()
94 auto cct
= store
->ctx();
96 // initialize the period without reading from rados
97 period
.init(cct
, store
->svc
.sysobj
, false);
99 // decode the period from input
100 const auto max_size
= cct
->_conf
->rgw_max_put_param_size
;
102 http_ret
= rgw_rest_get_json_input(cct
, s
, period
, max_size
, &empty
);
104 lderr(cct
) << "failed to decode period" << dendl
;
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
;
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
);
122 lderr(cct
) << "failed to read current realm: "
123 << cpp_strerror(-http_ret
) << dendl
;
127 RGWPeriod current_period
;
128 http_ret
= current_period
.init(cct
, store
->svc
.sysobj
, realm
.get_id());
130 lderr(cct
) << "failed to read current period: "
131 << cpp_strerror(-http_ret
) << dendl
;
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
);
139 lderr(cct
) << "master zone failed to commit period" << dendl
;
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
152 // write the period to rados
153 http_ret
= period
.store_info(false);
155 lderr(cct
) << "failed to store period " << period
.get_id() << dendl
;
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
;
168 lderr(cct
) << "failed to set latest epoch" << dendl
;
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
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
191 // attach a copy of the period into the period history
192 auto cursor
= store
->period_history
->attach(RGWPeriod
{period
});
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
;
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
;
209 // set as current period
210 http_ret
= realm
.set_current_period(period
);
212 lderr(cct
) << "failed to update realm's current period" << dendl
;
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
);
221 // reflect the period into our local objects
222 http_ret
= period
.reflect();
224 lderr(cct
) << "failed to update local objects: "
225 << cpp_strerror(-http_ret
) << dendl
;
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
});
236 class RGWHandler_Period
: public RGWHandler_Auth_S3
{
238 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
240 RGWOp
*op_get() override
{ return new RGWOp_Period_Get
; }
241 RGWOp
*op_post() override
{ return new RGWOp_Period_Post
; }
244 class RGWRESTMgr_Period
: public RGWRESTMgr
{
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
);
255 class RGWOp_Realm_Get
: public RGWRESTOp
{
256 std::unique_ptr
<RGWRealm
> realm
;
258 int check_caps(RGWUserCaps
& caps
) override
{
259 return caps
.check_cap("zone", RGW_CAP_READ
);
261 int verify_permission() override
{
262 return check_caps(s
->user
->caps
);
264 void execute() override
;
265 void send_response() override
;
266 const char* name() const override
{ return "get_realm"; }
269 void RGWOp_Realm_Get::execute()
272 RESTArgs::get_string(s
, "id", id
, &id
);
274 RESTArgs::get_string(s
, "name", name
, &name
);
277 realm
.reset(new RGWRealm(id
, name
));
278 http_ret
= realm
->init(g_ceph_context
, store
->svc
.sysobj
);
280 lderr(store
->ctx()) << "failed to read realm id=" << id
281 << " name=" << name
<< dendl
;
284 void RGWOp_Realm_Get::send_response()
286 set_req_state_err(s
, http_ret
);
294 encode_json("realm", *realm
, s
->formatter
);
295 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
299 class RGWHandler_Realm
: public RGWHandler_Auth_S3
{
301 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
302 RGWOp
*op_get() override
{ return new RGWOp_Realm_Get
; }
305 RGWRESTMgr_Realm::RGWRESTMgr_Realm()
307 // add the /admin/realm/period resource
308 register_resource("period", new RGWRESTMgr_Period
);
312 RGWRESTMgr_Realm::get_handler(struct req_state
*,
313 const rgw::auth::StrategyRegistry
& auth_registry
,
316 return new RGWHandler_Realm(auth_registry
);