1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
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 "rgw_sal_rados.h"
11 #include "services/svc_zone.h"
12 #include "services/svc_mdlog.h"
14 #include "include/ceph_assert.h"
16 #define dout_subsys ceph_subsys_rgw
20 // reject 'period push' if we would have to fetch too many intermediate periods
21 static const uint32_t PERIOD_HISTORY_FETCH_MAX
= 64;
23 // base period op, shared between Get and Post
24 class RGWOp_Period_Base
: public RGWRESTOp
{
27 std::ostringstream error_stream
;
29 int verify_permission(optional_yield
) override
{ return 0; }
30 void send_response() override
;
33 // reply with the period object on success
34 void RGWOp_Period_Base::send_response()
36 set_req_state_err(s
, op_ret
, error_stream
.str());
40 if (!s
->err
.message
.empty()) {
41 ldpp_dout(this, 4) << "Request failed with " << op_ret
42 << ": " << s
->err
.message
<< dendl
;
48 encode_json("period", period
, s
->formatter
);
49 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
53 // GET /admin/realm/period
54 class RGWOp_Period_Get
: public RGWOp_Period_Base
{
56 void execute(optional_yield y
) override
;
57 int check_caps(const RGWUserCaps
& caps
) override
{
58 return caps
.check_cap("zone", RGW_CAP_READ
);
60 int verify_permission(optional_yield
) override
{
61 return check_caps(s
->user
->get_caps());
63 const char* name() const override
{ return "get_period"; }
66 void RGWOp_Period_Get::execute(optional_yield y
)
68 string realm_id
, realm_name
, period_id
;
70 RESTArgs::get_string(s
, "realm_id", realm_id
, &realm_id
);
71 RESTArgs::get_string(s
, "realm_name", realm_name
, &realm_name
);
72 RESTArgs::get_string(s
, "period_id", period_id
, &period_id
);
73 RESTArgs::get_uint32(s
, "epoch", 0, &epoch
);
75 period
.set_id(period_id
);
76 period
.set_epoch(epoch
);
78 op_ret
= period
.init(this, driver
->ctx(), static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
, realm_id
, y
, realm_name
);
80 ldpp_dout(this, 5) << "failed to read period" << dendl
;
83 // POST /admin/realm/period
84 class RGWOp_Period_Post
: public RGWOp_Period_Base
{
86 void execute(optional_yield y
) override
;
87 int check_caps(const RGWUserCaps
& caps
) override
{
88 return caps
.check_cap("zone", RGW_CAP_WRITE
);
90 int verify_permission(optional_yield
) override
{
91 return check_caps(s
->user
->get_caps());
93 const char* name() const override
{ return "post_period"; }
94 RGWOpType
get_type() override
{ return RGW_OP_PERIOD_POST
; }
97 void RGWOp_Period_Post::execute(optional_yield y
)
99 auto cct
= driver
->ctx();
101 // initialize the period without reading from rados
102 period
.init(this, cct
, static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
, y
, false);
104 // decode the period from input
105 const auto max_size
= cct
->_conf
->rgw_max_put_param_size
;
107 op_ret
= get_json_input(cct
, s
, period
, max_size
, &empty
);
109 ldpp_dout(this, -1) << "failed to decode period" << dendl
;
113 // require period.realm_id to match our realm
114 if (period
.get_realm() != static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->zone
->get_realm().get_id()) {
115 error_stream
<< "period with realm id " << period
.get_realm()
116 << " doesn't match current realm " << static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->zone
->get_realm().get_id() << std::endl
;
121 // load the realm and current period from rados; there may be a more recent
122 // period that we haven't restarted with yet. we also don't want to modify
123 // the objects in use by RGWRados
124 RGWRealm
realm(period
.get_realm());
125 op_ret
= realm
.init(this, cct
, static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
, y
);
127 ldpp_dout(this, -1) << "failed to read current realm: "
128 << cpp_strerror(-op_ret
) << dendl
;
132 RGWPeriod current_period
;
133 op_ret
= current_period
.init(this, cct
, static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
, realm
.get_id(), y
);
135 ldpp_dout(this, -1) << "failed to read current period: "
136 << cpp_strerror(-op_ret
) << dendl
;
140 // if period id is empty, handle as 'period commit'
141 if (period
.get_id().empty()) {
142 op_ret
= period
.commit(this, driver
, realm
, current_period
, error_stream
, y
);
144 ldpp_dout(this, -1) << "master zone failed to commit period" << dendl
;
149 // if it's not period commit, nobody is allowed to push to the master zone
150 if (period
.get_master_zone() == static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->zone
->get_zone_params().get_id()) {
151 ldpp_dout(this, 10) << "master zone rejecting period id="
152 << period
.get_id() << " epoch=" << period
.get_epoch() << dendl
;
153 op_ret
= -EINVAL
; // XXX: error code
157 // write the period to rados
158 op_ret
= period
.store_info(this, false, y
);
160 ldpp_dout(this, -1) << "failed to store period " << period
.get_id() << dendl
;
163 // set as latest epoch
164 op_ret
= period
.update_latest_epoch(this, period
.get_epoch(), y
);
165 if (op_ret
== -EEXIST
) {
166 // already have this epoch (or a more recent one)
167 ldpp_dout(this, 4) << "already have epoch >= " << period
.get_epoch()
168 << " for period " << period
.get_id() << dendl
;
173 ldpp_dout(this, -1) << "failed to set latest epoch" << dendl
;
177 auto period_history
= static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->mdlog
->get_period_history();
179 // decide whether we can set_current_period() or set_latest_epoch()
180 if (period
.get_id() != current_period
.get_id()) {
181 auto current_epoch
= current_period
.get_realm_epoch();
182 // discard periods in the past
183 if (period
.get_realm_epoch() < current_epoch
) {
184 ldpp_dout(this, 10) << "discarding period " << period
.get_id()
185 << " with realm epoch " << period
.get_realm_epoch()
186 << " older than current epoch " << current_epoch
<< dendl
;
187 // return success to ack that we have this period
190 // discard periods too far in the future
191 if (period
.get_realm_epoch() > current_epoch
+ PERIOD_HISTORY_FETCH_MAX
) {
192 ldpp_dout(this, -1) << "discarding period " << period
.get_id()
193 << " with realm epoch " << period
.get_realm_epoch() << " too far in "
194 "the future from current epoch " << current_epoch
<< dendl
;
195 op_ret
= -ENOENT
; // XXX: error code
198 // attach a copy of the period into the period history
199 auto cursor
= period_history
->attach(this, RGWPeriod
{period
}, y
);
201 // we're missing some history between the new period and current_period
202 op_ret
= cursor
.get_error();
203 ldpp_dout(this, -1) << "failed to collect the periods between current period "
204 << current_period
.get_id() << " (realm epoch " << current_epoch
205 << ") and the new period " << period
.get_id()
206 << " (realm epoch " << period
.get_realm_epoch()
207 << "): " << cpp_strerror(-op_ret
) << dendl
;
210 if (cursor
.has_next()) {
211 // don't switch if we have a newer period in our history
212 ldpp_dout(this, 4) << "attached period " << period
.get_id()
213 << " to history, but the history contains newer periods" << dendl
;
216 // set as current period
217 op_ret
= realm
.set_current_period(this, period
, y
);
219 ldpp_dout(this, -1) << "failed to update realm's current period" << dendl
;
222 ldpp_dout(this, 4) << "period " << period
.get_id()
223 << " is newer than current period " << current_period
.get_id()
224 << ", updating realm's current period and notifying zone" << dendl
;
225 realm
.notify_new_period(this, period
, y
);
228 // reflect the period into our local objects
229 op_ret
= period
.reflect(this, y
);
231 ldpp_dout(this, -1) << "failed to update local objects: "
232 << cpp_strerror(-op_ret
) << dendl
;
235 ldpp_dout(this, 4) << "period epoch " << period
.get_epoch()
236 << " is newer than current epoch " << current_period
.get_epoch()
237 << ", updating period's latest epoch and notifying zone" << dendl
;
238 realm
.notify_new_period(this, period
, y
);
239 // update the period history
240 period_history
->insert(RGWPeriod
{period
});
243 class RGWHandler_Period
: public RGWHandler_Auth_S3
{
245 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
247 RGWOp
*op_get() override
{ return new RGWOp_Period_Get
; }
248 RGWOp
*op_post() override
{ return new RGWOp_Period_Post
; }
251 class RGWRESTMgr_Period
: public RGWRESTMgr
{
253 RGWHandler_REST
* get_handler(rgw::sal::Driver
* driver
,
255 const rgw::auth::StrategyRegistry
& auth_registry
,
256 const std::string
&) override
{
257 return new RGWHandler_Period(auth_registry
);
263 class RGWOp_Realm_Get
: public RGWRESTOp
{
264 std::unique_ptr
<RGWRealm
> realm
;
266 int check_caps(const RGWUserCaps
& caps
) override
{
267 return caps
.check_cap("zone", RGW_CAP_READ
);
269 int verify_permission(optional_yield
) override
{
270 return check_caps(s
->user
->get_caps());
272 void execute(optional_yield y
) override
;
273 void send_response() override
;
274 const char* name() const override
{ return "get_realm"; }
277 void RGWOp_Realm_Get::execute(optional_yield y
)
280 RESTArgs::get_string(s
, "id", id
, &id
);
282 RESTArgs::get_string(s
, "name", name
, &name
);
285 realm
.reset(new RGWRealm(id
, name
));
286 op_ret
= realm
->init(this, g_ceph_context
, static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
, y
);
288 ldpp_dout(this, -1) << "failed to read realm id=" << id
289 << " name=" << name
<< dendl
;
292 void RGWOp_Realm_Get::send_response()
294 set_req_state_err(s
, op_ret
);
302 encode_json("realm", *realm
, s
->formatter
);
303 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
307 // GET /admin/realm?list
308 class RGWOp_Realm_List
: public RGWRESTOp
{
309 std::string default_id
;
310 std::list
<std::string
> realms
;
312 int check_caps(const RGWUserCaps
& caps
) override
{
313 return caps
.check_cap("zone", RGW_CAP_READ
);
315 int verify_permission(optional_yield
) override
{
316 return check_caps(s
->user
->get_caps());
318 void execute(optional_yield y
) override
;
319 void send_response() override
;
320 const char* name() const override
{ return "list_realms"; }
323 void RGWOp_Realm_List::execute(optional_yield y
)
326 // read default realm
327 RGWRealm
realm(driver
->ctx(), static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->sysobj
);
328 [[maybe_unused
]] int ret
= realm
.read_default_id(this, default_id
, y
);
330 op_ret
= static_cast<rgw::sal::RadosStore
*>(driver
)->svc()->zone
->list_realms(this, realms
);
332 ldpp_dout(this, -1) << "failed to list realms" << dendl
;
335 void RGWOp_Realm_List::send_response()
337 set_req_state_err(s
, op_ret
);
345 s
->formatter
->open_object_section("realms_list");
346 encode_json("default_info", default_id
, s
->formatter
);
347 encode_json("realms", realms
, s
->formatter
);
348 s
->formatter
->close_section();
349 end_header(s
, NULL
, "application/json", s
->formatter
->get_len());
353 class RGWHandler_Realm
: public RGWHandler_Auth_S3
{
355 using RGWHandler_Auth_S3::RGWHandler_Auth_S3
;
356 RGWOp
*op_get() override
{
357 if (s
->info
.args
.sub_resource_exists("list"))
358 return new RGWOp_Realm_List
;
359 return new RGWOp_Realm_Get
;
363 RGWRESTMgr_Realm::RGWRESTMgr_Realm()
365 // add the /admin/realm/period resource
366 register_resource("period", new RGWRESTMgr_Period
);
370 RGWRESTMgr_Realm::get_handler(rgw::sal::Driver
* driver
,
372 const rgw::auth::StrategyRegistry
& auth_registry
,
375 return new RGWHandler_Realm(auth_registry
);