Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / rgw / rgw_rest_realm.cc
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 {
29   set_req_state_err(s, http_ret, error_stream.str());
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   }
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   }
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   }
206   // reflect the period into our local objects
207   http_ret = period.reflect();
208   if (http_ret < 0) {
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 }