1 // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/types.h"
5 #include "cls/rgw/cls_rgw_client.h"
6 #include "cls/rgw/cls_rgw_ops.h"
8 #include "gtest/gtest.h"
9 #include "test/librados/test_cxx.h"
10 #include "global/global_context.h"
18 using namespace librados
;
20 librados::Rados rados
;
21 librados::IoCtx ioctx
;
25 /* must be the first test! */
28 pool_name
= get_temp_pool_name();
30 ASSERT_EQ("", create_one_pool_pp(pool_name
, rados
));
31 ASSERT_EQ(0, rados
.ioctx_create(pool_name
.c_str(), ioctx
));
35 string
str_int(string s
, int i
)
38 snprintf(buf
, sizeof(buf
), "-%d", i
);
46 vector
<ObjectOperation
*> ops
;
51 vector
<ObjectOperation
*>::iterator iter
;
52 for (iter
= ops
.begin(); iter
!= ops
.end(); ++iter
) {
53 ObjectOperation
*op
= *iter
;
58 ObjectReadOperation
*read_op() {
59 ObjectReadOperation
*op
= new ObjectReadOperation
;
64 ObjectWriteOperation
*write_op() {
65 ObjectWriteOperation
*op
= new ObjectWriteOperation
;
71 void test_stats(librados::IoCtx
& ioctx
, string
& oid
, RGWObjCategory category
, uint64_t num_entries
, uint64_t total_size
)
73 map
<int, struct rgw_cls_list_ret
> results
;
74 map
<int, string
> oids
;
76 ASSERT_EQ(0, CLSRGWIssueGetDirHeader(ioctx
, oids
, results
, 8)());
80 map
<int, struct rgw_cls_list_ret
>::iterator iter
= results
.begin();
81 for (; iter
!= results
.end(); ++iter
) {
82 entries
+= (iter
->second
).dir
.header
.stats
[category
].num_entries
;
83 size
+= (iter
->second
).dir
.header
.stats
[category
].total_size
;
85 ASSERT_EQ(total_size
, size
);
86 ASSERT_EQ(num_entries
, entries
);
89 void index_prepare(OpMgr
& mgr
, librados::IoCtx
& ioctx
, string
& oid
, RGWModifyOp index_op
, string
& tag
,
90 string
& obj
, string
& loc
, uint16_t bi_flags
= 0, bool log_op
= true)
92 ObjectWriteOperation
*op
= mgr
.write_op();
93 cls_rgw_obj_key
key(obj
, string());
94 rgw_zone_set zones_trace
;
95 cls_rgw_bucket_prepare_op(*op
, index_op
, tag
, key
, loc
, log_op
, bi_flags
, zones_trace
);
96 ASSERT_EQ(0, ioctx
.operate(oid
, op
));
99 void index_complete(OpMgr
& mgr
, librados::IoCtx
& ioctx
, string
& oid
, RGWModifyOp index_op
, string
& tag
,
100 int epoch
, string
& obj
, rgw_bucket_dir_entry_meta
& meta
, uint16_t bi_flags
= 0, bool log_op
= true)
102 ObjectWriteOperation
*op
= mgr
.write_op();
103 cls_rgw_obj_key
key(obj
, string());
104 rgw_bucket_entry_ver ver
;
105 ver
.pool
= ioctx
.get_id();
107 meta
.accounted_size
= meta
.size
;
108 cls_rgw_bucket_complete_op(*op
, index_op
, tag
, ver
, key
, meta
, nullptr, log_op
, bi_flags
, nullptr);
109 ASSERT_EQ(0, ioctx
.operate(oid
, op
));
112 TEST(cls_rgw
, index_basic
)
114 string bucket_oid
= str_int("bucket", 0);
118 ObjectWriteOperation
*op
= mgr
.write_op();
119 cls_rgw_bucket_init_index(*op
);
120 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
124 uint64_t obj_size
= 1024;
127 for (int i
= 0; i
< NUM_OBJS
; i
++) {
128 string obj
= str_int("obj", i
);
129 string tag
= str_int("tag", i
);
130 string loc
= str_int("loc", i
);
132 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
134 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, i
, obj_size
* i
);
137 rgw_bucket_dir_entry_meta meta
;
138 meta
.category
= RGWObjCategory::None
;
139 meta
.size
= obj_size
;
140 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, epoch
, obj
, meta
);
143 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
,
144 obj_size
* NUM_OBJS
);
147 TEST(cls_rgw
, index_multiple_obj_writers
)
149 string bucket_oid
= str_int("bucket", 1);
153 ObjectWriteOperation
*op
= mgr
.write_op();
154 cls_rgw_bucket_init_index(*op
);
155 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
157 uint64_t obj_size
= 1024;
159 string obj
= str_int("obj", 0);
160 string loc
= str_int("loc", 0);
161 /* multi prepare on a single object */
162 for (int i
= 0; i
< NUM_OBJS
; i
++) {
163 string tag
= str_int("tag", i
);
165 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
167 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, 0, 0);
170 for (int i
= NUM_OBJS
; i
> 0; i
--) {
171 string tag
= str_int("tag", i
- 1);
173 rgw_bucket_dir_entry_meta meta
;
174 meta
.category
= RGWObjCategory::None
;
175 meta
.size
= obj_size
* i
;
177 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, i
, obj
, meta
);
179 /* verify that object size doesn't change, as we went back with epoch */
180 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, 1,
181 obj_size
* NUM_OBJS
);
185 TEST(cls_rgw
, index_remove_object
)
187 string bucket_oid
= str_int("bucket", 2);
191 ObjectWriteOperation
*op
= mgr
.write_op();
192 cls_rgw_bucket_init_index(*op
);
193 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
195 uint64_t obj_size
= 1024;
196 uint64_t total_size
= 0;
200 /* prepare multiple objects */
201 for (int i
= 0; i
< NUM_OBJS
; i
++) {
202 string obj
= str_int("obj", i
);
203 string tag
= str_int("tag", i
);
204 string loc
= str_int("loc", i
);
206 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
208 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, i
, total_size
);
210 rgw_bucket_dir_entry_meta meta
;
211 meta
.category
= RGWObjCategory::None
;
212 meta
.size
= i
* obj_size
;
213 total_size
+= i
* obj_size
;
215 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, ++epoch
, obj
, meta
);
217 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, i
+ 1, total_size
);
220 int i
= NUM_OBJS
/ 2;
221 string tag_remove
= "tag-rm";
222 string tag_modify
= "tag-mod";
223 string obj
= str_int("obj", i
);
224 string loc
= str_int("loc", i
);
226 /* prepare both removal and modification on the same object */
227 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag_remove
, obj
, loc
);
228 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag_modify
, obj
, loc
);
230 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
, total_size
);
232 rgw_bucket_dir_entry_meta meta
;
234 /* complete object removal */
235 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag_remove
, ++epoch
, obj
, meta
);
237 /* verify stats correct */
238 total_size
-= i
* obj_size
;
239 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
- 1, total_size
);
242 meta
.category
= RGWObjCategory::None
;
244 /* complete object modification */
245 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag_modify
, ++epoch
, obj
, meta
);
247 /* verify stats correct */
248 total_size
+= meta
.size
;
249 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
, total_size
);
252 /* prepare both removal and modification on the same object, this time we'll
253 * first complete modification then remove*/
254 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag_remove
, obj
, loc
);
255 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag_modify
, obj
, loc
);
257 /* complete modification */
258 total_size
-= meta
.size
;
259 meta
.size
= i
* obj_size
* 2;
260 meta
.category
= RGWObjCategory::None
;
262 /* complete object modification */
263 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag_modify
, ++epoch
, obj
, meta
);
265 /* verify stats correct */
266 total_size
+= meta
.size
;
267 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
, total_size
);
269 /* complete object removal */
270 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag_remove
, ++epoch
, obj
, meta
);
272 /* verify stats correct */
273 total_size
-= meta
.size
;
274 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, NUM_OBJS
- 1,
278 TEST(cls_rgw
, index_suggest
)
280 string bucket_oid
= str_int("bucket", 3);
284 ObjectWriteOperation
*op
= mgr
.write_op();
285 cls_rgw_bucket_init_index(*op
);
286 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
288 uint64_t total_size
= 0;
294 uint64_t obj_size
= 1024;
296 /* create multiple objects */
297 for (int i
= 0; i
< num_objs
; i
++) {
298 string obj
= str_int("obj", i
);
299 string tag
= str_int("tag", i
);
300 string loc
= str_int("loc", i
);
302 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
304 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, i
, total_size
);
306 rgw_bucket_dir_entry_meta meta
;
307 meta
.category
= RGWObjCategory::None
;
308 meta
.size
= obj_size
;
309 total_size
+= meta
.size
;
311 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, ++epoch
, obj
, meta
);
313 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, i
+ 1, total_size
);
316 /* prepare (without completion) some of the objects */
317 for (int i
= 0; i
< num_objs
; i
+= 2) {
318 string obj
= str_int("obj", i
);
319 string tag
= str_int("tag-prepare", i
);
320 string loc
= str_int("loc", i
);
322 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
324 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, num_objs
, total_size
);
327 int actual_num_objs
= num_objs
;
328 /* remove half of the objects */
329 for (int i
= num_objs
/ 2; i
< num_objs
; i
++) {
330 string obj
= str_int("obj", i
);
331 string tag
= str_int("tag-rm", i
);
332 string loc
= str_int("loc", i
);
334 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
);
336 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, actual_num_objs
, total_size
);
338 rgw_bucket_dir_entry_meta meta
;
339 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_DEL
, tag
, ++epoch
, obj
, meta
);
341 total_size
-= obj_size
;
343 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, actual_num_objs
, total_size
);
348 for (int i
= 0; i
< num_objs
; i
+= 2) {
349 string obj
= str_int("obj", i
);
350 string tag
= str_int("tag-rm", i
);
351 string loc
= str_int("loc", i
);
353 rgw_bucket_dir_entry dirent
;
354 dirent
.key
.name
= obj
;
355 dirent
.locator
= loc
;
356 dirent
.exists
= (i
< num_objs
/ 2); // we removed half the objects
357 dirent
.meta
.size
= 1024;
358 dirent
.meta
.accounted_size
= 1024;
360 char suggest_op
= (i
< num_objs
/ 2 ? CEPH_RGW_UPDATE
: CEPH_RGW_REMOVE
);
361 cls_rgw_encode_suggestion(suggest_op
, dirent
, updates
);
364 map
<int, string
> bucket_objs
;
365 bucket_objs
[0] = bucket_oid
;
366 int r
= CLSRGWIssueSetTagTimeout(ioctx
, bucket_objs
, 8 /* max aio */, 1)();
371 /* suggest changes! */
373 cls_rgw_suggest_changes(*op
, updates
);
374 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
376 /* suggest changes twice! */
378 cls_rgw_suggest_changes(*op
, updates
);
379 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
381 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, num_objs
/ 2, total_size
);
385 * This case is used to test whether get_obj_vals will
386 * return all validate utf8 objnames and filter out those
387 * in BI_PREFIX_CHAR private namespace.
389 TEST(cls_rgw
, index_list
)
391 string bucket_oid
= str_int("bucket", 4);
395 ObjectWriteOperation
*op
= mgr
.write_op();
396 cls_rgw_bucket_init_index(*op
);
397 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
400 uint64_t obj_size
= 1024;
401 const int num_objs
= 4;
402 const string keys
[num_objs
] = {
403 /* single byte utf8 character */
404 { static_cast<char>(0x41) },
405 /* double byte utf8 character */
406 { static_cast<char>(0xCF), static_cast<char>(0x8F) },
407 /* treble byte utf8 character */
408 { static_cast<char>(0xDF), static_cast<char>(0x8F), static_cast<char>(0x8F) },
409 /* quadruble byte utf8 character */
410 { static_cast<char>(0xF7), static_cast<char>(0x8F), static_cast<char>(0x8F), static_cast<char>(0x8F) },
413 for (int i
= 0; i
< num_objs
; i
++) {
414 string obj
= keys
[i
];
415 string tag
= str_int("tag", i
);
416 string loc
= str_int("loc", i
);
418 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
, 0 /* bi_falgs */, false /* log_op */);
421 rgw_bucket_dir_entry_meta meta
;
422 meta
.category
= RGWObjCategory::None
;
423 meta
.size
= obj_size
;
424 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, epoch
, obj
, meta
, 0 /* bi_falgs */, false /* log_op */);
427 map
<string
, bufferlist
> entries
;
428 /* insert 998 omap key starts with BI_PREFIX_CHAR,
429 * so bucket list first time will get one key before 0x80 and one key after */
430 for (int i
= 0; i
< 998; ++i
) {
432 snprintf(buf
, sizeof(buf
), "%c%s%d", 0x80, "1000_", i
);
433 entries
.emplace(string
{buf
}, bufferlist
{});
435 ioctx
.omap_set(bucket_oid
, entries
);
437 test_stats(ioctx
, bucket_oid
, RGWObjCategory::None
, num_objs
, obj_size
* num_objs
);
439 map
<int, string
> oids
= { {0, bucket_oid
} };
440 map
<int, struct rgw_cls_list_ret
> list_results
;
441 cls_rgw_obj_key
start_key("", "");
442 int r
= CLSRGWIssueBucketList(ioctx
, start_key
, "", 1000, true, oids
, list_results
, 1)();
445 ASSERT_EQ(1u, list_results
.size());
447 auto it
= list_results
.begin();
448 auto m
= (it
->second
).dir
.m
;
450 ASSERT_EQ(4u, m
.size());
452 for(auto it2
= m
.begin(); it2
!= m
.end(); it2
++, i
++)
453 ASSERT_EQ(it2
->first
.compare(keys
[i
]), 0);
457 TEST(cls_rgw
, bi_list
)
459 string bucket_oid
= str_int("bucket", 5);
461 CephContext
*cct
= reinterpret_cast<CephContext
*>(ioctx
.cct());
465 ObjectWriteOperation
*op
= mgr
.write_op();
466 cls_rgw_bucket_init_index(*op
);
467 ASSERT_EQ(0, ioctx
.operate(bucket_oid
, op
));
472 list
<rgw_cls_bi_entry
> entries
;
475 int ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, max
, &entries
,
478 ASSERT_EQ(entries
.size(), 0u);
479 ASSERT_EQ(is_truncated
, false);
482 uint64_t obj_size
= 1024;
483 uint64_t num_objs
= 35;
485 for (uint64_t i
= 0; i
< num_objs
; i
++) {
486 string obj
= str_int("obj", i
);
487 string tag
= str_int("tag", i
);
488 string loc
= str_int("loc", i
);
489 index_prepare(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, obj
, loc
, RGW_BILOG_FLAG_VERSIONED_OP
);
491 rgw_bucket_dir_entry_meta meta
;
492 meta
.category
= RGWObjCategory::None
;
493 meta
.size
= obj_size
;
494 index_complete(mgr
, ioctx
, bucket_oid
, CLS_RGW_OP_ADD
, tag
, epoch
, obj
, meta
, RGW_BILOG_FLAG_VERSIONED_OP
);
497 ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, num_objs
+ 10, &entries
,
500 if (cct
->_conf
->osd_max_omap_entries_per_request
< num_objs
) {
501 ASSERT_EQ(entries
.size(), cct
->_conf
->osd_max_omap_entries_per_request
);
503 ASSERT_EQ(entries
.size(), num_objs
);
506 uint64_t num_entries
= 0;
509 while(is_truncated
) {
510 ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, max
, &entries
,
514 ASSERT_EQ(entries
.size(), std::min(max
, cct
->_conf
->osd_max_omap_entries_per_request
));
516 ASSERT_EQ(entries
.size(), num_objs
- num_entries
);
518 num_entries
+= entries
.size();
519 marker
= entries
.back().idx
;
522 ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, max
, &entries
,
525 ASSERT_EQ(entries
.size(), 0u);
526 ASSERT_EQ(is_truncated
, false);
528 if (cct
->_conf
->osd_max_omap_entries_per_request
< 15) {
533 while(is_truncated
) {
534 ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, max
, &entries
,
538 ASSERT_EQ(entries
.size(), cct
->_conf
->osd_max_omap_entries_per_request
);
540 ASSERT_EQ(entries
.size(), num_objs
- num_entries
);
542 num_entries
+= entries
.size();
543 marker
= entries
.back().idx
;
547 ret
= cls_rgw_bi_list(ioctx
, bucket_oid
, name
, marker
, max
, &entries
,
550 ASSERT_EQ(entries
.size(), 0u);
551 ASSERT_EQ(is_truncated
, false);
554 /* test garbage collection */
555 static void create_obj(cls_rgw_obj
& obj
, int i
, int j
)
558 snprintf(buf
, sizeof(buf
), "-%d.%d", i
, j
);
560 obj
.pool
.append(buf
);
561 obj
.key
.name
= "oid";
562 obj
.key
.name
.append(buf
);
567 static bool cmp_objs(cls_rgw_obj
& obj1
, cls_rgw_obj
& obj2
)
569 return (obj1
.pool
== obj2
.pool
) &&
570 (obj1
.key
== obj2
.key
) &&
571 (obj1
.loc
== obj2
.loc
);
575 TEST(cls_rgw
, gc_set
)
579 for (int i
= 0; i
< 10; i
++) {
581 snprintf(buf
, sizeof(buf
), "chain-%d", i
);
583 librados::ObjectWriteOperation op
;
584 cls_rgw_gc_obj_info info
;
586 cls_rgw_obj obj1
, obj2
;
587 create_obj(obj1
, i
, 1);
588 create_obj(obj2
, i
, 2);
589 info
.chain
.objs
.push_back(obj1
);
590 info
.chain
.objs
.push_back(obj2
);
592 op
.create(false); // create object
595 cls_rgw_gc_set_entry(op
, 0, info
);
597 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
601 list
<cls_rgw_gc_obj_info
> entries
;
605 /* list chains, verify truncated */
606 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 8, true, entries
, &truncated
, next_marker
));
607 ASSERT_EQ(8, (int)entries
.size());
608 ASSERT_EQ(1, truncated
);
613 /* list all chains, verify not truncated */
614 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 10, true, entries
, &truncated
, next_marker
));
615 ASSERT_EQ(10, (int)entries
.size());
616 ASSERT_EQ(0, truncated
);
618 /* verify all chains are valid */
619 list
<cls_rgw_gc_obj_info
>::iterator iter
= entries
.begin();
620 for (int i
= 0; i
< 10; i
++, ++iter
) {
621 cls_rgw_gc_obj_info
& entry
= *iter
;
623 /* create expected chain name */
625 snprintf(buf
, sizeof(buf
), "chain-%d", i
);
628 /* verify chain name as expected */
629 ASSERT_EQ(entry
.tag
, tag
);
631 /* verify expected num of objects in chain */
632 ASSERT_EQ(2, (int)entry
.chain
.objs
.size());
634 list
<cls_rgw_obj
>::iterator oiter
= entry
.chain
.objs
.begin();
635 cls_rgw_obj obj1
, obj2
;
637 /* create expected objects */
638 create_obj(obj1
, i
, 1);
639 create_obj(obj2
, i
, 2);
641 /* assign returned object names */
642 cls_rgw_obj
& ret_obj1
= *oiter
++;
643 cls_rgw_obj
& ret_obj2
= *oiter
;
645 /* verify objects are as expected */
646 ASSERT_EQ(1, (int)cmp_objs(obj1
, ret_obj1
));
647 ASSERT_EQ(1, (int)cmp_objs(obj2
, ret_obj2
));
651 TEST(cls_rgw
, gc_list
)
655 for (int i
= 0; i
< 10; i
++) {
657 snprintf(buf
, sizeof(buf
), "chain-%d", i
);
659 librados::ObjectWriteOperation op
;
660 cls_rgw_gc_obj_info info
;
662 cls_rgw_obj obj1
, obj2
;
663 create_obj(obj1
, i
, 1);
664 create_obj(obj2
, i
, 2);
665 info
.chain
.objs
.push_back(obj1
);
666 info
.chain
.objs
.push_back(obj2
);
668 op
.create(false); // create object
671 cls_rgw_gc_set_entry(op
, 0, info
);
673 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
677 list
<cls_rgw_gc_obj_info
> entries
;
678 list
<cls_rgw_gc_obj_info
> entries2
;
682 /* list chains, verify truncated */
683 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 8, true, entries
, &truncated
, next_marker
));
684 ASSERT_EQ(8, (int)entries
.size());
685 ASSERT_EQ(1, truncated
);
687 marker
= next_marker
;
690 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 8, true, entries2
, &truncated
, next_marker
));
691 ASSERT_EQ(2, (int)entries2
.size());
692 ASSERT_EQ(0, truncated
);
694 entries
.splice(entries
.end(), entries2
);
696 /* verify all chains are valid */
697 list
<cls_rgw_gc_obj_info
>::iterator iter
= entries
.begin();
698 for (int i
= 0; i
< 10; i
++, ++iter
) {
699 cls_rgw_gc_obj_info
& entry
= *iter
;
701 /* create expected chain name */
703 snprintf(buf
, sizeof(buf
), "chain-%d", i
);
706 /* verify chain name as expected */
707 ASSERT_EQ(entry
.tag
, tag
);
709 /* verify expected num of objects in chain */
710 ASSERT_EQ(2, (int)entry
.chain
.objs
.size());
712 list
<cls_rgw_obj
>::iterator oiter
= entry
.chain
.objs
.begin();
713 cls_rgw_obj obj1
, obj2
;
715 /* create expected objects */
716 create_obj(obj1
, i
, 1);
717 create_obj(obj2
, i
, 2);
719 /* assign returned object names */
720 cls_rgw_obj
& ret_obj1
= *oiter
++;
721 cls_rgw_obj
& ret_obj2
= *oiter
;
723 /* verify objects are as expected */
724 ASSERT_EQ(1, (int)cmp_objs(obj1
, ret_obj1
));
725 ASSERT_EQ(1, (int)cmp_objs(obj2
, ret_obj2
));
729 TEST(cls_rgw
, gc_defer
)
731 librados::IoCtx ioctx
;
732 librados::Rados rados
;
734 string gc_pool_name
= get_temp_pool_name();
736 ASSERT_EQ("", create_one_pool_pp(gc_pool_name
, rados
));
737 ASSERT_EQ(0, rados
.ioctx_create(gc_pool_name
.c_str(), ioctx
));
740 string tag
= "mychain";
742 librados::ObjectWriteOperation op
;
743 cls_rgw_gc_obj_info info
;
750 cls_rgw_gc_set_entry(op
, 0, info
);
752 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
755 list
<cls_rgw_gc_obj_info
> entries
;
759 /* list chains, verify num entries as expected */
760 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 1, true, entries
, &truncated
, next_marker
));
761 ASSERT_EQ(1, (int)entries
.size());
762 ASSERT_EQ(0, truncated
);
764 librados::ObjectWriteOperation op2
;
767 cls_rgw_gc_defer_entry(op2
, 5, tag
);
768 ASSERT_EQ(0, ioctx
.operate(oid
, &op2
));
773 /* verify list doesn't show deferred entry (this may fail if cluster is thrashing) */
774 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 1, true, entries
, &truncated
, next_marker
));
775 ASSERT_EQ(0, (int)entries
.size());
776 ASSERT_EQ(0, truncated
);
782 /* verify list shows deferred entry */
783 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 1, true, entries
, &truncated
, next_marker
));
784 ASSERT_EQ(1, (int)entries
.size());
785 ASSERT_EQ(0, truncated
);
787 librados::ObjectWriteOperation op3
;
792 cls_rgw_gc_remove(op3
, tags
);
793 ASSERT_EQ(0, ioctx
.operate(oid
, &op3
));
798 /* verify entry was removed */
799 ASSERT_EQ(0, cls_rgw_gc_list(ioctx
, oid
, marker
, 1, true, entries
, &truncated
, next_marker
));
800 ASSERT_EQ(0, (int)entries
.size());
801 ASSERT_EQ(0, truncated
);
805 ASSERT_EQ(0, destroy_one_pool_pp(gc_pool_name
, rados
));
808 auto populate_usage_log_info(std::string user
, std::string payer
, int total_usage_entries
)
810 rgw_usage_log_info info
;
812 for (int i
=0; i
< total_usage_entries
; i
++){
813 auto bucket
= str_int("bucket", i
);
814 info
.entries
.emplace_back(rgw_usage_log_entry(user
, payer
, bucket
));
820 auto gen_usage_log_info(std::string payer
, std::string bucket
, int total_usage_entries
)
822 rgw_usage_log_info info
;
823 for (int i
=0; i
< total_usage_entries
; i
++){
824 auto user
= str_int("user", i
);
825 info
.entries
.emplace_back(rgw_usage_log_entry(user
, payer
, bucket
));
831 TEST(cls_rgw
, usage_basic
)
833 string oid
="usage.1";
835 uint64_t start_epoch
{0}, end_epoch
{(uint64_t) -1};
836 int total_usage_entries
= 512;
837 uint64_t max_entries
= 2000;
840 auto info
= populate_usage_log_info(user
, payer
, total_usage_entries
);
841 ObjectWriteOperation op
;
842 cls_rgw_usage_log_add(op
, info
);
843 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
846 map
<rgw_user_bucket
, rgw_usage_log_entry
> usage
, usage2
;
850 int ret
= cls_rgw_usage_log_read(ioctx
, oid
, user
, "", start_epoch
, end_epoch
,
851 max_entries
, read_iter
, usage
, &truncated
);
852 // read the entries, and see that we have all the added entries
854 ASSERT_FALSE(truncated
);
855 ASSERT_EQ(static_cast<uint64_t>(total_usage_entries
), usage
.size());
857 // delete and read to assert that we've deleted all the values
858 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx
, oid
, user
, "", start_epoch
, end_epoch
));
861 ret
= cls_rgw_usage_log_read(ioctx
, oid
, user
, "", start_epoch
, end_epoch
,
862 max_entries
, read_iter
, usage2
, &truncated
);
864 ASSERT_EQ(0u, usage2
.size());
866 // add and read to assert that bucket option is valid for usage reading
867 string bucket1
= "bucket-usage-1";
868 string bucket2
= "bucket-usage-2";
869 info
= gen_usage_log_info(payer
, bucket1
, 100);
870 cls_rgw_usage_log_add(op
, info
);
871 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
873 info
= gen_usage_log_info(payer
, bucket2
, 100);
874 cls_rgw_usage_log_add(op
, info
);
875 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
876 ret
= cls_rgw_usage_log_read(ioctx
, oid
, "", bucket1
, start_epoch
, end_epoch
,
877 max_entries
, read_iter
, usage2
, &truncated
);
879 ASSERT_EQ(100u, usage2
.size());
881 // delete and read to assert that bucket option is valid for usage trim
882 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx
, oid
, "", bucket1
, start_epoch
, end_epoch
));
884 ret
= cls_rgw_usage_log_read(ioctx
, oid
, "", bucket1
, start_epoch
, end_epoch
,
885 max_entries
, read_iter
, usage2
, &truncated
);
887 ASSERT_EQ(0u, usage2
.size());
888 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx
, oid
, "", bucket2
, start_epoch
, end_epoch
));
891 TEST(cls_rgw
, usage_clear_no_obj
)
894 string oid
="usage.10";
895 librados::ObjectWriteOperation op
;
896 cls_rgw_usage_log_clear(op
);
897 int ret
= ioctx
.operate(oid
, &op
);
902 TEST(cls_rgw
, usage_clear
)
906 string oid
="usage.10";
907 librados::ObjectWriteOperation op
;
908 int max_entries
=2000;
910 auto info
= populate_usage_log_info(user
, payer
, max_entries
);
912 cls_rgw_usage_log_add(op
, info
);
913 ASSERT_EQ(0, ioctx
.operate(oid
, &op
));
915 ObjectWriteOperation op2
;
916 cls_rgw_usage_log_clear(op2
);
917 int ret
= ioctx
.operate(oid
, &op2
);
920 map
<rgw_user_bucket
, rgw_usage_log_entry
> usage
;
922 uint64_t start_epoch
{0}, end_epoch
{(uint64_t) -1};
924 ret
= cls_rgw_usage_log_read(ioctx
, oid
, user
, "", start_epoch
, end_epoch
,
925 max_entries
, read_iter
, usage
, &truncated
);
927 ASSERT_EQ(0u, usage
.size());
932 /* must be last test! */
934 TEST(cls_rgw
, finalize
)
938 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, rados
));