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"
9 #include "include/assert.h"
11 #define dout_subsys ceph_subsys_rgw
13 // reject 'period push' if we would have to fetch too many intermediate periods
14 static const uint32_t PERIOD_HISTORY_FETCH_MAX
= 64;
16 // base period op, shared between Get and Post
17 class RGWOp_Period_Base
: public RGWRESTOp
{
20 std::ostringstream error_stream
;
22 int verify_permission() override
{ return 0; }
23 void send_response() override
;
26 // reply with the period object on success
27 void RGWOp_Period_Base::send_response()
29 set_req_state_err(s
, http_ret
, error_stream
.str());
33 if (!s
->err
.message
.empty()) {
34 ldout(s
->cct
, 4) << "Request failed with " << http_ret
35 << ": " << s
->err
.message
<< dendl
;
41 encode_json("period", period
, s
->formatter
);
42 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
46 // GET /admin/realm/period
47 class RGWOp_Period_Get
: public RGWOp_Period_Base
{
49 void execute() override
;
50 const string
name() override
{ return "get_period"; }
53 void RGWOp_Period_Get::execute()
55 string realm_id
, realm_name
, period_id
;
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
);
62 period
.set_id(period_id
);
63 period
.set_epoch(epoch
);
65 http_ret
= period
.init(store
->ctx(), store
, realm_id
, realm_name
);
67 ldout(store
->ctx(), 5) << "failed to read period" << dendl
;
70 // POST /admin/realm/period
71 class RGWOp_Period_Post
: public RGWOp_Period_Base
{
73 void execute() override
;
74 const string
name() override
{ return "post_period"; }
77 void RGWOp_Period_Post::execute()
79 auto cct
= store
->ctx();
81 // initialize the period without reading from rados
82 period
.init(cct
, store
, false);
84 // decode the period from input
85 const auto max_size
= cct
->_conf
->rgw_max_put_param_size
;
87 http_ret
= rgw_rest_get_json_input(cct
, s
, period
, max_size
, &empty
);
89 lderr(cct
) << "failed to decode period" << dendl
;
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
;
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
);
107 lderr(cct
) << "failed to read current realm: "
108 << cpp_strerror(-http_ret
) << dendl
;
112 RGWPeriod current_period
;
113 http_ret
= current_period
.init(cct
, store
, realm
.get_id());
115 lderr(cct
) << "failed to read current period: "
116 << cpp_strerror(-http_ret
) << dendl
;
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
);
124 lderr(cct
) << "master zone failed to commit period" << dendl
;
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
137 // write the period to rados
138 http_ret
= period
.store_info(false);
140 lderr(cct
) << "failed to store period " << period
.get_id() << dendl
;
144 // decide whether we can set_current_period() or set_latest_epoch()
145 if (period
.get_id() != current_period
.get_id()) {
146 auto current_epoch
= current_period
.get_realm_epoch();
147 // discard periods in the past
148 if (period
.get_realm_epoch() < current_epoch
) {
149 ldout(cct
, 10) << "discarding period " << period
.get_id()
150 << " with realm epoch " << period
.get_realm_epoch()
151 << " older than current epoch " << current_epoch
<< dendl
;
152 // return success to ack that we have this period
155 // discard periods too far in the future
156 if (period
.get_realm_epoch() > current_epoch
+ PERIOD_HISTORY_FETCH_MAX
) {
157 lderr(cct
) << "discarding period " << period
.get_id()
158 << " with realm epoch " << period
.get_realm_epoch() << " too far in "
159 "the future from current epoch " << current_epoch
<< dendl
;
160 http_ret
= -ENOENT
; // XXX: error code
163 // attach a copy of the period into the period history
164 auto cursor
= store
->period_history
->attach(RGWPeriod
{period
});
166 // we're missing some history between the new period and current_period
167 http_ret
= cursor
.get_error();
168 lderr(cct
) << "failed to collect the periods between current period "
169 << current_period
.get_id() << " (realm epoch " << current_epoch
170 << ") and the new period " << period
.get_id()
171 << " (realm epoch " << period
.get_realm_epoch()
172 << "): " << cpp_strerror(-http_ret
) << dendl
;
175 if (cursor
.has_next()) {
176 // don't switch if we have a newer period in our history
177 ldout(cct
, 4) << "attached period " << period
.get_id()
178 << " to history, but the history contains newer periods" << dendl
;
181 // set as current period
182 http_ret
= realm
.set_current_period(period
);
184 lderr(cct
) << "failed to update realm's current period" << dendl
;
187 ldout(cct
, 4) << "period " << period
.get_id()
188 << " is newer than current period " << current_period
.get_id()
189 << ", updating realm's current period and notifying zone" << dendl
;
190 realm
.notify_new_period(period
);
194 if (period
.get_epoch() <= current_period
.get_epoch()) {
195 lderr(cct
) << "period epoch " << period
.get_epoch() << " is not newer "
196 "than current epoch " << current_period
.get_epoch()
197 << ", discarding update" << dendl
;
200 // set as latest epoch
201 http_ret
= period
.set_latest_epoch(period
.get_epoch());
203 lderr(cct
) << "failed to set latest epoch" << dendl
;
206 // reflect the period into our local objects
207 http_ret
= period
.reflect();
209 lderr(cct
) << "failed to update local objects: "
210 << cpp_strerror(-http_ret
) << dendl
;
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
});
221 class RGWHandler_Period
: public RGWHandler_Auth_S3
{
223 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
225 RGWOp
*op_get() override
{ return new RGWOp_Period_Get
; }
226 RGWOp
*op_post() override
{ return new RGWOp_Period_Post
; }
229 class RGWRESTMgr_Period
: public RGWRESTMgr
{
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
);
240 class RGWOp_Realm_Get
: public RGWRESTOp
{
241 std::unique_ptr
<RGWRealm
> realm
;
243 int verify_permission() override
{ return 0; }
244 void execute() override
;
245 void send_response() override
;
246 const string
name() override
{ return "get_realm"; }
249 void RGWOp_Realm_Get::execute()
252 RESTArgs::get_string(s
, "id", id
, &id
);
254 RESTArgs::get_string(s
, "name", name
, &name
);
257 realm
.reset(new RGWRealm(id
, name
));
258 http_ret
= realm
->init(g_ceph_context
, store
);
260 lderr(store
->ctx()) << "failed to read realm id=" << id
261 << " name=" << name
<< dendl
;
264 void RGWOp_Realm_Get::send_response()
266 set_req_state_err(s
, http_ret
);
274 encode_json("realm", *realm
, s
->formatter
);
275 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
279 class RGWHandler_Realm
: public RGWHandler_Auth_S3
{
281 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
282 RGWOp
*op_get() override
{ return new RGWOp_Realm_Get
; }
285 RGWRESTMgr_Realm::RGWRESTMgr_Realm()
287 // add the /admin/realm/period resource
288 register_resource("period", new RGWRESTMgr_Period
);
292 RGWRESTMgr_Realm::get_handler(struct req_state
*,
293 const rgw::auth::StrategyRegistry
& auth_registry
,
296 return new RGWHandler_Realm(auth_registry
);