]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/cls_rgw/test_cls_rgw.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / cls_rgw / test_cls_rgw.cc
CommitLineData
1e59de90 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
7c673cae
FG
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"
11fdf7f2 9#include "test/librados/test_cxx.h"
1adf2230 10#include "global/global_context.h"
9f95a23c 11#include "common/ceph_context.h"
7c673cae
FG
12
13#include <errno.h>
14#include <string>
15#include <vector>
16#include <map>
31f18b77 17#include <set>
7c673cae 18
20effc67 19using namespace std;
7c673cae
FG
20using namespace librados;
21
9f95a23c
TL
22// creates a temporary pool and initializes an IoCtx shared by all tests
23class cls_rgw : public ::testing::Test {
24 static librados::Rados rados;
25 static std::string pool_name;
26 protected:
27 static librados::IoCtx ioctx;
28
29 static void SetUpTestCase() {
30 pool_name = get_temp_pool_name();
31 /* create pool */
32 ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
33 ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
34 }
35 static void TearDownTestCase() {
36 /* remove pool */
37 ioctx.close();
38 ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
39 }
40};
41librados::Rados cls_rgw::rados;
42std::string cls_rgw::pool_name;
43librados::IoCtx cls_rgw::ioctx;
7c673cae
FG
44
45
46string str_int(string s, int i)
47{
48 char buf[32];
49 snprintf(buf, sizeof(buf), "-%d", i);
50 s.append(buf);
51
52 return s;
53}
54
11fdf7f2 55void test_stats(librados::IoCtx& ioctx, string& oid, RGWObjCategory category, uint64_t num_entries, uint64_t total_size)
7c673cae
FG
56{
57 map<int, struct rgw_cls_list_ret> results;
58 map<int, string> oids;
59 oids[0] = oid;
60 ASSERT_EQ(0, CLSRGWIssueGetDirHeader(ioctx, oids, results, 8)());
61
62 uint64_t entries = 0;
63 uint64_t size = 0;
64 map<int, struct rgw_cls_list_ret>::iterator iter = results.begin();
65 for (; iter != results.end(); ++iter) {
66 entries += (iter->second).dir.header.stats[category].num_entries;
67 size += (iter->second).dir.header.stats[category].total_size;
68 }
69 ASSERT_EQ(total_size, size);
70 ASSERT_EQ(num_entries, entries);
71}
72
9f95a23c
TL
73void index_prepare(librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op,
74 string& tag, const cls_rgw_obj_key& key, string& loc,
75 uint16_t bi_flags = 0, bool log_op = true)
7c673cae 76{
9f95a23c 77 ObjectWriteOperation op;
31f18b77 78 rgw_zone_set zones_trace;
9f95a23c
TL
79 cls_rgw_bucket_prepare_op(op, index_op, tag, key, loc, log_op, bi_flags, zones_trace);
80 ASSERT_EQ(0, ioctx.operate(oid, &op));
7c673cae
FG
81}
82
9f95a23c
TL
83void index_complete(librados::IoCtx& ioctx, string& oid, RGWModifyOp index_op,
84 string& tag, int epoch, const cls_rgw_obj_key& key,
85 rgw_bucket_dir_entry_meta& meta, uint16_t bi_flags = 0,
86 bool log_op = true)
7c673cae 87{
9f95a23c 88 ObjectWriteOperation op;
7c673cae
FG
89 rgw_bucket_entry_ver ver;
90 ver.pool = ioctx.get_id();
91 ver.epoch = epoch;
92 meta.accounted_size = meta.size;
9f95a23c
TL
93 cls_rgw_bucket_complete_op(op, index_op, tag, ver, key, meta, nullptr, log_op, bi_flags, nullptr);
94 ASSERT_EQ(0, ioctx.operate(oid, &op));
95 if (!key.instance.empty()) {
96 bufferlist olh_tag;
97 olh_tag.append(tag);
98 rgw_zone_set zone_set;
99 ASSERT_EQ(0, cls_rgw_bucket_link_olh(ioctx, oid, key, olh_tag,
100 false, tag, &meta, epoch,
101 ceph::real_time{}, true, true, zone_set));
102 }
7c673cae
FG
103}
104
9f95a23c 105TEST_F(cls_rgw, index_basic)
7c673cae
FG
106{
107 string bucket_oid = str_int("bucket", 0);
108
9f95a23c
TL
109 ObjectWriteOperation op;
110 cls_rgw_bucket_init_index(op);
111 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
7c673cae
FG
112
113 uint64_t epoch = 1;
114
115 uint64_t obj_size = 1024;
116
117#define NUM_OBJS 10
118 for (int i = 0; i < NUM_OBJS; i++) {
9f95a23c 119 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
120 string tag = str_int("tag", i);
121 string loc = str_int("loc", i);
122
9f95a23c 123 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 124
11fdf7f2 125 test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, obj_size * i);
7c673cae 126
7c673cae 127 rgw_bucket_dir_entry_meta meta;
11fdf7f2 128 meta.category = RGWObjCategory::None;
7c673cae 129 meta.size = obj_size;
9f95a23c 130 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta);
7c673cae
FG
131 }
132
11fdf7f2
TL
133 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS,
134 obj_size * NUM_OBJS);
7c673cae
FG
135}
136
9f95a23c 137TEST_F(cls_rgw, index_multiple_obj_writers)
7c673cae
FG
138{
139 string bucket_oid = str_int("bucket", 1);
140
9f95a23c
TL
141 ObjectWriteOperation op;
142 cls_rgw_bucket_init_index(op);
143 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
7c673cae
FG
144
145 uint64_t obj_size = 1024;
146
9f95a23c 147 cls_rgw_obj_key obj = str_int("obj", 0);
7c673cae
FG
148 string loc = str_int("loc", 0);
149 /* multi prepare on a single object */
150 for (int i = 0; i < NUM_OBJS; i++) {
151 string tag = str_int("tag", i);
152
9f95a23c 153 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 154
11fdf7f2 155 test_stats(ioctx, bucket_oid, RGWObjCategory::None, 0, 0);
7c673cae
FG
156 }
157
158 for (int i = NUM_OBJS; i > 0; i--) {
159 string tag = str_int("tag", i - 1);
160
161 rgw_bucket_dir_entry_meta meta;
11fdf7f2 162 meta.category = RGWObjCategory::None;
7c673cae
FG
163 meta.size = obj_size * i;
164
9f95a23c 165 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, i, obj, meta);
7c673cae
FG
166
167 /* verify that object size doesn't change, as we went back with epoch */
11fdf7f2
TL
168 test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1,
169 obj_size * NUM_OBJS);
7c673cae
FG
170 }
171}
172
9f95a23c 173TEST_F(cls_rgw, index_remove_object)
7c673cae
FG
174{
175 string bucket_oid = str_int("bucket", 2);
176
9f95a23c
TL
177 ObjectWriteOperation op;
178 cls_rgw_bucket_init_index(op);
179 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
7c673cae
FG
180
181 uint64_t obj_size = 1024;
182 uint64_t total_size = 0;
183
184 int epoch = 0;
185
186 /* prepare multiple objects */
187 for (int i = 0; i < NUM_OBJS; i++) {
9f95a23c 188 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
189 string tag = str_int("tag", i);
190 string loc = str_int("loc", i);
191
9f95a23c 192 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 193
11fdf7f2 194 test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, total_size);
7c673cae
FG
195
196 rgw_bucket_dir_entry_meta meta;
11fdf7f2 197 meta.category = RGWObjCategory::None;
7c673cae
FG
198 meta.size = i * obj_size;
199 total_size += i * obj_size;
200
9f95a23c 201 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
7c673cae 202
11fdf7f2 203 test_stats(ioctx, bucket_oid, RGWObjCategory::None, i + 1, total_size);
7c673cae
FG
204 }
205
206 int i = NUM_OBJS / 2;
207 string tag_remove = "tag-rm";
208 string tag_modify = "tag-mod";
9f95a23c 209 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
210 string loc = str_int("loc", i);
211
212 /* prepare both removal and modification on the same object */
9f95a23c
TL
213 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
214 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, obj, loc);
7c673cae 215
11fdf7f2 216 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
7c673cae
FG
217
218 rgw_bucket_dir_entry_meta meta;
219
220 /* complete object removal */
9f95a23c 221 index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
7c673cae
FG
222
223 /* verify stats correct */
224 total_size -= i * obj_size;
11fdf7f2 225 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS - 1, total_size);
7c673cae
FG
226
227 meta.size = 512;
11fdf7f2 228 meta.category = RGWObjCategory::None;
7c673cae
FG
229
230 /* complete object modification */
9f95a23c 231 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
7c673cae
FG
232
233 /* verify stats correct */
234 total_size += meta.size;
11fdf7f2 235 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
7c673cae
FG
236
237
238 /* prepare both removal and modification on the same object, this time we'll
239 * first complete modification then remove*/
9f95a23c
TL
240 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, obj, loc);
241 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_modify, obj, loc);
7c673cae
FG
242
243 /* complete modification */
244 total_size -= meta.size;
245 meta.size = i * obj_size * 2;
11fdf7f2 246 meta.category = RGWObjCategory::None;
7c673cae
FG
247
248 /* complete object modification */
9f95a23c 249 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag_modify, ++epoch, obj, meta);
7c673cae
FG
250
251 /* verify stats correct */
252 total_size += meta.size;
11fdf7f2 253 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS, total_size);
7c673cae
FG
254
255 /* complete object removal */
9f95a23c 256 index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag_remove, ++epoch, obj, meta);
7c673cae
FG
257
258 /* verify stats correct */
259 total_size -= meta.size;
11fdf7f2
TL
260 test_stats(ioctx, bucket_oid, RGWObjCategory::None, NUM_OBJS - 1,
261 total_size);
7c673cae
FG
262}
263
9f95a23c 264TEST_F(cls_rgw, index_suggest)
7c673cae 265{
33c7a0ef 266 string bucket_oid = str_int("suggest", 1);
9f95a23c
TL
267 {
268 ObjectWriteOperation op;
269 cls_rgw_bucket_init_index(op);
270 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
271 }
7c673cae
FG
272 uint64_t total_size = 0;
273
274 int epoch = 0;
275
276 int num_objs = 100;
277
278 uint64_t obj_size = 1024;
279
280 /* create multiple objects */
281 for (int i = 0; i < num_objs; i++) {
9f95a23c 282 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
283 string tag = str_int("tag", i);
284 string loc = str_int("loc", i);
285
9f95a23c 286 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 287
11fdf7f2 288 test_stats(ioctx, bucket_oid, RGWObjCategory::None, i, total_size);
7c673cae
FG
289
290 rgw_bucket_dir_entry_meta meta;
11fdf7f2 291 meta.category = RGWObjCategory::None;
7c673cae
FG
292 meta.size = obj_size;
293 total_size += meta.size;
294
9f95a23c 295 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
7c673cae 296
11fdf7f2 297 test_stats(ioctx, bucket_oid, RGWObjCategory::None, i + 1, total_size);
7c673cae
FG
298 }
299
300 /* prepare (without completion) some of the objects */
301 for (int i = 0; i < num_objs; i += 2) {
9f95a23c 302 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
303 string tag = str_int("tag-prepare", i);
304 string loc = str_int("loc", i);
305
9f95a23c 306 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 307
11fdf7f2 308 test_stats(ioctx, bucket_oid, RGWObjCategory::None, num_objs, total_size);
7c673cae
FG
309 }
310
311 int actual_num_objs = num_objs;
312 /* remove half of the objects */
313 for (int i = num_objs / 2; i < num_objs; i++) {
9f95a23c 314 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
315 string tag = str_int("tag-rm", i);
316 string loc = str_int("loc", i);
317
9f95a23c 318 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
7c673cae 319
11fdf7f2 320 test_stats(ioctx, bucket_oid, RGWObjCategory::None, actual_num_objs, total_size);
7c673cae
FG
321
322 rgw_bucket_dir_entry_meta meta;
9f95a23c 323 index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag, ++epoch, obj, meta);
7c673cae
FG
324
325 total_size -= obj_size;
326 actual_num_objs--;
11fdf7f2 327 test_stats(ioctx, bucket_oid, RGWObjCategory::None, actual_num_objs, total_size);
7c673cae
FG
328 }
329
330 bufferlist updates;
331
332 for (int i = 0; i < num_objs; i += 2) {
9f95a23c 333 cls_rgw_obj_key obj = str_int("obj", i);
7c673cae
FG
334 string tag = str_int("tag-rm", i);
335 string loc = str_int("loc", i);
336
337 rgw_bucket_dir_entry dirent;
9f95a23c 338 dirent.key.name = obj.name;
7c673cae
FG
339 dirent.locator = loc;
340 dirent.exists = (i < num_objs / 2); // we removed half the objects
341 dirent.meta.size = 1024;
342 dirent.meta.accounted_size = 1024;
343
344 char suggest_op = (i < num_objs / 2 ? CEPH_RGW_UPDATE : CEPH_RGW_REMOVE);
345 cls_rgw_encode_suggestion(suggest_op, dirent, updates);
346 }
347
348 map<int, string> bucket_objs;
349 bucket_objs[0] = bucket_oid;
350 int r = CLSRGWIssueSetTagTimeout(ioctx, bucket_objs, 8 /* max aio */, 1)();
351 ASSERT_EQ(0, r);
352
353 sleep(1);
354
355 /* suggest changes! */
9f95a23c
TL
356 {
357 ObjectWriteOperation op;
358 cls_rgw_suggest_changes(op, updates);
359 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
360 }
7c673cae 361 /* suggest changes twice! */
9f95a23c
TL
362 {
363 ObjectWriteOperation op;
364 cls_rgw_suggest_changes(op, updates);
365 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
366 }
11fdf7f2 367 test_stats(ioctx, bucket_oid, RGWObjCategory::None, num_objs / 2, total_size);
7c673cae
FG
368}
369
33c7a0ef
TL
370static void list_entries(librados::IoCtx& ioctx,
371 const std::string& oid,
372 uint32_t num_entries,
373 std::map<int, rgw_cls_list_ret>& results)
374{
375 std::map<int, std::string> oids = { {0, oid} };
376 cls_rgw_obj_key start_key;
377 string empty_prefix;
378 string empty_delimiter;
379 ASSERT_EQ(0, CLSRGWIssueBucketList(ioctx, start_key, empty_prefix,
380 empty_delimiter, num_entries,
381 true, oids, results, 1)());
382}
383
384TEST_F(cls_rgw, index_suggest_complete)
385{
386 string bucket_oid = str_int("suggest", 2);
387 {
388 ObjectWriteOperation op;
389 cls_rgw_bucket_init_index(op);
390 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
391 }
392
393 cls_rgw_obj_key obj = str_int("obj", 0);
394 string tag = str_int("tag-prepare", 0);
395 string loc = str_int("loc", 0);
396
397 // prepare entry
398 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
399
400 // list entry before completion
401 rgw_bucket_dir_entry dirent;
402 {
403 std::map<int, rgw_cls_list_ret> listing;
404 list_entries(ioctx, bucket_oid, 1, listing);
405 ASSERT_EQ(1, listing.size());
406 const auto& entries = listing.begin()->second.dir.m;
407 ASSERT_EQ(1, entries.size());
408 dirent = entries.begin()->second;
409 ASSERT_EQ(obj, dirent.key);
410 }
411 // complete entry
412 {
413 rgw_bucket_dir_entry_meta meta;
414 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, 1, obj, meta);
415 }
416 // suggest removal of listed entry
417 {
418 bufferlist updates;
419 cls_rgw_encode_suggestion(CEPH_RGW_REMOVE, dirent, updates);
420
421 ObjectWriteOperation op;
422 cls_rgw_suggest_changes(op, updates);
423 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
424 }
425 // list entry again, verify that suggested removal was not applied
426 {
427 std::map<int, rgw_cls_list_ret> listing;
428 list_entries(ioctx, bucket_oid, 1, listing);
429 ASSERT_EQ(1, listing.size());
430 const auto& entries = listing.begin()->second.dir.m;
431 ASSERT_EQ(1, entries.size());
432 EXPECT_TRUE(entries.begin()->second.exists);
433 }
434}
9f95a23c 435
1adf2230
AA
436/*
437 * This case is used to test whether get_obj_vals will
438 * return all validate utf8 objnames and filter out those
439 * in BI_PREFIX_CHAR private namespace.
440 */
9f95a23c 441TEST_F(cls_rgw, index_list)
1adf2230
AA
442{
443 string bucket_oid = str_int("bucket", 4);
444
9f95a23c
TL
445 ObjectWriteOperation op;
446 cls_rgw_bucket_init_index(op);
447 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
1adf2230
AA
448
449 uint64_t epoch = 1;
450 uint64_t obj_size = 1024;
81eedcae 451 const int num_objs = 4;
1adf2230
AA
452 const string keys[num_objs] = {
453 /* single byte utf8 character */
454 { static_cast<char>(0x41) },
455 /* double byte utf8 character */
456 { static_cast<char>(0xCF), static_cast<char>(0x8F) },
457 /* treble byte utf8 character */
458 { static_cast<char>(0xDF), static_cast<char>(0x8F), static_cast<char>(0x8F) },
459 /* quadruble byte utf8 character */
460 { static_cast<char>(0xF7), static_cast<char>(0x8F), static_cast<char>(0x8F), static_cast<char>(0x8F) },
1adf2230
AA
461 };
462
463 for (int i = 0; i < num_objs; i++) {
464 string obj = keys[i];
465 string tag = str_int("tag", i);
466 string loc = str_int("loc", i);
467
9f95a23c
TL
468 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
469 0 /* bi_flags */, false /* log_op */);
1adf2230 470
1adf2230 471 rgw_bucket_dir_entry_meta meta;
11fdf7f2 472 meta.category = RGWObjCategory::None;
1adf2230 473 meta.size = obj_size;
9f95a23c
TL
474 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
475 0 /* bi_flags */, false /* log_op */);
81eedcae
TL
476 }
477
478 map<string, bufferlist> entries;
479 /* insert 998 omap key starts with BI_PREFIX_CHAR,
480 * so bucket list first time will get one key before 0x80 and one key after */
481 for (int i = 0; i < 998; ++i) {
482 char buf[10];
483 snprintf(buf, sizeof(buf), "%c%s%d", 0x80, "1000_", i);
484 entries.emplace(string{buf}, bufferlist{});
1adf2230 485 }
81eedcae 486 ioctx.omap_set(bucket_oid, entries);
1adf2230 487
9f95a23c
TL
488 test_stats(ioctx, bucket_oid, RGWObjCategory::None,
489 num_objs, obj_size * num_objs);
1adf2230
AA
490
491 map<int, string> oids = { {0, bucket_oid} };
492 map<int, struct rgw_cls_list_ret> list_results;
493 cls_rgw_obj_key start_key("", "");
9f95a23c
TL
494 string empty_prefix;
495 string empty_delimiter;
496 int r = CLSRGWIssueBucketList(ioctx, start_key,
497 empty_prefix, empty_delimiter,
498 1000, true, oids, list_results, 1)();
1adf2230 499 ASSERT_EQ(r, 0);
11fdf7f2 500 ASSERT_EQ(1u, list_results.size());
1adf2230
AA
501
502 auto it = list_results.begin();
503 auto m = (it->second).dir.m;
504
11fdf7f2 505 ASSERT_EQ(4u, m.size());
1adf2230 506 int i = 0;
9f95a23c 507 for(auto it2 = m.cbegin(); it2 != m.cend(); it2++, i++) {
1adf2230 508 ASSERT_EQ(it2->first.compare(keys[i]), 0);
9f95a23c 509 }
1adf2230
AA
510}
511
512
9f95a23c
TL
513/*
514 * This case is used to test when bucket index list that includes a
515 * delimiter can handle the first chunk ending in a delimiter.
516 */
517TEST_F(cls_rgw, index_list_delimited)
518{
519 string bucket_oid = str_int("bucket", 7);
520
521 ObjectWriteOperation op;
522 cls_rgw_bucket_init_index(op);
523 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
524
525 uint64_t epoch = 1;
526 uint64_t obj_size = 1024;
527 const int file_num_objs = 5;
528 const int dir_num_objs = 1005;
529
530 std::vector<std::string> file_prefixes =
531 { "a", "c", "e", "g", "i", "k", "m", "o", "q", "s", "u" };
532 std::vector<std::string> dir_prefixes =
533 { "b/", "d/", "f/", "h/", "j/", "l/", "n/", "p/", "r/", "t/" };
534
535 rgw_bucket_dir_entry_meta meta;
536 meta.category = RGWObjCategory::None;
537 meta.size = obj_size;
538
539 // create top-level files
540 for (const auto& p : file_prefixes) {
541 for (int i = 0; i < file_num_objs; i++) {
542 string tag = str_int("tag", i);
543 string loc = str_int("loc", i);
544 const string obj = str_int(p, i);
545
546 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
547 0 /* bi_flags */, false /* log_op */);
548
549 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
550 0 /* bi_flags */, false /* log_op */);
551 }
552 }
553
554 // create large directories
555 for (const auto& p : dir_prefixes) {
556 for (int i = 0; i < dir_num_objs; i++) {
557 string tag = str_int("tag", i);
558 string loc = str_int("loc", i);
559 const string obj = p + str_int("f", i);
560
561 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
562 0 /* bi_flags */, false /* log_op */);
563
564 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
565 0 /* bi_flags */, false /* log_op */);
566 }
567 }
568
569 map<int, string> oids = { {0, bucket_oid} };
570 map<int, struct rgw_cls_list_ret> list_results;
571 cls_rgw_obj_key start_key("", "");
572 const string empty_prefix;
573 const string delimiter = "/";
574 int r = CLSRGWIssueBucketList(ioctx, start_key,
575 empty_prefix, delimiter,
576 1000, true, oids, list_results, 1)();
577 ASSERT_EQ(r, 0);
578 ASSERT_EQ(1u, list_results.size()) <<
579 "Because we only have one bucket index shard, we should "
580 "only get one list_result.";
581
582 auto it = list_results.begin();
583 auto id_entry_map = it->second.dir.m;
584 bool truncated = it->second.is_truncated;
585
586 // the cls code will make 4 tries to get 1000 entries; however
587 // because each of the subdirectories is so large, each attempt will
588 // only retrieve the first part of the subdirectory
589
590 ASSERT_EQ(48u, id_entry_map.size()) <<
591 "We should get 40 top-level entries and the tops of 8 \"subdirectories\".";
592 ASSERT_EQ(true, truncated) << "We did not get all entries.";
593
594 ASSERT_EQ("a-0", id_entry_map.cbegin()->first);
595 ASSERT_EQ("p/", id_entry_map.crbegin()->first);
596
597 // now let's get the rest of the entries
598
599 list_results.clear();
600
601 cls_rgw_obj_key start_key2("p/", "");
602 r = CLSRGWIssueBucketList(ioctx, start_key2,
603 empty_prefix, delimiter,
604 1000, true, oids, list_results, 1)();
605 ASSERT_EQ(r, 0);
606
607 it = list_results.begin();
608 id_entry_map = it->second.dir.m;
609 truncated = it->second.is_truncated;
610
611 ASSERT_EQ(17u, id_entry_map.size()) <<
612 "We should get 15 top-level entries and the tops of 2 \"subdirectories\".";
613 ASSERT_EQ(false, truncated) << "We now have all entries.";
614
615 ASSERT_EQ("q-0", id_entry_map.cbegin()->first);
616 ASSERT_EQ("u-4", id_entry_map.crbegin()->first);
617}
618
619
620TEST_F(cls_rgw, bi_list)
1adf2230
AA
621{
622 string bucket_oid = str_int("bucket", 5);
623
20effc67 624 CephContext *cct = reinterpret_cast<CephContext *>(ioctx.cct());
1adf2230 625
9f95a23c
TL
626 ObjectWriteOperation op;
627 cls_rgw_bucket_init_index(op);
628 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
1adf2230 629
20effc67 630 const std::string empty_name_filter;
1adf2230 631 uint64_t max = 10;
20effc67 632 std::list<rgw_cls_bi_entry> entries;
1adf2230 633 bool is_truncated;
20effc67 634 std::string marker;
1adf2230 635
20effc67
TL
636 int ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
637 &entries, &is_truncated);
1adf2230 638 ASSERT_EQ(ret, 0);
9f95a23c
TL
639 ASSERT_EQ(entries.size(), 0u) <<
640 "The listing of an empty bucket as 0 entries.";
641 ASSERT_EQ(is_truncated, false) <<
642 "The listing of an empty bucket is not truncated.";
1adf2230
AA
643
644 uint64_t epoch = 1;
645 uint64_t obj_size = 1024;
20effc67 646 const uint64_t num_objs = 35;
1adf2230
AA
647
648 for (uint64_t i = 0; i < num_objs; i++) {
b3b6e05e 649 string obj = str_int(i % 4 ? "obj" : "об'єкт", i);
1adf2230
AA
650 string tag = str_int("tag", i);
651 string loc = str_int("loc", i);
9f95a23c
TL
652 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc,
653 RGW_BILOG_FLAG_VERSIONED_OP);
654
1adf2230 655 rgw_bucket_dir_entry_meta meta;
11fdf7f2 656 meta.category = RGWObjCategory::None;
1adf2230 657 meta.size = obj_size;
9f95a23c
TL
658 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, epoch, obj, meta,
659 RGW_BILOG_FLAG_VERSIONED_OP);
1adf2230
AA
660 }
661
20effc67
TL
662 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs + 10,
663 &entries, &is_truncated);
1adf2230 664 ASSERT_EQ(ret, 0);
b3b6e05e
TL
665 if (is_truncated) {
666 ASSERT_LT(entries.size(), num_objs);
1adf2230
AA
667 } else {
668 ASSERT_EQ(entries.size(), num_objs);
669 }
670
671 uint64_t num_entries = 0;
672
673 is_truncated = true;
20effc67 674 marker.clear();
1adf2230 675 while(is_truncated) {
20effc67
TL
676 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
677 &entries, &is_truncated);
1adf2230
AA
678 ASSERT_EQ(ret, 0);
679 if (is_truncated) {
b3b6e05e 680 ASSERT_LT(entries.size(), num_objs - num_entries);
1adf2230
AA
681 } else {
682 ASSERT_EQ(entries.size(), num_objs - num_entries);
683 }
684 num_entries += entries.size();
685 marker = entries.back().idx;
686 }
687
20effc67
TL
688 // try with marker as final entry
689 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
690 &entries, &is_truncated);
1adf2230 691 ASSERT_EQ(ret, 0);
11fdf7f2 692 ASSERT_EQ(entries.size(), 0u);
1adf2230
AA
693 ASSERT_EQ(is_truncated, false);
694
695 if (cct->_conf->osd_max_omap_entries_per_request < 15) {
696 num_entries = 0;
697 max = 15;
698 is_truncated = true;
699 marker.clear();
700 while(is_truncated) {
20effc67
TL
701 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
702 &entries, &is_truncated);
1adf2230
AA
703 ASSERT_EQ(ret, 0);
704 if (is_truncated) {
b3b6e05e 705 ASSERT_LT(entries.size(), num_objs - num_entries);
1adf2230
AA
706 } else {
707 ASSERT_EQ(entries.size(), num_objs - num_entries);
708 }
709 num_entries += entries.size();
710 marker = entries.back().idx;
711 }
20effc67
TL
712
713 // try with marker as final entry
714 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, max,
715 &entries, &is_truncated);
716 ASSERT_EQ(ret, 0);
717 ASSERT_EQ(entries.size(), 0u);
718 ASSERT_EQ(is_truncated, false);
1adf2230
AA
719 }
720
20effc67
TL
721 // test with name filters; pairs contain filter and expected number of elements returned
722 const std::list<std::pair<const std::string,unsigned>> filters_results =
723 { { str_int("obj", 9), 1 },
724 { str_int("об'єкт", 8), 1 },
725 { str_int("obj", 8), 0 } };
726 for (const auto& filter_result : filters_results) {
727 is_truncated = true;
728 entries.clear();
729 marker.clear();
730
731 ret = cls_rgw_bi_list(ioctx, bucket_oid, filter_result.first, marker, max,
732 &entries, &is_truncated);
733
734 ASSERT_EQ(ret, 0) << "bi list test with name filters should succeed";
735 ASSERT_EQ(entries.size(), filter_result.second) <<
736 "bi list test with filters should return the correct number of results";
737 ASSERT_EQ(is_truncated, false) <<
738 "bi list test with filters should return correct truncation indicator";
739 }
740
741 // test whether combined segment count is correcgt
742 is_truncated = false;
743 entries.clear();
744 marker.clear();
745
746 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs - 1,
747 &entries, &is_truncated);
748 ASSERT_EQ(ret, 0) << "combined segment count should succeed";
749 ASSERT_EQ(entries.size(), num_objs - 1) <<
750 "combined segment count should return the correct number of results";
751 ASSERT_EQ(is_truncated, true) <<
752 "combined segment count should return correct truncation indicator";
753
754
755 marker = entries.back().idx; // advance marker
756 ret = cls_rgw_bi_list(ioctx, bucket_oid, empty_name_filter, marker, num_objs - 1,
757 &entries, &is_truncated);
758 ASSERT_EQ(ret, 0) << "combined segment count should succeed";
759 ASSERT_EQ(entries.size(), 1) <<
760 "combined segment count should return the correct number of results";
761 ASSERT_EQ(is_truncated, false) <<
762 "combined segment count should return correct truncation indicator";
1adf2230
AA
763}
764
7c673cae
FG
765/* test garbage collection */
766static void create_obj(cls_rgw_obj& obj, int i, int j)
767{
768 char buf[32];
769 snprintf(buf, sizeof(buf), "-%d.%d", i, j);
770 obj.pool = "pool";
771 obj.pool.append(buf);
772 obj.key.name = "oid";
773 obj.key.name.append(buf);
774 obj.loc = "loc";
775 obj.loc.append(buf);
776}
777
778static bool cmp_objs(cls_rgw_obj& obj1, cls_rgw_obj& obj2)
779{
780 return (obj1.pool == obj2.pool) &&
781 (obj1.key == obj2.key) &&
782 (obj1.loc == obj2.loc);
783}
784
785
9f95a23c 786TEST_F(cls_rgw, gc_set)
7c673cae
FG
787{
788 /* add chains */
789 string oid = "obj";
790 for (int i = 0; i < 10; i++) {
791 char buf[32];
792 snprintf(buf, sizeof(buf), "chain-%d", i);
793 string tag = buf;
794 librados::ObjectWriteOperation op;
795 cls_rgw_gc_obj_info info;
796
797 cls_rgw_obj obj1, obj2;
798 create_obj(obj1, i, 1);
799 create_obj(obj2, i, 2);
800 info.chain.objs.push_back(obj1);
801 info.chain.objs.push_back(obj2);
802
803 op.create(false); // create object
804
805 info.tag = tag;
806 cls_rgw_gc_set_entry(op, 0, info);
807
808 ASSERT_EQ(0, ioctx.operate(oid, &op));
809 }
810
811 bool truncated;
812 list<cls_rgw_gc_obj_info> entries;
813 string marker;
31f18b77 814 string next_marker;
7c673cae
FG
815
816 /* list chains, verify truncated */
31f18b77 817 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
7c673cae
FG
818 ASSERT_EQ(8, (int)entries.size());
819 ASSERT_EQ(1, truncated);
820
821 entries.clear();
31f18b77 822 next_marker.clear();
7c673cae
FG
823
824 /* list all chains, verify not truncated */
31f18b77 825 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 10, true, entries, &truncated, next_marker));
7c673cae
FG
826 ASSERT_EQ(10, (int)entries.size());
827 ASSERT_EQ(0, truncated);
828
829 /* verify all chains are valid */
830 list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
831 for (int i = 0; i < 10; i++, ++iter) {
832 cls_rgw_gc_obj_info& entry = *iter;
833
834 /* create expected chain name */
835 char buf[32];
836 snprintf(buf, sizeof(buf), "chain-%d", i);
837 string tag = buf;
838
839 /* verify chain name as expected */
840 ASSERT_EQ(entry.tag, tag);
841
842 /* verify expected num of objects in chain */
843 ASSERT_EQ(2, (int)entry.chain.objs.size());
844
845 list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
846 cls_rgw_obj obj1, obj2;
847
848 /* create expected objects */
849 create_obj(obj1, i, 1);
850 create_obj(obj2, i, 2);
851
852 /* assign returned object names */
853 cls_rgw_obj& ret_obj1 = *oiter++;
854 cls_rgw_obj& ret_obj2 = *oiter;
855
856 /* verify objects are as expected */
857 ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
858 ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
859 }
860}
861
9f95a23c 862TEST_F(cls_rgw, gc_list)
31f18b77
FG
863{
864 /* add chains */
865 string oid = "obj";
866 for (int i = 0; i < 10; i++) {
867 char buf[32];
868 snprintf(buf, sizeof(buf), "chain-%d", i);
869 string tag = buf;
870 librados::ObjectWriteOperation op;
871 cls_rgw_gc_obj_info info;
872
873 cls_rgw_obj obj1, obj2;
874 create_obj(obj1, i, 1);
875 create_obj(obj2, i, 2);
876 info.chain.objs.push_back(obj1);
877 info.chain.objs.push_back(obj2);
878
879 op.create(false); // create object
880
881 info.tag = tag;
882 cls_rgw_gc_set_entry(op, 0, info);
883
884 ASSERT_EQ(0, ioctx.operate(oid, &op));
885 }
886
887 bool truncated;
888 list<cls_rgw_gc_obj_info> entries;
889 list<cls_rgw_gc_obj_info> entries2;
890 string marker;
891 string next_marker;
892
893 /* list chains, verify truncated */
894 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
895 ASSERT_EQ(8, (int)entries.size());
896 ASSERT_EQ(1, truncated);
897
898 marker = next_marker;
899 next_marker.clear();
900
901 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries2, &truncated, next_marker));
902 ASSERT_EQ(2, (int)entries2.size());
903 ASSERT_EQ(0, truncated);
904
905 entries.splice(entries.end(), entries2);
906
907 /* verify all chains are valid */
908 list<cls_rgw_gc_obj_info>::iterator iter = entries.begin();
909 for (int i = 0; i < 10; i++, ++iter) {
910 cls_rgw_gc_obj_info& entry = *iter;
911
912 /* create expected chain name */
913 char buf[32];
914 snprintf(buf, sizeof(buf), "chain-%d", i);
915 string tag = buf;
916
917 /* verify chain name as expected */
918 ASSERT_EQ(entry.tag, tag);
919
920 /* verify expected num of objects in chain */
921 ASSERT_EQ(2, (int)entry.chain.objs.size());
922
923 list<cls_rgw_obj>::iterator oiter = entry.chain.objs.begin();
924 cls_rgw_obj obj1, obj2;
925
926 /* create expected objects */
927 create_obj(obj1, i, 1);
928 create_obj(obj2, i, 2);
929
930 /* assign returned object names */
931 cls_rgw_obj& ret_obj1 = *oiter++;
932 cls_rgw_obj& ret_obj2 = *oiter;
933
934 /* verify objects are as expected */
935 ASSERT_EQ(1, (int)cmp_objs(obj1, ret_obj1));
936 ASSERT_EQ(1, (int)cmp_objs(obj2, ret_obj2));
937 }
938}
939
9f95a23c 940TEST_F(cls_rgw, gc_defer)
7c673cae
FG
941{
942 librados::IoCtx ioctx;
943 librados::Rados rados;
944
945 string gc_pool_name = get_temp_pool_name();
946 /* create pool */
947 ASSERT_EQ("", create_one_pool_pp(gc_pool_name, rados));
948 ASSERT_EQ(0, rados.ioctx_create(gc_pool_name.c_str(), ioctx));
949
950 string oid = "obj";
951 string tag = "mychain";
952
953 librados::ObjectWriteOperation op;
954 cls_rgw_gc_obj_info info;
955
956 op.create(false);
957
958 info.tag = tag;
959
960 /* create chain */
961 cls_rgw_gc_set_entry(op, 0, info);
962
963 ASSERT_EQ(0, ioctx.operate(oid, &op));
964
965 bool truncated;
966 list<cls_rgw_gc_obj_info> entries;
967 string marker;
31f18b77 968 string next_marker;
7c673cae
FG
969
970 /* list chains, verify num entries as expected */
31f18b77 971 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
972 ASSERT_EQ(1, (int)entries.size());
973 ASSERT_EQ(0, truncated);
974
975 librados::ObjectWriteOperation op2;
976
977 /* defer chain */
978 cls_rgw_gc_defer_entry(op2, 5, tag);
979 ASSERT_EQ(0, ioctx.operate(oid, &op2));
980
981 entries.clear();
31f18b77 982 next_marker.clear();
7c673cae
FG
983
984 /* verify list doesn't show deferred entry (this may fail if cluster is thrashing) */
31f18b77 985 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
986 ASSERT_EQ(0, (int)entries.size());
987 ASSERT_EQ(0, truncated);
988
989 /* wait enough */
990 sleep(5);
31f18b77 991 next_marker.clear();
7c673cae
FG
992
993 /* verify list shows deferred entry */
31f18b77 994 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
995 ASSERT_EQ(1, (int)entries.size());
996 ASSERT_EQ(0, truncated);
997
998 librados::ObjectWriteOperation op3;
11fdf7f2 999 vector<string> tags;
7c673cae
FG
1000 tags.push_back(tag);
1001
1002 /* remove chain */
1003 cls_rgw_gc_remove(op3, tags);
1004 ASSERT_EQ(0, ioctx.operate(oid, &op3));
1005
1006 entries.clear();
31f18b77 1007 next_marker.clear();
7c673cae
FG
1008
1009 /* verify entry was removed */
31f18b77 1010 ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
7c673cae
FG
1011 ASSERT_EQ(0, (int)entries.size());
1012 ASSERT_EQ(0, truncated);
1013
1014 /* remove pool */
1015 ioctx.close();
1016 ASSERT_EQ(0, destroy_one_pool_pp(gc_pool_name, rados));
1017}
1018
11fdf7f2
TL
1019auto populate_usage_log_info(std::string user, std::string payer, int total_usage_entries)
1020{
1021 rgw_usage_log_info info;
1022
1023 for (int i=0; i < total_usage_entries; i++){
1024 auto bucket = str_int("bucket", i);
1025 info.entries.emplace_back(rgw_usage_log_entry(user, payer, bucket));
1026 }
1027
1028 return info;
1029}
1030
1031auto gen_usage_log_info(std::string payer, std::string bucket, int total_usage_entries)
1032{
1033 rgw_usage_log_info info;
1034 for (int i=0; i < total_usage_entries; i++){
1035 auto user = str_int("user", i);
1036 info.entries.emplace_back(rgw_usage_log_entry(user, payer, bucket));
1037 }
1038
1039 return info;
1040}
1041
9f95a23c 1042TEST_F(cls_rgw, usage_basic)
b32b8144
FG
1043{
1044 string oid="usage.1";
1045 string user="user1";
1046 uint64_t start_epoch{0}, end_epoch{(uint64_t) -1};
11fdf7f2 1047 int total_usage_entries = 512;
b32b8144 1048 uint64_t max_entries = 2000;
11fdf7f2 1049 string payer;
b32b8144 1050
11fdf7f2 1051 auto info = populate_usage_log_info(user, payer, total_usage_entries);
b32b8144
FG
1052 ObjectWriteOperation op;
1053 cls_rgw_usage_log_add(op, info);
1054 ASSERT_EQ(0, ioctx.operate(oid, &op));
1055
1056 string read_iter;
1057 map <rgw_user_bucket, rgw_usage_log_entry> usage, usage2;
1058 bool truncated;
1059
1060
11fdf7f2 1061 int ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
b32b8144
FG
1062 max_entries, read_iter, usage, &truncated);
1063 // read the entries, and see that we have all the added entries
1064 ASSERT_EQ(0, ret);
1065 ASSERT_FALSE(truncated);
11fdf7f2 1066 ASSERT_EQ(static_cast<uint64_t>(total_usage_entries), usage.size());
b32b8144
FG
1067
1068 // delete and read to assert that we've deleted all the values
11fdf7f2 1069 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, user, "", start_epoch, end_epoch));
b32b8144
FG
1070
1071
11fdf7f2 1072 ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
b32b8144
FG
1073 max_entries, read_iter, usage2, &truncated);
1074 ASSERT_EQ(0, ret);
11fdf7f2
TL
1075 ASSERT_EQ(0u, usage2.size());
1076
1077 // add and read to assert that bucket option is valid for usage reading
1078 string bucket1 = "bucket-usage-1";
1079 string bucket2 = "bucket-usage-2";
1080 info = gen_usage_log_info(payer, bucket1, 100);
1081 cls_rgw_usage_log_add(op, info);
1082 ASSERT_EQ(0, ioctx.operate(oid, &op));
1083
1084 info = gen_usage_log_info(payer, bucket2, 100);
1085 cls_rgw_usage_log_add(op, info);
1086 ASSERT_EQ(0, ioctx.operate(oid, &op));
1087 ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
1088 max_entries, read_iter, usage2, &truncated);
1089 ASSERT_EQ(0, ret);
1090 ASSERT_EQ(100u, usage2.size());
1091
1092 // delete and read to assert that bucket option is valid for usage trim
1093 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket1, start_epoch, end_epoch));
1094
1095 ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
1096 max_entries, read_iter, usage2, &truncated);
1097 ASSERT_EQ(0, ret);
1098 ASSERT_EQ(0u, usage2.size());
1099 ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket2, start_epoch, end_epoch));
1100}
1101
9f95a23c 1102TEST_F(cls_rgw, usage_clear_no_obj)
11fdf7f2
TL
1103{
1104 string user="user1";
1105 string oid="usage.10";
1106 librados::ObjectWriteOperation op;
1107 cls_rgw_usage_log_clear(op);
1108 int ret = ioctx.operate(oid, &op);
1109 ASSERT_EQ(0, ret);
b32b8144
FG
1110
1111}
7c673cae 1112
9f95a23c 1113TEST_F(cls_rgw, usage_clear)
11fdf7f2
TL
1114{
1115 string user="user1";
1116 string payer;
1117 string oid="usage.10";
1118 librados::ObjectWriteOperation op;
1119 int max_entries=2000;
1120
1121 auto info = populate_usage_log_info(user, payer, max_entries);
1122
1123 cls_rgw_usage_log_add(op, info);
1124 ASSERT_EQ(0, ioctx.operate(oid, &op));
1125
1126 ObjectWriteOperation op2;
1127 cls_rgw_usage_log_clear(op2);
1128 int ret = ioctx.operate(oid, &op2);
1129 ASSERT_EQ(0, ret);
1130
1131 map <rgw_user_bucket, rgw_usage_log_entry> usage;
1132 bool truncated;
1133 uint64_t start_epoch{0}, end_epoch{(uint64_t) -1};
1134 string read_iter;
1135 ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
1136 max_entries, read_iter, usage, &truncated);
1137 ASSERT_EQ(0, ret);
1138 ASSERT_EQ(0u, usage.size());
11fdf7f2
TL
1139}
1140
9f95a23c
TL
1141static int bilog_list(librados::IoCtx& ioctx, const std::string& oid,
1142 cls_rgw_bi_log_list_ret *result)
1143{
1144 int retcode = 0;
1145 librados::ObjectReadOperation op;
1146 cls_rgw_bilog_list(op, "", 128, result, &retcode);
1147 int ret = ioctx.operate(oid, &op, nullptr);
1148 if (ret < 0) {
1149 return ret;
1150 }
1151 return retcode;
1152}
11fdf7f2 1153
9f95a23c
TL
1154static int bilog_trim(librados::IoCtx& ioctx, const std::string& oid,
1155 const std::string& start_marker,
1156 const std::string& end_marker)
1157{
1158 librados::ObjectWriteOperation op;
1159 cls_rgw_bilog_trim(op, start_marker, end_marker);
1160 return ioctx.operate(oid, &op);
1161}
7c673cae 1162
9f95a23c 1163TEST_F(cls_rgw, bi_log_trim)
7c673cae 1164{
9f95a23c
TL
1165 string bucket_oid = str_int("bucket", 6);
1166
1167 ObjectWriteOperation op;
1168 cls_rgw_bucket_init_index(op);
1169 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
1170
1171 // create 10 versioned entries. this generates instance and olh bi entries,
1172 // allowing us to check that bilog trim doesn't remove any of those
1173 for (int i = 0; i < 10; i++) {
1174 cls_rgw_obj_key obj{str_int("obj", i), "inst"};
1175 string tag = str_int("tag", i);
1176 string loc = str_int("loc", i);
1177
1178 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
1179 rgw_bucket_dir_entry_meta meta;
1180 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, 1, obj, meta);
1181 }
1182 // bi list
1183 {
1184 list<rgw_cls_bi_entry> entries;
1185 bool truncated{false};
1186 ASSERT_EQ(0, cls_rgw_bi_list(ioctx, bucket_oid, "", "", 128,
1187 &entries, &truncated));
1188 // prepare/complete/instance/olh entry for each
1189 EXPECT_EQ(40u, entries.size());
1190 EXPECT_FALSE(truncated);
1191 }
1192 // bilog list
1193 vector<rgw_bi_log_entry> bilog1;
1194 {
1195 cls_rgw_bi_log_list_ret bilog;
1196 ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
1197 // complete/olh entry for each
1198 EXPECT_EQ(20u, bilog.entries.size());
1199
1200 bilog1.assign(std::make_move_iterator(bilog.entries.begin()),
1201 std::make_move_iterator(bilog.entries.end()));
1202 }
1203 // trim front of bilog
1204 {
1205 const std::string from = "";
1206 const std::string to = bilog1[0].id;
1207 ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
1208 cls_rgw_bi_log_list_ret bilog;
1209 ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
1210 EXPECT_EQ(19u, bilog.entries.size());
1211 EXPECT_EQ(bilog1[1].id, bilog.entries.begin()->id);
1212 ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
1213 }
1214 // trim back of bilog
1215 {
1216 const std::string from = bilog1[18].id;
1217 const std::string to = "9";
1218 ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
1219 cls_rgw_bi_log_list_ret bilog;
1220 ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
1221 EXPECT_EQ(18u, bilog.entries.size());
1222 EXPECT_EQ(bilog1[18].id, bilog.entries.rbegin()->id);
1223 ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
1224 }
1225 // trim middle of bilog
1226 {
1227 const std::string from = bilog1[13].id;
1228 const std::string to = bilog1[14].id;
1229 ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
1230 cls_rgw_bi_log_list_ret bilog;
1231 ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
1232 EXPECT_EQ(17u, bilog.entries.size());
1233 ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
1234 }
1235 // trim full bilog
1236 {
1237 const std::string from = "";
1238 const std::string to = "9";
1239 ASSERT_EQ(0, bilog_trim(ioctx, bucket_oid, from, to));
1240 cls_rgw_bi_log_list_ret bilog;
1241 ASSERT_EQ(0, bilog_list(ioctx, bucket_oid, &bilog));
1242 EXPECT_EQ(0u, bilog.entries.size());
1243 ASSERT_EQ(-ENODATA, bilog_trim(ioctx, bucket_oid, from, to));
1244 }
1245 // bi list should be the same
1246 {
1247 list<rgw_cls_bi_entry> entries;
1248 bool truncated{false};
1249 ASSERT_EQ(0, cls_rgw_bi_list(ioctx, bucket_oid, "", "", 128,
1250 &entries, &truncated));
1251 EXPECT_EQ(40u, entries.size());
1252 EXPECT_FALSE(truncated);
1253 }
7c673cae 1254}
39ae355f
TL
1255
1256TEST_F(cls_rgw, index_racing_removes)
1257{
1258 string bucket_oid = str_int("bucket", 8);
1259
1260 ObjectWriteOperation op;
1261 cls_rgw_bucket_init_index(op);
1262 ASSERT_EQ(0, ioctx.operate(bucket_oid, &op));
1263
1264 int epoch = 0;
1265 rgw_bucket_dir_entry dirent;
1266 rgw_bucket_dir_entry_meta meta;
1267
1268 // prepare/complete add for single object
1269 const cls_rgw_obj_key obj{"obj"};
1270 std::string loc = "loc";
1271 {
1272 std::string tag = "tag-add";
1273 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, obj, loc);
1274 index_complete(ioctx, bucket_oid, CLS_RGW_OP_ADD, tag, ++epoch, obj, meta);
1275 test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1, 0);
1276 }
1277
1278 // list to verify no pending ops
1279 {
1280 std::map<int, rgw_cls_list_ret> results;
1281 list_entries(ioctx, bucket_oid, 1, results);
1282 ASSERT_EQ(1, results.size());
1283 const auto& entries = results.begin()->second.dir.m;
1284 ASSERT_EQ(1, entries.size());
1285 dirent = std::move(entries.begin()->second);
1286 ASSERT_EQ(obj, dirent.key);
1287 ASSERT_TRUE(dirent.exists);
1288 ASSERT_TRUE(dirent.pending_map.empty());
1289 }
1290
1291 // prepare three racing removals
1292 std::string tag1 = "tag-rm1";
1293 std::string tag2 = "tag-rm2";
1294 std::string tag3 = "tag-rm3";
1295 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag1, obj, loc);
1296 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag2, obj, loc);
1297 index_prepare(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag3, obj, loc);
1298
1299 test_stats(ioctx, bucket_oid, RGWObjCategory::None, 1, 0);
1300
1301 // complete on tag2
1302 index_complete(ioctx, bucket_oid, CLS_RGW_OP_DEL, tag2, ++epoch, obj, meta);
1303 {
1304 std::map<int, rgw_cls_list_ret> results;
1305 list_entries(ioctx, bucket_oid, 1, results);
1306 ASSERT_EQ(1, results.size());
1307 const auto& entries = results.begin()->second.dir.m;
1308 ASSERT_EQ(1, entries.size());
1309 dirent = std::move(entries.begin()->second);
1310 ASSERT_EQ(obj, dirent.key);
1311 ASSERT_FALSE(dirent.exists);
1312 ASSERT_FALSE(dirent.pending_map.empty());
1313 }
1314
1315 // cancel on tag1
1316 index_complete(ioctx, bucket_oid, CLS_RGW_OP_CANCEL, tag1, ++epoch, obj, meta);
1317 {
1318 std::map<int, rgw_cls_list_ret> results;
1319 list_entries(ioctx, bucket_oid, 1, results);
1320 ASSERT_EQ(1, results.size());
1321 const auto& entries = results.begin()->second.dir.m;
1322 ASSERT_EQ(1, entries.size());
1323 dirent = std::move(entries.begin()->second);
1324 ASSERT_EQ(obj, dirent.key);
1325 ASSERT_FALSE(dirent.exists);
1326 ASSERT_FALSE(dirent.pending_map.empty());
1327 }
1328
1329 // final cancel on tag3
1330 index_complete(ioctx, bucket_oid, CLS_RGW_OP_CANCEL, tag3, ++epoch, obj, meta);
1331
1332 // verify that the key was removed
1333 {
1334 std::map<int, rgw_cls_list_ret> results;
1335 list_entries(ioctx, bucket_oid, 1, results);
1336 EXPECT_EQ(1, results.size());
1337 const auto& entries = results.begin()->second.dir.m;
1338 ASSERT_EQ(0, entries.size());
1339 }
1340
1341 test_stats(ioctx, bucket_oid, RGWObjCategory::None, 0, 0);
1342}