]>
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 | |
11fdf7f2 | 3 | |
7c673cae FG |
4 | /* |
5 | * Ceph - scalable distributed file system | |
6 | * | |
7 | * Copyright (C) 2013 eNovance SAS <licensing@enovance.com> | |
8 | * | |
9 | * This is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License version 2.1, as published by the Free Software | |
12 | * Foundation. See file COPYING. | |
13 | * | |
14 | */ | |
11fdf7f2 | 15 | |
7c673cae FG |
16 | #include "common/ceph_json.h" |
17 | #include "common/strtol.h" | |
18 | #include "rgw_rest.h" | |
19 | #include "rgw_op.h" | |
20 | #include "rgw_rest_s3.h" | |
21 | #include "rgw_rest_log.h" | |
22 | #include "rgw_client_io.h" | |
23 | #include "rgw_sync.h" | |
24 | #include "rgw_data_sync.h" | |
31f18b77 | 25 | #include "rgw_common.h" |
11fdf7f2 TL |
26 | #include "rgw_zone.h" |
27 | ||
28 | #include "services/svc_zone.h" | |
29 | ||
7c673cae | 30 | #include "common/errno.h" |
11fdf7f2 | 31 | #include "include/ceph_assert.h" |
7c673cae FG |
32 | |
33 | #define dout_context g_ceph_context | |
34 | #define LOG_CLASS_LIST_MAX_ENTRIES (1000) | |
35 | #define dout_subsys ceph_subsys_rgw | |
36 | ||
37 | static int parse_date_str(string& in, real_time& out) { | |
38 | uint64_t epoch = 0; | |
39 | uint64_t nsec = 0; | |
40 | ||
41 | if (!in.empty()) { | |
42 | if (utime_t::parse_date(in, &epoch, &nsec) < 0) { | |
43 | dout(5) << "Error parsing date " << in << dendl; | |
44 | return -EINVAL; | |
45 | } | |
46 | } | |
47 | out = utime_t(epoch, nsec).to_real_time(); | |
48 | return 0; | |
49 | } | |
50 | ||
51 | void RGWOp_MDLog_List::execute() { | |
52 | string period = s->info.args.get("period"); | |
53 | string shard = s->info.args.get("id"); | |
54 | string max_entries_str = s->info.args.get("max-entries"); | |
55 | string st = s->info.args.get("start-time"), | |
56 | et = s->info.args.get("end-time"), | |
57 | marker = s->info.args.get("marker"), | |
58 | err; | |
59 | real_time ut_st, | |
60 | ut_et; | |
61 | void *handle; | |
62 | unsigned shard_id, max_entries = LOG_CLASS_LIST_MAX_ENTRIES; | |
63 | ||
64 | shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
65 | if (!err.empty()) { | |
66 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
67 | http_ret = -EINVAL; | |
68 | return; | |
69 | } | |
70 | ||
71 | if (parse_date_str(st, ut_st) < 0) { | |
72 | http_ret = -EINVAL; | |
73 | return; | |
74 | } | |
75 | ||
76 | if (parse_date_str(et, ut_et) < 0) { | |
77 | http_ret = -EINVAL; | |
78 | return; | |
79 | } | |
80 | ||
81 | if (!max_entries_str.empty()) { | |
82 | max_entries = (unsigned)strict_strtol(max_entries_str.c_str(), 10, &err); | |
83 | if (!err.empty()) { | |
84 | dout(5) << "Error parsing max-entries " << max_entries_str << dendl; | |
85 | http_ret = -EINVAL; | |
86 | return; | |
87 | } | |
224ce89b WB |
88 | if (max_entries > LOG_CLASS_LIST_MAX_ENTRIES) { |
89 | max_entries = LOG_CLASS_LIST_MAX_ENTRIES; | |
90 | } | |
7c673cae FG |
91 | } |
92 | ||
93 | if (period.empty()) { | |
94 | ldout(s->cct, 5) << "Missing period id trying to use current" << dendl; | |
11fdf7f2 | 95 | period = store->svc.zone->get_current_period_id(); |
7c673cae FG |
96 | if (period.empty()) { |
97 | ldout(s->cct, 5) << "Missing period id" << dendl; | |
98 | http_ret = -EINVAL; | |
99 | return; | |
100 | } | |
101 | } | |
102 | ||
103 | RGWMetadataLog meta_log{s->cct, store, period}; | |
104 | ||
105 | meta_log.init_list_entries(shard_id, ut_st, ut_et, marker, &handle); | |
106 | ||
224ce89b WB |
107 | http_ret = meta_log.list_entries(handle, max_entries, entries, |
108 | &last_marker, &truncated); | |
7c673cae FG |
109 | |
110 | meta_log.complete_list_entries(handle); | |
111 | } | |
112 | ||
113 | void RGWOp_MDLog_List::send_response() { | |
114 | set_req_state_err(s, http_ret); | |
115 | dump_errno(s); | |
116 | end_header(s); | |
117 | ||
118 | if (http_ret < 0) | |
119 | return; | |
120 | ||
121 | s->formatter->open_object_section("log_entries"); | |
122 | s->formatter->dump_string("marker", last_marker); | |
123 | s->formatter->dump_bool("truncated", truncated); | |
124 | { | |
125 | s->formatter->open_array_section("entries"); | |
126 | for (list<cls_log_entry>::iterator iter = entries.begin(); | |
127 | iter != entries.end(); ++iter) { | |
128 | cls_log_entry& entry = *iter; | |
129 | store->meta_mgr->dump_log_entry(entry, s->formatter); | |
130 | flusher.flush(); | |
131 | } | |
132 | s->formatter->close_section(); | |
133 | } | |
134 | s->formatter->close_section(); | |
135 | flusher.flush(); | |
136 | } | |
137 | ||
138 | void RGWOp_MDLog_Info::execute() { | |
139 | num_objects = s->cct->_conf->rgw_md_log_max_shards; | |
140 | period = store->meta_mgr->read_oldest_log_period(); | |
141 | http_ret = period.get_error(); | |
142 | } | |
143 | ||
144 | void RGWOp_MDLog_Info::send_response() { | |
145 | set_req_state_err(s, http_ret); | |
146 | dump_errno(s); | |
147 | end_header(s); | |
148 | ||
149 | s->formatter->open_object_section("mdlog"); | |
150 | s->formatter->dump_unsigned("num_objects", num_objects); | |
151 | if (period) { | |
152 | s->formatter->dump_string("period", period.get_period().get_id()); | |
153 | s->formatter->dump_unsigned("realm_epoch", period.get_epoch()); | |
154 | } | |
155 | s->formatter->close_section(); | |
156 | flusher.flush(); | |
157 | } | |
158 | ||
159 | void RGWOp_MDLog_ShardInfo::execute() { | |
160 | string period = s->info.args.get("period"); | |
161 | string shard = s->info.args.get("id"); | |
162 | string err; | |
163 | ||
164 | unsigned shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
165 | if (!err.empty()) { | |
166 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
167 | http_ret = -EINVAL; | |
168 | return; | |
169 | } | |
170 | ||
171 | if (period.empty()) { | |
172 | ldout(s->cct, 5) << "Missing period id trying to use current" << dendl; | |
11fdf7f2 | 173 | period = store->svc.zone->get_current_period_id(); |
7c673cae FG |
174 | |
175 | if (period.empty()) { | |
176 | ldout(s->cct, 5) << "Missing period id" << dendl; | |
177 | http_ret = -EINVAL; | |
178 | return; | |
179 | } | |
180 | } | |
181 | RGWMetadataLog meta_log{s->cct, store, period}; | |
182 | ||
183 | http_ret = meta_log.get_info(shard_id, &info); | |
184 | } | |
185 | ||
186 | void RGWOp_MDLog_ShardInfo::send_response() { | |
187 | set_req_state_err(s, http_ret); | |
188 | dump_errno(s); | |
189 | end_header(s); | |
190 | ||
191 | encode_json("info", info, s->formatter); | |
192 | flusher.flush(); | |
193 | } | |
194 | ||
195 | void RGWOp_MDLog_Delete::execute() { | |
196 | string st = s->info.args.get("start-time"), | |
197 | et = s->info.args.get("end-time"), | |
198 | start_marker = s->info.args.get("start-marker"), | |
199 | end_marker = s->info.args.get("end-marker"), | |
200 | period = s->info.args.get("period"), | |
201 | shard = s->info.args.get("id"), | |
202 | err; | |
203 | real_time ut_st, | |
204 | ut_et; | |
205 | unsigned shard_id; | |
206 | ||
207 | http_ret = 0; | |
208 | ||
209 | shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
210 | if (!err.empty()) { | |
211 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
212 | http_ret = -EINVAL; | |
213 | return; | |
214 | } | |
215 | if (et.empty() && end_marker.empty()) { /* bounding end */ | |
216 | http_ret = -EINVAL; | |
217 | return; | |
218 | } | |
219 | ||
220 | if (parse_date_str(st, ut_st) < 0) { | |
221 | http_ret = -EINVAL; | |
222 | return; | |
223 | } | |
224 | ||
225 | if (parse_date_str(et, ut_et) < 0) { | |
226 | http_ret = -EINVAL; | |
227 | return; | |
228 | } | |
229 | ||
230 | if (period.empty()) { | |
231 | ldout(s->cct, 5) << "Missing period id trying to use current" << dendl; | |
11fdf7f2 | 232 | period = store->svc.zone->get_current_period_id(); |
7c673cae FG |
233 | |
234 | if (period.empty()) { | |
235 | ldout(s->cct, 5) << "Missing period id" << dendl; | |
236 | http_ret = -EINVAL; | |
237 | return; | |
238 | } | |
239 | } | |
240 | RGWMetadataLog meta_log{s->cct, store, period}; | |
241 | ||
242 | http_ret = meta_log.trim(shard_id, ut_st, ut_et, start_marker, end_marker); | |
243 | } | |
244 | ||
245 | void RGWOp_MDLog_Lock::execute() { | |
246 | string period, shard_id_str, duration_str, locker_id, zone_id; | |
247 | unsigned shard_id; | |
248 | ||
249 | http_ret = 0; | |
250 | ||
251 | period = s->info.args.get("period"); | |
252 | shard_id_str = s->info.args.get("id"); | |
253 | duration_str = s->info.args.get("length"); | |
254 | locker_id = s->info.args.get("locker-id"); | |
255 | zone_id = s->info.args.get("zone-id"); | |
256 | ||
257 | if (period.empty()) { | |
258 | ldout(s->cct, 5) << "Missing period id trying to use current" << dendl; | |
11fdf7f2 | 259 | period = store->svc.zone->get_current_period_id(); |
7c673cae FG |
260 | } |
261 | ||
262 | if (period.empty() || | |
263 | shard_id_str.empty() || | |
264 | (duration_str.empty()) || | |
265 | locker_id.empty() || | |
266 | zone_id.empty()) { | |
267 | dout(5) << "Error invalid parameter list" << dendl; | |
268 | http_ret = -EINVAL; | |
269 | return; | |
270 | } | |
271 | ||
272 | string err; | |
273 | shard_id = (unsigned)strict_strtol(shard_id_str.c_str(), 10, &err); | |
274 | if (!err.empty()) { | |
275 | dout(5) << "Error parsing shard_id param " << shard_id_str << dendl; | |
276 | http_ret = -EINVAL; | |
277 | return; | |
278 | } | |
279 | ||
280 | RGWMetadataLog meta_log{s->cct, store, period}; | |
281 | unsigned dur; | |
282 | dur = (unsigned)strict_strtol(duration_str.c_str(), 10, &err); | |
283 | if (!err.empty() || dur <= 0) { | |
284 | dout(5) << "invalid length param " << duration_str << dendl; | |
285 | http_ret = -EINVAL; | |
286 | return; | |
287 | } | |
288 | http_ret = meta_log.lock_exclusive(shard_id, make_timespan(dur), zone_id, | |
289 | locker_id); | |
290 | if (http_ret == -EBUSY) | |
291 | http_ret = -ERR_LOCKED; | |
292 | } | |
293 | ||
294 | void RGWOp_MDLog_Unlock::execute() { | |
295 | string period, shard_id_str, locker_id, zone_id; | |
296 | unsigned shard_id; | |
297 | ||
298 | http_ret = 0; | |
299 | ||
300 | period = s->info.args.get("period"); | |
301 | shard_id_str = s->info.args.get("id"); | |
302 | locker_id = s->info.args.get("locker-id"); | |
303 | zone_id = s->info.args.get("zone-id"); | |
304 | ||
305 | if (period.empty()) { | |
306 | ldout(s->cct, 5) << "Missing period id trying to use current" << dendl; | |
11fdf7f2 | 307 | period = store->svc.zone->get_current_period_id(); |
7c673cae FG |
308 | } |
309 | ||
310 | if (period.empty() || | |
311 | shard_id_str.empty() || | |
312 | locker_id.empty() || | |
313 | zone_id.empty()) { | |
314 | dout(5) << "Error invalid parameter list" << dendl; | |
315 | http_ret = -EINVAL; | |
316 | return; | |
317 | } | |
318 | ||
319 | string err; | |
320 | shard_id = (unsigned)strict_strtol(shard_id_str.c_str(), 10, &err); | |
321 | if (!err.empty()) { | |
322 | dout(5) << "Error parsing shard_id param " << shard_id_str << dendl; | |
323 | http_ret = -EINVAL; | |
324 | return; | |
325 | } | |
326 | ||
327 | RGWMetadataLog meta_log{s->cct, store, period}; | |
328 | http_ret = meta_log.unlock(shard_id, zone_id, locker_id); | |
329 | } | |
330 | ||
331 | void RGWOp_MDLog_Notify::execute() { | |
7c673cae | 332 | #define LARGE_ENOUGH_BUF (128 * 1024) |
11fdf7f2 TL |
333 | |
334 | int r = 0; | |
335 | bufferlist data; | |
336 | std::tie(r, data) = rgw_rest_read_all_input(s, LARGE_ENOUGH_BUF); | |
7c673cae FG |
337 | if (r < 0) { |
338 | http_ret = r; | |
339 | return; | |
340 | } | |
341 | ||
11fdf7f2 TL |
342 | char* buf = data.c_str(); |
343 | ldout(s->cct, 20) << __func__ << "(): read data: " << buf << dendl; | |
7c673cae FG |
344 | |
345 | JSONParser p; | |
11fdf7f2 | 346 | r = p.parse(buf, data.length()); |
7c673cae FG |
347 | if (r < 0) { |
348 | ldout(s->cct, 0) << "ERROR: failed to parse JSON" << dendl; | |
349 | http_ret = r; | |
350 | return; | |
351 | } | |
352 | ||
353 | set<int> updated_shards; | |
354 | try { | |
355 | decode_json_obj(updated_shards, &p); | |
356 | } catch (JSONDecoder::err& err) { | |
357 | ldout(s->cct, 0) << "ERROR: failed to decode JSON" << dendl; | |
358 | http_ret = -EINVAL; | |
359 | return; | |
360 | } | |
361 | ||
11fdf7f2 | 362 | if (store->ctx()->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) { |
7c673cae FG |
363 | for (set<int>::iterator iter = updated_shards.begin(); iter != updated_shards.end(); ++iter) { |
364 | ldout(s->cct, 20) << __func__ << "(): updated shard=" << *iter << dendl; | |
365 | } | |
366 | } | |
367 | ||
368 | store->wakeup_meta_sync_shards(updated_shards); | |
369 | ||
370 | http_ret = 0; | |
371 | } | |
372 | ||
373 | void RGWOp_BILog_List::execute() { | |
374 | string tenant_name = s->info.args.get("tenant"), | |
375 | bucket_name = s->info.args.get("bucket"), | |
376 | marker = s->info.args.get("marker"), | |
377 | max_entries_str = s->info.args.get("max-entries"), | |
378 | bucket_instance = s->info.args.get("bucket-instance"); | |
379 | RGWBucketInfo bucket_info; | |
380 | unsigned max_entries; | |
381 | ||
7c673cae FG |
382 | if (bucket_name.empty() && bucket_instance.empty()) { |
383 | dout(5) << "ERROR: neither bucket nor bucket instance specified" << dendl; | |
384 | http_ret = -EINVAL; | |
385 | return; | |
386 | } | |
387 | ||
388 | int shard_id; | |
389 | http_ret = rgw_bucket_parse_bucket_instance(bucket_instance, &bucket_instance, &shard_id); | |
390 | if (http_ret < 0) { | |
391 | return; | |
392 | } | |
393 | ||
394 | if (!bucket_instance.empty()) { | |
11fdf7f2 | 395 | http_ret = store->get_bucket_instance_info(*s->sysobj_ctx, bucket_instance, bucket_info, NULL, NULL); |
7c673cae FG |
396 | if (http_ret < 0) { |
397 | dout(5) << "could not get bucket instance info for bucket instance id=" << bucket_instance << dendl; | |
398 | return; | |
399 | } | |
400 | } else { /* !bucket_name.empty() */ | |
11fdf7f2 | 401 | http_ret = store->get_bucket_info(*s->sysobj_ctx, tenant_name, bucket_name, bucket_info, NULL, NULL); |
7c673cae FG |
402 | if (http_ret < 0) { |
403 | dout(5) << "could not get bucket info for bucket=" << bucket_name << dendl; | |
404 | return; | |
405 | } | |
406 | } | |
407 | ||
408 | bool truncated; | |
409 | unsigned count = 0; | |
410 | string err; | |
411 | ||
412 | max_entries = (unsigned)strict_strtol(max_entries_str.c_str(), 10, &err); | |
413 | if (!err.empty()) | |
414 | max_entries = LOG_CLASS_LIST_MAX_ENTRIES; | |
415 | ||
416 | send_response(); | |
417 | do { | |
418 | list<rgw_bi_log_entry> entries; | |
419 | int ret = store->list_bi_log_entries(bucket_info, shard_id, | |
420 | marker, max_entries - count, | |
421 | entries, &truncated); | |
422 | if (ret < 0) { | |
423 | dout(5) << "ERROR: list_bi_log_entries()" << dendl; | |
424 | return; | |
425 | } | |
426 | ||
427 | count += entries.size(); | |
428 | ||
429 | send_response(entries, marker); | |
430 | } while (truncated && count < max_entries); | |
431 | ||
432 | send_response_end(); | |
433 | } | |
434 | ||
435 | void RGWOp_BILog_List::send_response() { | |
436 | if (sent_header) | |
437 | return; | |
438 | ||
439 | set_req_state_err(s, http_ret); | |
440 | dump_errno(s); | |
441 | end_header(s); | |
442 | ||
443 | sent_header = true; | |
444 | ||
445 | if (http_ret < 0) | |
446 | return; | |
447 | ||
448 | s->formatter->open_array_section("entries"); | |
449 | } | |
450 | ||
451 | void RGWOp_BILog_List::send_response(list<rgw_bi_log_entry>& entries, string& marker) | |
452 | { | |
453 | for (list<rgw_bi_log_entry>::iterator iter = entries.begin(); iter != entries.end(); ++iter) { | |
454 | rgw_bi_log_entry& entry = *iter; | |
455 | encode_json("entry", entry, s->formatter); | |
456 | ||
457 | marker = entry.id; | |
458 | flusher.flush(); | |
459 | } | |
460 | } | |
461 | ||
462 | void RGWOp_BILog_List::send_response_end() { | |
463 | s->formatter->close_section(); | |
464 | flusher.flush(); | |
465 | } | |
466 | ||
467 | void RGWOp_BILog_Info::execute() { | |
468 | string tenant_name = s->info.args.get("tenant"), | |
469 | bucket_name = s->info.args.get("bucket"), | |
470 | bucket_instance = s->info.args.get("bucket-instance"); | |
471 | RGWBucketInfo bucket_info; | |
472 | ||
7c673cae FG |
473 | if (bucket_name.empty() && bucket_instance.empty()) { |
474 | dout(5) << "ERROR: neither bucket nor bucket instance specified" << dendl; | |
475 | http_ret = -EINVAL; | |
476 | return; | |
477 | } | |
478 | ||
479 | int shard_id; | |
480 | http_ret = rgw_bucket_parse_bucket_instance(bucket_instance, &bucket_instance, &shard_id); | |
481 | if (http_ret < 0) { | |
482 | return; | |
483 | } | |
484 | ||
485 | if (!bucket_instance.empty()) { | |
11fdf7f2 | 486 | http_ret = store->get_bucket_instance_info(*s->sysobj_ctx, bucket_instance, bucket_info, NULL, NULL); |
7c673cae FG |
487 | if (http_ret < 0) { |
488 | dout(5) << "could not get bucket instance info for bucket instance id=" << bucket_instance << dendl; | |
489 | return; | |
490 | } | |
491 | } else { /* !bucket_name.empty() */ | |
11fdf7f2 | 492 | http_ret = store->get_bucket_info(*s->sysobj_ctx, tenant_name, bucket_name, bucket_info, NULL, NULL); |
7c673cae FG |
493 | if (http_ret < 0) { |
494 | dout(5) << "could not get bucket info for bucket=" << bucket_name << dendl; | |
495 | return; | |
496 | } | |
497 | } | |
498 | map<RGWObjCategory, RGWStorageStats> stats; | |
c07f9fc5 | 499 | int ret = store->get_bucket_stats(bucket_info, shard_id, &bucket_ver, &master_ver, stats, &max_marker, &syncstopped); |
7c673cae FG |
500 | if (ret < 0 && ret != -ENOENT) { |
501 | http_ret = ret; | |
502 | return; | |
503 | } | |
504 | } | |
505 | ||
506 | void RGWOp_BILog_Info::send_response() { | |
507 | set_req_state_err(s, http_ret); | |
508 | dump_errno(s); | |
509 | end_header(s); | |
510 | ||
511 | if (http_ret < 0) | |
512 | return; | |
513 | ||
514 | s->formatter->open_object_section("info"); | |
515 | encode_json("bucket_ver", bucket_ver, s->formatter); | |
516 | encode_json("master_ver", master_ver, s->formatter); | |
517 | encode_json("max_marker", max_marker, s->formatter); | |
c07f9fc5 | 518 | encode_json("syncstopped", syncstopped, s->formatter); |
7c673cae FG |
519 | s->formatter->close_section(); |
520 | ||
521 | flusher.flush(); | |
522 | } | |
523 | ||
524 | void RGWOp_BILog_Delete::execute() { | |
525 | string tenant_name = s->info.args.get("tenant"), | |
526 | bucket_name = s->info.args.get("bucket"), | |
527 | start_marker = s->info.args.get("start-marker"), | |
528 | end_marker = s->info.args.get("end-marker"), | |
529 | bucket_instance = s->info.args.get("bucket-instance"); | |
530 | ||
531 | RGWBucketInfo bucket_info; | |
532 | ||
7c673cae FG |
533 | http_ret = 0; |
534 | if ((bucket_name.empty() && bucket_instance.empty()) || | |
535 | end_marker.empty()) { | |
536 | dout(5) << "ERROR: one of bucket and bucket instance, and also end-marker is mandatory" << dendl; | |
537 | http_ret = -EINVAL; | |
538 | return; | |
539 | } | |
540 | ||
541 | int shard_id; | |
542 | http_ret = rgw_bucket_parse_bucket_instance(bucket_instance, &bucket_instance, &shard_id); | |
543 | if (http_ret < 0) { | |
544 | return; | |
545 | } | |
546 | ||
547 | if (!bucket_instance.empty()) { | |
11fdf7f2 | 548 | http_ret = store->get_bucket_instance_info(*s->sysobj_ctx, bucket_instance, bucket_info, NULL, NULL); |
7c673cae FG |
549 | if (http_ret < 0) { |
550 | dout(5) << "could not get bucket instance info for bucket instance id=" << bucket_instance << dendl; | |
551 | return; | |
552 | } | |
553 | } else { /* !bucket_name.empty() */ | |
11fdf7f2 | 554 | http_ret = store->get_bucket_info(*s->sysobj_ctx, tenant_name, bucket_name, bucket_info, NULL, NULL); |
7c673cae FG |
555 | if (http_ret < 0) { |
556 | dout(5) << "could not get bucket info for bucket=" << bucket_name << dendl; | |
557 | return; | |
558 | } | |
559 | } | |
560 | http_ret = store->trim_bi_log_entries(bucket_info, shard_id, start_marker, end_marker); | |
561 | if (http_ret < 0) { | |
562 | dout(5) << "ERROR: trim_bi_log_entries() " << dendl; | |
563 | } | |
564 | return; | |
565 | } | |
566 | ||
567 | void RGWOp_DATALog_List::execute() { | |
568 | string shard = s->info.args.get("id"); | |
569 | ||
570 | string st = s->info.args.get("start-time"), | |
571 | et = s->info.args.get("end-time"), | |
572 | max_entries_str = s->info.args.get("max-entries"), | |
573 | marker = s->info.args.get("marker"), | |
574 | err; | |
575 | real_time ut_st, | |
576 | ut_et; | |
577 | unsigned shard_id, max_entries = LOG_CLASS_LIST_MAX_ENTRIES; | |
578 | ||
579 | s->info.args.get_bool("extra-info", &extra_info, false); | |
580 | ||
581 | shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
582 | if (!err.empty()) { | |
583 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
584 | http_ret = -EINVAL; | |
585 | return; | |
586 | } | |
587 | ||
588 | if (parse_date_str(st, ut_st) < 0) { | |
589 | http_ret = -EINVAL; | |
590 | return; | |
591 | } | |
592 | ||
593 | if (parse_date_str(et, ut_et) < 0) { | |
594 | http_ret = -EINVAL; | |
595 | return; | |
596 | } | |
597 | ||
598 | if (!max_entries_str.empty()) { | |
599 | max_entries = (unsigned)strict_strtol(max_entries_str.c_str(), 10, &err); | |
600 | if (!err.empty()) { | |
601 | dout(5) << "Error parsing max-entries " << max_entries_str << dendl; | |
602 | http_ret = -EINVAL; | |
603 | return; | |
604 | } | |
224ce89b WB |
605 | if (max_entries > LOG_CLASS_LIST_MAX_ENTRIES) { |
606 | max_entries = LOG_CLASS_LIST_MAX_ENTRIES; | |
607 | } | |
608 | } | |
609 | ||
610 | // Note that last_marker is updated to be the marker of the last | |
611 | // entry listed | |
612 | http_ret = store->data_log->list_entries(shard_id, ut_st, ut_et, | |
613 | max_entries, entries, marker, | |
614 | &last_marker, &truncated); | |
7c673cae FG |
615 | } |
616 | ||
617 | void RGWOp_DATALog_List::send_response() { | |
618 | set_req_state_err(s, http_ret); | |
619 | dump_errno(s); | |
620 | end_header(s); | |
621 | ||
622 | if (http_ret < 0) | |
623 | return; | |
624 | ||
625 | s->formatter->open_object_section("log_entries"); | |
626 | s->formatter->dump_string("marker", last_marker); | |
627 | s->formatter->dump_bool("truncated", truncated); | |
628 | { | |
629 | s->formatter->open_array_section("entries"); | |
630 | for (list<rgw_data_change_log_entry>::iterator iter = entries.begin(); | |
631 | iter != entries.end(); ++iter) { | |
632 | rgw_data_change_log_entry& entry = *iter; | |
633 | if (!extra_info) { | |
634 | encode_json("entry", entry.entry, s->formatter); | |
635 | } else { | |
636 | encode_json("entry", entry, s->formatter); | |
637 | } | |
638 | flusher.flush(); | |
639 | } | |
640 | s->formatter->close_section(); | |
641 | } | |
642 | s->formatter->close_section(); | |
643 | flusher.flush(); | |
644 | } | |
645 | ||
646 | ||
647 | void RGWOp_DATALog_Info::execute() { | |
648 | num_objects = s->cct->_conf->rgw_data_log_num_shards; | |
649 | http_ret = 0; | |
650 | } | |
651 | ||
652 | void RGWOp_DATALog_Info::send_response() { | |
653 | set_req_state_err(s, http_ret); | |
654 | dump_errno(s); | |
655 | end_header(s); | |
656 | ||
657 | s->formatter->open_object_section("num_objects"); | |
658 | s->formatter->dump_unsigned("num_objects", num_objects); | |
659 | s->formatter->close_section(); | |
660 | flusher.flush(); | |
661 | } | |
662 | ||
663 | void RGWOp_DATALog_ShardInfo::execute() { | |
664 | string shard = s->info.args.get("id"); | |
665 | string err; | |
666 | ||
667 | unsigned shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
668 | if (!err.empty()) { | |
669 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
670 | http_ret = -EINVAL; | |
671 | return; | |
672 | } | |
673 | ||
674 | http_ret = store->data_log->get_info(shard_id, &info); | |
675 | } | |
676 | ||
677 | void RGWOp_DATALog_ShardInfo::send_response() { | |
678 | set_req_state_err(s, http_ret); | |
679 | dump_errno(s); | |
680 | end_header(s); | |
681 | ||
682 | encode_json("info", info, s->formatter); | |
683 | flusher.flush(); | |
684 | } | |
685 | ||
686 | void RGWOp_DATALog_Lock::execute() { | |
687 | string shard_id_str, duration_str, locker_id, zone_id; | |
688 | unsigned shard_id; | |
689 | ||
690 | http_ret = 0; | |
691 | ||
692 | shard_id_str = s->info.args.get("id"); | |
693 | duration_str = s->info.args.get("length"); | |
694 | locker_id = s->info.args.get("locker-id"); | |
695 | zone_id = s->info.args.get("zone-id"); | |
696 | ||
697 | if (shard_id_str.empty() || | |
698 | (duration_str.empty()) || | |
699 | locker_id.empty() || | |
700 | zone_id.empty()) { | |
701 | dout(5) << "Error invalid parameter list" << dendl; | |
702 | http_ret = -EINVAL; | |
703 | return; | |
704 | } | |
705 | ||
706 | string err; | |
707 | shard_id = (unsigned)strict_strtol(shard_id_str.c_str(), 10, &err); | |
708 | if (!err.empty()) { | |
709 | dout(5) << "Error parsing shard_id param " << shard_id_str << dendl; | |
710 | http_ret = -EINVAL; | |
711 | return; | |
712 | } | |
713 | ||
714 | unsigned dur; | |
715 | dur = (unsigned)strict_strtol(duration_str.c_str(), 10, &err); | |
716 | if (!err.empty() || dur <= 0) { | |
717 | dout(5) << "invalid length param " << duration_str << dendl; | |
718 | http_ret = -EINVAL; | |
719 | return; | |
720 | } | |
721 | http_ret = store->data_log->lock_exclusive(shard_id, make_timespan(dur), zone_id, locker_id); | |
722 | if (http_ret == -EBUSY) | |
723 | http_ret = -ERR_LOCKED; | |
724 | } | |
725 | ||
726 | void RGWOp_DATALog_Unlock::execute() { | |
727 | string shard_id_str, locker_id, zone_id; | |
728 | unsigned shard_id; | |
729 | ||
730 | http_ret = 0; | |
731 | ||
732 | shard_id_str = s->info.args.get("id"); | |
733 | locker_id = s->info.args.get("locker-id"); | |
734 | zone_id = s->info.args.get("zone-id"); | |
735 | ||
736 | if (shard_id_str.empty() || | |
737 | locker_id.empty() || | |
738 | zone_id.empty()) { | |
739 | dout(5) << "Error invalid parameter list" << dendl; | |
740 | http_ret = -EINVAL; | |
741 | return; | |
742 | } | |
743 | ||
744 | string err; | |
745 | shard_id = (unsigned)strict_strtol(shard_id_str.c_str(), 10, &err); | |
746 | if (!err.empty()) { | |
747 | dout(5) << "Error parsing shard_id param " << shard_id_str << dendl; | |
748 | http_ret = -EINVAL; | |
749 | return; | |
750 | } | |
751 | ||
752 | http_ret = store->data_log->unlock(shard_id, zone_id, locker_id); | |
753 | } | |
754 | ||
755 | void RGWOp_DATALog_Notify::execute() { | |
756 | string source_zone = s->info.args.get("source-zone"); | |
7c673cae | 757 | #define LARGE_ENOUGH_BUF (128 * 1024) |
11fdf7f2 TL |
758 | |
759 | int r = 0; | |
760 | bufferlist data; | |
761 | std::tie(r, data) = rgw_rest_read_all_input(s, LARGE_ENOUGH_BUF); | |
7c673cae FG |
762 | if (r < 0) { |
763 | http_ret = r; | |
764 | return; | |
765 | } | |
766 | ||
11fdf7f2 TL |
767 | char* buf = data.c_str(); |
768 | ldout(s->cct, 20) << __func__ << "(): read data: " << buf << dendl; | |
7c673cae FG |
769 | |
770 | JSONParser p; | |
11fdf7f2 | 771 | r = p.parse(buf, data.length()); |
7c673cae FG |
772 | if (r < 0) { |
773 | ldout(s->cct, 0) << "ERROR: failed to parse JSON" << dendl; | |
774 | http_ret = r; | |
775 | return; | |
776 | } | |
777 | ||
778 | map<int, set<string> > updated_shards; | |
779 | try { | |
780 | decode_json_obj(updated_shards, &p); | |
781 | } catch (JSONDecoder::err& err) { | |
782 | ldout(s->cct, 0) << "ERROR: failed to decode JSON" << dendl; | |
783 | http_ret = -EINVAL; | |
784 | return; | |
785 | } | |
786 | ||
11fdf7f2 | 787 | if (store->ctx()->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) { |
7c673cae FG |
788 | for (map<int, set<string> >::iterator iter = updated_shards.begin(); iter != updated_shards.end(); ++iter) { |
789 | ldout(s->cct, 20) << __func__ << "(): updated shard=" << iter->first << dendl; | |
790 | set<string>& keys = iter->second; | |
791 | for (set<string>::iterator kiter = keys.begin(); kiter != keys.end(); ++kiter) { | |
792 | ldout(s->cct, 20) << __func__ << "(): modified key=" << *kiter << dendl; | |
793 | } | |
794 | } | |
795 | } | |
796 | ||
797 | store->wakeup_data_sync_shards(source_zone, updated_shards); | |
798 | ||
799 | http_ret = 0; | |
800 | } | |
801 | ||
802 | void RGWOp_DATALog_Delete::execute() { | |
803 | string st = s->info.args.get("start-time"), | |
804 | et = s->info.args.get("end-time"), | |
805 | start_marker = s->info.args.get("start-marker"), | |
806 | end_marker = s->info.args.get("end-marker"), | |
807 | shard = s->info.args.get("id"), | |
808 | err; | |
809 | real_time ut_st, | |
810 | ut_et; | |
811 | unsigned shard_id; | |
812 | ||
813 | http_ret = 0; | |
814 | ||
815 | shard_id = (unsigned)strict_strtol(shard.c_str(), 10, &err); | |
816 | if (!err.empty()) { | |
817 | dout(5) << "Error parsing shard_id " << shard << dendl; | |
818 | http_ret = -EINVAL; | |
819 | return; | |
820 | } | |
821 | if (et.empty() && end_marker.empty()) { /* bounding end */ | |
822 | http_ret = -EINVAL; | |
823 | return; | |
824 | } | |
825 | ||
826 | if (parse_date_str(st, ut_st) < 0) { | |
827 | http_ret = -EINVAL; | |
828 | return; | |
829 | } | |
830 | ||
831 | if (parse_date_str(et, ut_et) < 0) { | |
832 | http_ret = -EINVAL; | |
833 | return; | |
834 | } | |
835 | ||
836 | http_ret = store->data_log->trim_entries(shard_id, ut_st, ut_et, start_marker, end_marker); | |
837 | } | |
838 | ||
839 | // not in header to avoid pulling in rgw_sync.h | |
840 | class RGWOp_MDLog_Status : public RGWRESTOp { | |
841 | rgw_meta_sync_status status; | |
842 | public: | |
843 | int check_caps(RGWUserCaps& caps) override { | |
844 | return caps.check_cap("mdlog", RGW_CAP_READ); | |
845 | } | |
846 | int verify_permission() override { | |
847 | return check_caps(s->user->caps); | |
848 | } | |
849 | void execute() override; | |
850 | void send_response() override; | |
11fdf7f2 | 851 | const char* name() const override { return "get_metadata_log_status"; } |
7c673cae FG |
852 | }; |
853 | ||
854 | void RGWOp_MDLog_Status::execute() | |
855 | { | |
856 | auto sync = store->get_meta_sync_manager(); | |
857 | if (sync == nullptr) { | |
858 | ldout(s->cct, 1) << "no sync manager" << dendl; | |
859 | http_ret = -ENOENT; | |
860 | return; | |
861 | } | |
862 | http_ret = sync->read_sync_status(&status); | |
863 | } | |
864 | ||
865 | void RGWOp_MDLog_Status::send_response() | |
866 | { | |
867 | set_req_state_err(s, http_ret); | |
868 | dump_errno(s); | |
869 | end_header(s); | |
870 | ||
871 | if (http_ret >= 0) { | |
872 | encode_json("status", status, s->formatter); | |
873 | } | |
874 | flusher.flush(); | |
875 | } | |
876 | ||
b32b8144 FG |
877 | // not in header to avoid pulling in rgw_data_sync.h |
878 | class RGWOp_BILog_Status : public RGWRESTOp { | |
879 | std::vector<rgw_bucket_shard_sync_info> status; | |
880 | public: | |
881 | int check_caps(RGWUserCaps& caps) override { | |
882 | return caps.check_cap("bilog", RGW_CAP_READ); | |
883 | } | |
884 | int verify_permission() override { | |
885 | return check_caps(s->user->caps); | |
886 | } | |
887 | void execute() override; | |
888 | void send_response() override; | |
11fdf7f2 | 889 | const char* name() const override { return "get_bucket_index_log_status"; } |
b32b8144 FG |
890 | }; |
891 | ||
892 | void RGWOp_BILog_Status::execute() | |
893 | { | |
894 | const auto source_zone = s->info.args.get("source-zone"); | |
895 | const auto key = s->info.args.get("bucket"); | |
896 | if (key.empty()) { | |
897 | ldout(s->cct, 4) << "no 'bucket' provided" << dendl; | |
898 | http_ret = -EINVAL; | |
899 | return; | |
900 | } | |
901 | ||
902 | rgw_bucket bucket; | |
903 | int shard_id{-1}; // unused | |
904 | http_ret = rgw_bucket_parse_bucket_key(s->cct, key, &bucket, &shard_id); | |
905 | if (http_ret < 0) { | |
906 | ldout(s->cct, 4) << "no 'bucket' provided" << dendl; | |
907 | http_ret = -EINVAL; | |
908 | return; | |
909 | } | |
910 | ||
28e407b8 | 911 | // read the bucket instance info for num_shards |
11fdf7f2 | 912 | auto ctx = store->svc.sysobj->init_obj_ctx(); |
28e407b8 AA |
913 | RGWBucketInfo info; |
914 | http_ret = store->get_bucket_instance_info(ctx, bucket, info, nullptr, nullptr); | |
915 | if (http_ret < 0) { | |
916 | ldout(s->cct, 4) << "failed to read bucket info: " << cpp_strerror(http_ret) << dendl; | |
917 | return; | |
918 | } | |
11fdf7f2 | 919 | http_ret = rgw_bucket_sync_status(this, store, source_zone, info, &status); |
b32b8144 FG |
920 | } |
921 | ||
922 | void RGWOp_BILog_Status::send_response() | |
923 | { | |
924 | set_req_state_err(s, http_ret); | |
925 | dump_errno(s); | |
926 | end_header(s); | |
927 | ||
928 | if (http_ret >= 0) { | |
929 | encode_json("status", status, s->formatter); | |
930 | } | |
931 | flusher.flush(); | |
932 | } | |
933 | ||
7c673cae FG |
934 | // not in header to avoid pulling in rgw_data_sync.h |
935 | class RGWOp_DATALog_Status : public RGWRESTOp { | |
936 | rgw_data_sync_status status; | |
937 | public: | |
938 | int check_caps(RGWUserCaps& caps) override { | |
939 | return caps.check_cap("datalog", RGW_CAP_READ); | |
940 | } | |
941 | int verify_permission() override { | |
942 | return check_caps(s->user->caps); | |
943 | } | |
944 | void execute() override ; | |
945 | void send_response() override; | |
11fdf7f2 | 946 | const char* name() const override { return "get_data_changes_log_status"; } |
7c673cae FG |
947 | }; |
948 | ||
949 | void RGWOp_DATALog_Status::execute() | |
950 | { | |
951 | const auto source_zone = s->info.args.get("source-zone"); | |
952 | auto sync = store->get_data_sync_manager(source_zone); | |
953 | if (sync == nullptr) { | |
954 | ldout(s->cct, 1) << "no sync manager for source-zone " << source_zone << dendl; | |
955 | http_ret = -ENOENT; | |
956 | return; | |
957 | } | |
958 | http_ret = sync->read_sync_status(&status); | |
959 | } | |
960 | ||
961 | void RGWOp_DATALog_Status::send_response() | |
962 | { | |
963 | set_req_state_err(s, http_ret); | |
964 | dump_errno(s); | |
965 | end_header(s); | |
966 | ||
967 | if (http_ret >= 0) { | |
968 | encode_json("status", status, s->formatter); | |
969 | } | |
970 | flusher.flush(); | |
971 | } | |
972 | ||
973 | ||
974 | RGWOp *RGWHandler_Log::op_get() { | |
975 | bool exists; | |
976 | string type = s->info.args.get("type", &exists); | |
977 | ||
978 | if (!exists) { | |
979 | return NULL; | |
980 | } | |
981 | ||
982 | if (type.compare("metadata") == 0) { | |
983 | if (s->info.args.exists("id")) { | |
984 | if (s->info.args.exists("info")) { | |
985 | return new RGWOp_MDLog_ShardInfo; | |
986 | } else { | |
987 | return new RGWOp_MDLog_List; | |
988 | } | |
989 | } else if (s->info.args.exists("status")) { | |
990 | return new RGWOp_MDLog_Status; | |
991 | } else { | |
992 | return new RGWOp_MDLog_Info; | |
993 | } | |
994 | } else if (type.compare("bucket-index") == 0) { | |
995 | if (s->info.args.exists("info")) { | |
996 | return new RGWOp_BILog_Info; | |
b32b8144 FG |
997 | } else if (s->info.args.exists("status")) { |
998 | return new RGWOp_BILog_Status; | |
7c673cae FG |
999 | } else { |
1000 | return new RGWOp_BILog_List; | |
1001 | } | |
1002 | } else if (type.compare("data") == 0) { | |
1003 | if (s->info.args.exists("id")) { | |
1004 | if (s->info.args.exists("info")) { | |
1005 | return new RGWOp_DATALog_ShardInfo; | |
1006 | } else { | |
1007 | return new RGWOp_DATALog_List; | |
1008 | } | |
1009 | } else if (s->info.args.exists("status")) { | |
1010 | return new RGWOp_DATALog_Status; | |
1011 | } else { | |
1012 | return new RGWOp_DATALog_Info; | |
1013 | } | |
1014 | } | |
1015 | return NULL; | |
1016 | } | |
1017 | ||
1018 | RGWOp *RGWHandler_Log::op_delete() { | |
1019 | bool exists; | |
1020 | string type = s->info.args.get("type", &exists); | |
1021 | ||
1022 | if (!exists) { | |
1023 | return NULL; | |
1024 | } | |
1025 | ||
1026 | if (type.compare("metadata") == 0) | |
1027 | return new RGWOp_MDLog_Delete; | |
1028 | else if (type.compare("bucket-index") == 0) | |
1029 | return new RGWOp_BILog_Delete; | |
1030 | else if (type.compare("data") == 0) | |
1031 | return new RGWOp_DATALog_Delete; | |
1032 | return NULL; | |
1033 | } | |
1034 | ||
1035 | RGWOp *RGWHandler_Log::op_post() { | |
1036 | bool exists; | |
1037 | string type = s->info.args.get("type", &exists); | |
1038 | ||
1039 | if (!exists) { | |
1040 | return NULL; | |
1041 | } | |
1042 | ||
1043 | if (type.compare("metadata") == 0) { | |
1044 | if (s->info.args.exists("lock")) | |
1045 | return new RGWOp_MDLog_Lock; | |
1046 | else if (s->info.args.exists("unlock")) | |
1047 | return new RGWOp_MDLog_Unlock; | |
1048 | else if (s->info.args.exists("notify")) | |
1049 | return new RGWOp_MDLog_Notify; | |
1050 | } else if (type.compare("data") == 0) { | |
1051 | if (s->info.args.exists("lock")) | |
1052 | return new RGWOp_DATALog_Lock; | |
1053 | else if (s->info.args.exists("unlock")) | |
1054 | return new RGWOp_DATALog_Unlock; | |
1055 | else if (s->info.args.exists("notify")) | |
1056 | return new RGWOp_DATALog_Notify; | |
1057 | } | |
1058 | return NULL; | |
1059 | } | |
1060 |