Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / cls_rgw / test_cls_rgw.cc
1 // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "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>
15 #include <set>
16
17 using namespace librados;
18
19 librados::Rados rados;
20 librados::IoCtx ioctx;
21 string pool_name;
22
23
24 /* must be the first test! */
25 TEST(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
34 string 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
44 class OpMgr {
45   vector<ObjectOperation *> ops;
46
47 public:
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
70 void 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
88 void 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());
92   rgw_zone_set zones_trace;
93   cls_rgw_bucket_prepare_op(*op, index_op, tag, key, loc, true, 0, zones_trace);
94   ASSERT_EQ(0, ioctx.operate(oid, op));
95 }
96
97 void 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;
105   cls_rgw_bucket_complete_op(*op, index_op, tag, ver, key, meta, nullptr, true, 0, nullptr);
106   ASSERT_EQ(0, ioctx.operate(oid, op));
107 }
108
109 TEST(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
143 TEST(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
180 TEST(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
272 TEST(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 */
379 static 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
391 static 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
399 TEST(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;
427   string next_marker;
428
429   /* list chains, verify truncated */
430   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 8, true, entries, &truncated, next_marker));
431   ASSERT_EQ(8, (int)entries.size());
432   ASSERT_EQ(1, truncated);
433
434   entries.clear();
435   next_marker.clear();
436
437   /* list all chains, verify not truncated */
438   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 10, true, entries, &truncated, next_marker));
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
475 TEST(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
553 TEST(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;
581   string next_marker;
582
583   /* list chains, verify num entries as expected */
584   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
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();
595   next_marker.clear();
596
597   /* verify list doesn't show deferred entry (this may fail if cluster is thrashing) */
598   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
599   ASSERT_EQ(0, (int)entries.size());
600   ASSERT_EQ(0, truncated);
601
602   /* wait enough */
603   sleep(5);
604   next_marker.clear();
605
606   /* verify list shows deferred entry */
607   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
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();
620   next_marker.clear();
621
622   /* verify entry was removed */
623   ASSERT_EQ(0, cls_rgw_gc_list(ioctx, oid, marker, 1, true, entries, &truncated, next_marker));
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
635 TEST(cls_rgw, finalize)
636 {
637   /* remove pool */
638   ioctx.close();
639   ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
640 }