]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/cls_rgw/test_cls_rgw.cc
update sources to v12.1.0
[ceph.git] / ceph / src / test / cls_rgw / test_cls_rgw.cc
CommitLineData
7c673cae
FG
1// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "include/types.h"
5#include "cls/rgw/cls_rgw_client.h"
6#include "cls/rgw/cls_rgw_ops.h"
7
8#include "gtest/gtest.h"
9#include "test/librados/test.h"
10
11#include <errno.h>
12#include <string>
13#include <vector>
14#include <map>
31f18b77 15#include <set>
7c673cae
FG
16
17using namespace librados;
18
19librados::Rados rados;
20librados::IoCtx ioctx;
21string pool_name;
22
23
24/* must be the first test! */
25TEST(cls_rgw, init)
26{
27 pool_name = get_temp_pool_name();
28 /* create pool */
29 ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
30 ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
31}
32
33
34string str_int(string s, int i)
35{
36 char buf[32];
37 snprintf(buf, sizeof(buf), "-%d", i);
38 s.append(buf);
39
40 return s;
41}
42
43
44class OpMgr {
45 vector<ObjectOperation *> ops;
46
47public:
48 OpMgr() {}
49 ~OpMgr() {
50 vector<ObjectOperation *>::iterator iter;
51 for (iter = ops.begin(); iter != ops.end(); ++iter) {
52 ObjectOperation *op = *iter;
53 delete op;
54 }
55 }
56
57 ObjectReadOperation *read_op() {
58 ObjectReadOperation *op = new ObjectReadOperation;
59 ops.push_back(op);
60 return op;
61 }
62
63 ObjectWriteOperation *write_op() {
64 ObjectWriteOperation *op = new ObjectWriteOperation;
65 ops.push_back(op);
66 return op;
67 }
68};
69
70void test_stats(librados::IoCtx& ioctx, string& oid, int category, uint64_t num_entries, uint64_t total_size)
71{
72 map<int, struct rgw_cls_list_ret> results;
73 map<int, string> oids;
74 oids[0] = oid;
75 ASSERT_EQ(0, CLSRGWIssueGetDirHeader(ioctx, oids, results, 8)());
76
77 uint64_t entries = 0;
78 uint64_t size = 0;
79 map<int, struct rgw_cls_list_ret>::iterator iter = results.begin();
80 for (; iter != results.end(); ++iter) {
81 entries += (iter->second).dir.header.stats[category].num_entries;
82 size += (iter->second).dir.header.stats[category].total_size;
83 }
84 ASSERT_EQ(total_size, size);
85 ASSERT_EQ(num_entries, entries);
86}
87
88void index_prepare(OpMgr& mgr, librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op, string& tag, string& obj, string& loc)
89{
90 ObjectWriteOperation *op = mgr.write_op();
91 cls_rgw_obj_key key(obj, string());
31f18b77
FG
92 rgw_zone_set zones_trace;
93 cls_rgw_bucket_prepare_op(*op, index_op, tag, key, loc, true, 0, zones_trace);
7c673cae
FG
94 ASSERT_EQ(0, ioctx.operate(oid, op));
95}
96
97void index_complete(OpMgr& mgr, librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op, string& tag, int epoch, string& obj, rgw_bucket_dir_entry_meta& meta)
98{
99 ObjectWriteOperation *op = mgr.write_op();
100 cls_rgw_obj_key key(obj, string());
101 rgw_bucket_entry_ver ver;
102 ver.pool = ioctx.get_id();
103 ver.epoch = epoch;
104 meta.accounted_size = meta.size;
31f18b77 105 cls_rgw_bucket_complete_op(*op, index_op, tag, ver, key, meta, nullptr, true, 0, nullptr);
7c673cae
FG
106 ASSERT_EQ(0, ioctx.operate(oid, op));
107}
108
109TEST(cls_rgw, index_basic)
110{
111 string bucket_oid = str_int("bucket", 0);
112
113 OpMgr mgr;
114
115 ObjectWriteOperation *op = mgr.write_op();
116 cls_rgw_bucket_init(*op);
117 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
118
119 uint64_t epoch = 1;
120
121 uint64_t obj_size = 1024;
122
123#define NUM_OBJS 10
124 for (int i = 0; i < NUM_OBJS; i++) {
125 string obj = str_int("obj", i);
126 string tag = str_int("tag", i);
127 string loc = str_int("loc", i);
128
129 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
130
131 test_stats(ioctx, bucket_oid, 0, i, obj_size * i);
132
133 op = mgr.write_op();
134 rgw_bucket_dir_entry_meta meta;
135 meta.category = 0;
136 meta.size = obj_size;
137 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta);
138 }
139
140 test_stats(ioctx, bucket_oid, 0, NUM_OBJS, obj_size * NUM_OBJS);
141}
142
143TEST(cls_rgw, index_multiple_obj_writers)
144{
145 string bucket_oid = str_int("bucket", 1);
146
147 OpMgr mgr;
148
149 ObjectWriteOperation *op = mgr.write_op();
150 cls_rgw_bucket_init(*op);
151 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
152
153 uint64_t obj_size = 1024;
154
155 string obj = str_int("obj", 0);
156 string loc = str_int("loc", 0);
157 /* multi prepare on a single object */
158 for (int i = 0; i < NUM_OBJS; i++) {
159 string tag = str_int("tag", i);
160
161 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
162
163 test_stats(ioctx, bucket_oid, 0, 0, 0);
164 }
165
166 for (int i = NUM_OBJS; i > 0; i--) {
167 string tag = str_int("tag", i - 1);
168
169 rgw_bucket_dir_entry_meta meta;
170 meta.category = 0;
171 meta.size = obj_size * i;
172
173 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, i, obj, meta);
174
175 /* verify that object size doesn't change, as we went back with epoch */
176 test_stats(ioctx, bucket_oid, 0, 1, obj_size * NUM_OBJS);
177 }
178}
179
180TEST(cls_rgw, index_remove_object)
181{
182 string bucket_oid = str_int("bucket", 2);
183
184 OpMgr mgr;
185
186 ObjectWriteOperation *op = mgr.write_op();
187 cls_rgw_bucket_init(*op);
188 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
189
190 uint64_t obj_size = 1024;
191 uint64_t total_size = 0;
192
193 int epoch = 0;
194
195 /* prepare multiple objects */
196 for (int i = 0; i < NUM_OBJS; i++) {
197 string obj = str_int("obj", i);
198 string tag = str_int("tag", i);
199 string loc = str_int("loc", i);
200
201 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
202
203 test_stats(ioctx, bucket_oid, 0, i, total_size);
204
205 rgw_bucket_dir_entry_meta meta;
206 meta.category = 0;
207 meta.size = i * obj_size;
208 total_size += i * obj_size;
209
210 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
211
212 test_stats(ioctx, bucket_oid, 0, i + 1, total_size);
213 }
214
215 int i = NUM_OBJS / 2;
216 string tag_remove = "tag-rm";
217 string tag_modify = "tag-mod";
218 string obj = str_int("obj", i);
219 string loc = str_int("loc", i);
220
221 /* prepare both removal and modification on the same object */
222 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
223 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, obj, loc);
224
225 test_stats(ioctx, bucket_oid, 0, NUM_OBJS, total_size);
226
227 rgw_bucket_dir_entry_meta meta;
228
229 /* complete object removal */
230 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
231
232 /* verify stats correct */
233 total_size -= i * obj_size;
234 test_stats(ioctx, bucket_oid, 0, NUM_OBJS - 1, total_size);
235
236 meta.size = 512;
237 meta.category = 0;
238
239 /* complete object modification */
240 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
241
242 /* verify stats correct */
243 total_size += meta.size;
244 test_stats(ioctx, bucket_oid, 0, NUM_OBJS, total_size);
245
246
247 /* prepare both removal and modification on the same object, this time we'll
248 * first complete modification then remove*/
249 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
250 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_modify, obj, loc);
251
252 /* complete modification */
253 total_size -= meta.size;
254 meta.size = i * obj_size * 2;
255 meta.category = 0;
256
257 /* complete object modification */
258 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
259
260 /* verify stats correct */
261 total_size += meta.size;
262 test_stats(ioctx, bucket_oid, 0, NUM_OBJS, total_size);
263
264 /* complete object removal */
265 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
266
267 /* verify stats correct */
268 total_size -= meta.size;
269 test_stats(ioctx, bucket_oid, 0, NUM_OBJS - 1, total_size);
270}
271
272TEST(cls_rgw, index_suggest)
273{
274 string bucket_oid = str_int("bucket", 3);
275
276 OpMgr mgr;
277
278 ObjectWriteOperation *op = mgr.write_op();
279 cls_rgw_bucket_init(*op);
280 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
281
282 uint64_t total_size = 0;
283
284 int epoch = 0;
285
286 int num_objs = 100;
287
288 uint64_t obj_size = 1024;
289
290 /* create multiple objects */
291 for (int i = 0; i < num_objs; i++) {
292 string obj = str_int("obj", i);
293 string tag = str_int("tag", i);
294 string loc = str_int("loc", i);
295
296 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
297
298 test_stats(ioctx, bucket_oid, 0, i, total_size);
299
300 rgw_bucket_dir_entry_meta meta;
301 meta.category = 0;
302 meta.size = obj_size;
303 total_size += meta.size;
304
305 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
306
307 test_stats(ioctx, bucket_oid, 0, i + 1, total_size);
308 }
309
310 /* prepare (without completion) some of the objects */
311 for (int i = 0; i < num_objs; i += 2) {
312 string obj = str_int("obj", i);
313 string tag = str_int("tag-prepare", i);
314 string loc = str_int("loc", i);
315
316 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
317
318 test_stats(ioctx, bucket_oid, 0, num_objs, total_size);
319 }
320
321 int actual_num_objs = num_objs;
322 /* remove half of the objects */
323 for (int i = num_objs / 2; i < num_objs; i++) {
324 string obj = str_int("obj", i);
325 string tag = str_int("tag-rm", i);
326 string loc = str_int("loc", i);
327
328 index_prepare(mgr, ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
329
330 test_stats(ioctx, bucket_oid, 0, actual_num_objs, total_size);
331
332 rgw_bucket_dir_entry_meta meta;
333 index_complete(mgr, ioctx, bucket_oid, CLS_RGW_OP_DEL, tag, ++epoch, obj, meta);
334
335 total_size -= obj_size;
336 actual_num_objs--;
337 test_stats(ioctx, bucket_oid, 0, actual_num_objs, total_size);
338 }
339
340 bufferlist updates;
341
342 for (int i = 0; i < num_objs; i += 2) {
343 string obj = str_int("obj", i);
344 string tag = str_int("tag-rm", i);
345 string loc = str_int("loc", i);
346
347 rgw_bucket_dir_entry dirent;
348 dirent.key.name = obj;
349 dirent.locator = loc;
350 dirent.exists = (i < num_objs / 2); // we removed half the objects
351 dirent.meta.size = 1024;
352 dirent.meta.accounted_size = 1024;
353
354 char suggest_op = (i < num_objs / 2 ? CEPH_RGW_UPDATE : CEPH_RGW_REMOVE);
355 cls_rgw_encode_suggestion(suggest_op, dirent, updates);
356 }
357
358 map<int, string> bucket_objs;
359 bucket_objs[0] = bucket_oid;
360 int r = CLSRGWIssueSetTagTimeout(ioctx, bucket_objs, 8 /* max aio */, 1)();
361 ASSERT_EQ(0, r);
362
363 sleep(1);
364
365 /* suggest changes! */
366 op = mgr.write_op();
367 cls_rgw_suggest_changes(*op, updates);
368 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
369
370 /* suggest changes twice! */
371 op = mgr.write_op();
372 cls_rgw_suggest_changes(*op, updates);
373 ASSERT_EQ(0, ioctx.operate(bucket_oid, op));
374
375 test_stats(ioctx, bucket_oid, 0, num_objs / 2, total_size);
376}
377
378/* test garbage collection */
379static void create_obj(cls_rgw_obj& obj, int i, int j)
380{
381 char buf[32];
382 snprintf(buf, sizeof(buf), "-%d.%d", i, j);
383 obj.pool = "pool";
384 obj.pool.append(buf);
385 obj.key.name = "oid";
386 obj.key.name.append(buf);
387 obj.loc = "loc";
388 obj.loc.append(buf);
389}
390
391static bool cmp_objs(cls_rgw_obj& obj1, cls_rgw_obj& obj2)
392{
393 return (obj1.pool == obj2.pool) &&
394 (obj1.key == obj2.key) &&
395 (obj1.loc == obj2.loc);
396}
397
398
399TEST(cls_rgw, gc_set)
400{
401 /* add chains */
402 string oid = "obj";
403 for (int i = 0; i < 10; i++) {
404 char buf[32];
405 snprintf(buf, sizeof(buf), "chain-%d", i);
406 string tag = buf;
407 librados::ObjectWriteOperation op;
408 cls_rgw_gc_obj_info info;
409
410 cls_rgw_obj obj1, obj2;
411 create_obj(obj1, i, 1);
412 create_obj(obj2, i, 2);
413 info.chain.objs.push_back(obj1);
414 info.chain.objs.push_back(obj2);
415
416 op.create(false); // create object
417
418 info.tag = tag;
419 cls_rgw_gc_set_entry(op, 0, info);
420
421 ASSERT_EQ(0, ioctx.operate(oid, &op));
422 }
423
424 bool truncated;
425 list<cls_rgw_gc_obj_info> entries;
426 string marker;
31f18b77 427 string next_marker;
7c673cae
FG
428
429 /* list chains, verify truncated */
31f18b77 430 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
7c673cae
FG
431 ASSERT_EQ(8, (int)entries.size());
432 ASSERT_EQ(1, truncated);
433
434 entries.clear();
31f18b77 435 next_marker.clear();
7c673cae
FG
436
437 /* list all chains, verify not truncated */
31f18b77 438 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 10, true, entries, &truncated, next_marker));
7c673cae
FG
439 ASSERT_EQ(10, (int)entries.size());
440 ASSERT_EQ(0, truncated);
441
442 /* verify all chains are valid */
443 list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
444 for (int i = 0; i < 10; i++, ++iter) {
445 cls_rgw_gc_obj_info& entry = *iter;
446
447 /* create expected chain name */
448 char buf[32];
449 snprintf(buf, sizeof(buf), "chain-%d", i);
450 string tag = buf;
451
452 /* verify chain name as expected */
453 ASSERT_EQ(entry.tag, tag);
454
455 /* verify expected num of objects in chain */
456 ASSERT_EQ(2, (int)entry.chain.objs.size());
457
458 list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
459 cls_rgw_obj obj1, obj2;
460
461 /* create expected objects */
462 create_obj(obj1, i, 1);
463 create_obj(obj2, i, 2);
464
465 /* assign returned object names */
466 cls_rgw_obj& ret_obj1 = *oiter++;
467 cls_rgw_obj& ret_obj2 = *oiter;
468
469 /* verify objects are as expected */
470 ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
471 ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
472 }
473}
474
31f18b77
FG
475TEST(cls_rgw, gc_list)
476{
477 /* add chains */
478 string oid = "obj";
479 for (int i = 0; i < 10; i++) {
480 char buf[32];
481 snprintf(buf, sizeof(buf), "chain-%d", i);
482 string tag = buf;
483 librados::ObjectWriteOperation op;
484 cls_rgw_gc_obj_info info;
485
486 cls_rgw_obj obj1, obj2;
487 create_obj(obj1, i, 1);
488 create_obj(obj2, i, 2);
489 info.chain.objs.push_back(obj1);
490 info.chain.objs.push_back(obj2);
491
492 op.create(false); // create object
493
494 info.tag = tag;
495 cls_rgw_gc_set_entry(op, 0, info);
496
497 ASSERT_EQ(0, ioctx.operate(oid, &op));
498 }
499
500 bool truncated;
501 list<cls_rgw_gc_obj_info> entries;
502 list<cls_rgw_gc_obj_info> entries2;
503 string marker;
504 string next_marker;
505
506 /* list chains, verify truncated */
507 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
508 ASSERT_EQ(8, (int)entries.size());
509 ASSERT_EQ(1, truncated);
510
511 marker = next_marker;
512 next_marker.clear();
513
514 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries2, &truncated, next_marker));
515 ASSERT_EQ(2, (int)entries2.size());
516 ASSERT_EQ(0, truncated);
517
518 entries.splice(entries.end(), entries2);
519
520 /* verify all chains are valid */
521 list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
522 for (int i = 0; i < 10; i++, ++iter) {
523 cls_rgw_gc_obj_info& entry = *iter;
524
525 /* create expected chain name */
526 char buf[32];
527 snprintf(buf, sizeof(buf), "chain-%d", i);
528 string tag = buf;
529
530 /* verify chain name as expected */
531 ASSERT_EQ(entry.tag, tag);
532
533 /* verify expected num of objects in chain */
534 ASSERT_EQ(2, (int)entry.chain.objs.size());
535
536 list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
537 cls_rgw_obj obj1, obj2;
538
539 /* create expected objects */
540 create_obj(obj1, i, 1);
541 create_obj(obj2, i, 2);
542
543 /* assign returned object names */
544 cls_rgw_obj& ret_obj1 = *oiter++;
545 cls_rgw_obj& ret_obj2 = *oiter;
546
547 /* verify objects are as expected */
548 ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
549 ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
550 }
551}
552
7c673cae
FG
553TEST(cls_rgw, gc_defer)
554{
555 librados::IoCtx ioctx;
556 librados::Rados rados;
557
558 string gc_pool_name = get_temp_pool_name();
559 /* create pool */
560 ASSERT_EQ("", create_one_pool_pp(gc_pool_name, rados));
561 ASSERT_EQ(0, rados.ioctx_create(gc_pool_name.c_str(), ioctx));
562
563 string oid = "obj";
564 string tag = "mychain";
565
566 librados::ObjectWriteOperation op;
567 cls_rgw_gc_obj_info info;
568
569 op.create(false);
570
571 info.tag = tag;
572
573 /* create chain */
574 cls_rgw_gc_set_entry(op, 0, info);
575
576 ASSERT_EQ(0, ioctx.operate(oid, &op));
577
578 bool truncated;
579 list<cls_rgw_gc_obj_info> entries;
580 string marker;
31f18b77 581 string next_marker;
7c673cae
FG
582
583 /* list chains, verify num entries as expected */
31f18b77 584 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
585 ASSERT_EQ(1, (int)entries.size());
586 ASSERT_EQ(0, truncated);
587
588 librados::ObjectWriteOperation op2;
589
590 /* defer chain */
591 cls_rgw_gc_defer_entry(op2, 5, tag);
592 ASSERT_EQ(0, ioctx.operate(oid, &op2));
593
594 entries.clear();
31f18b77 595 next_marker.clear();
7c673cae
FG
596
597 /* verify list doesn't show deferred entry (this may fail if cluster is thrashing) */
31f18b77 598 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
599 ASSERT_EQ(0, (int)entries.size());
600 ASSERT_EQ(0, truncated);
601
602 /* wait enough */
603 sleep(5);
31f18b77 604 next_marker.clear();
7c673cae
FG
605
606 /* verify list shows deferred entry */
31f18b77 607 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
608 ASSERT_EQ(1, (int)entries.size());
609 ASSERT_EQ(0, truncated);
610
611 librados::ObjectWriteOperation op3;
612 list<string> tags;
613 tags.push_back(tag);
614
615 /* remove chain */
616 cls_rgw_gc_remove(op3, tags);
617 ASSERT_EQ(0, ioctx.operate(oid, &op3));
618
619 entries.clear();
31f18b77 620 next_marker.clear();
7c673cae
FG
621
622 /* verify entry was removed */
31f18b77 623 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
624 ASSERT_EQ(0, (int)entries.size());
625 ASSERT_EQ(0, truncated);
626
627 /* remove pool */
628 ioctx.close();
629 ASSERT_EQ(0, destroy_one_pool_pp(gc_pool_name, rados));
630}
631
632
633/* must be last test! */
634
635TEST(cls_rgw, finalize)
636{
637 /* remove pool */
638 ioctx.close();
639 ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
640}