Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / osd / TestRados.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "common/Mutex.h"
4 #include "common/Cond.h"
5 #include "common/errno.h"
6 #include "common/version.h"
7
8 #include <iostream>
9 #include <sstream>
10 #include <map>
11 #include <numeric>
12 #include <string>
13 #include <vector>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include "test/osd/RadosModel.h"
18
19
20 using namespace std;
21
22 class WeightedTestGenerator : public TestOpGenerator
23 {
24 public:
25
26   WeightedTestGenerator(int ops,
27                         int objects,
28                         map<TestOpType, unsigned int> op_weights,
29                         TestOpStat *stats,
30                         int max_seconds,
31                         bool ec_pool,
32                         bool balance_reads,
33                         bool set_redirect) :
34     m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
35     m_objects(objects), m_stats(stats),
36     m_total_weight(0),
37     m_ec_pool(ec_pool),
38     m_balance_reads(balance_reads),
39     m_set_redirect(set_redirect)
40   {
41     m_start = time(0);
42     for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
43          it != op_weights.end();
44          ++it) {
45       m_total_weight += it->second;
46       m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
47                                                           m_total_weight));
48     }
49     if (m_set_redirect) {
50       /* create redirect objects + set-redirect*/
51       m_redirect_objects = objects*2; // for copy_from + set-redirect test
52       m_initial_redirected_objects = objects;
53       m_ops = ops+m_redirect_objects+m_initial_redirected_objects;
54     }
55   }
56
57   TestOp *next(RadosTestContext &context) override
58   {
59     TestOp *retval = NULL;
60
61     ++m_op;
62     if (m_op <= m_objects) {
63       stringstream oid;
64       oid << m_op;
65       if (m_op % 2) {
66         // make it a long name
67         oid << " " << string(300, 'o');
68       }
69       cout << m_op << ": write initial oid " << oid.str() << std::endl;
70       context.oid_not_flushing.insert(oid.str());
71       if (m_ec_pool) {
72         return new WriteOp(m_op, &context, oid.str(), true, true);
73       } else {
74         return new WriteOp(m_op, &context, oid.str(), false, true);
75       }
76     } else if (m_op >= m_ops) {
77       return NULL;
78     }
79     
80     if (m_set_redirect) {
81       /*
82        * set-redirect test
83        * 1. create objects (copy from)
84        * 2. set-redirect
85        */
86       int create_objects_end = m_objects + m_redirect_objects;
87       int set_redirect_end = create_objects_end + m_initial_redirected_objects; 
88
89       if (m_op <= create_objects_end) {
90         stringstream oid;
91         int _oid = m_op;
92         oid << _oid;
93         if ((_oid) % 2) {
94           oid << " " << string(300, 'o');
95         }
96         stringstream oid2;
97         int _oid2 = _oid - m_objects;
98         oid2 << _oid2;
99         if ((_oid2) % 2) {
100           oid2 << " " << string(300, 'o');
101         }
102         cout << m_op << ": " << "(create redirect oid) copy_from oid " << oid.str() 
103               << " from oid " << oid2.str() << std::endl;
104         return new CopyFromOp(m_op, &context, oid.str(), oid2.str(), m_stats);
105       } else if (m_op <= set_redirect_end) {
106         stringstream oid;
107         int _oid = m_op-create_objects_end;
108         oid << _oid;
109         if ((_oid) % 2) {
110           oid << " " << string(300, 'o');
111         }
112         stringstream oid2;
113         int _oid2 = _oid + m_objects;
114         oid2 << _oid2;
115         if ((_oid2) % 2) {
116           oid2 << " " << string(300, 'o');
117         }
118         cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid " 
119               << oid2.str() << std::endl;
120         return new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name);
121       } 
122
123       if (!context.oid_redirect_not_in_use.size() && m_op > set_redirect_end) {
124         int set_size = context.oid_not_in_use.size();
125         if (set_size < m_objects + m_redirect_objects) {
126           return NULL;
127         }
128         for (int t_op = m_objects+1; t_op <= create_objects_end; t_op++) {
129           stringstream oid;
130           oid << t_op;
131           if (t_op % 2) {
132             oid << " " << string(300, 'o');
133           }
134           context.oid_not_flushing.erase(oid.str());
135           context.oid_not_in_use.erase(oid.str());
136           context.oid_in_use.erase(oid.str());
137           cout << m_op << ": " << " remove oid " << oid.str() << " from oid_*_use " << std::endl;
138           if (t_op > m_objects + m_initial_redirected_objects) {
139             context.oid_redirect_not_in_use.insert(oid.str());
140           }
141         }
142         cout << m_op << ": " << " oid_not_in_use: " << context.oid_not_in_use.size()
143               << " oid_in_use: " << context.oid_in_use.size() << std::endl;
144       }
145     }
146
147     if (m_nextop) {
148       retval = m_nextop;
149       m_nextop = NULL;
150       return retval;
151     }
152
153     while (retval == NULL) {
154       unsigned int rand_val = rand() % m_total_weight;
155
156       time_t now = time(0);
157       if (m_seconds && now - m_start > m_seconds)
158         break;
159
160       for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin();
161            it != m_weight_sums.end();
162            ++it) {
163         if (rand_val < it->second) {
164           retval = gen_op(context, it->first);
165           break;
166         }
167       }
168     }
169     return retval;
170   }
171
172 private:
173
174   TestOp *gen_op(RadosTestContext &context, TestOpType type)
175   {
176     string oid, oid2;
177     assert(context.oid_not_in_use.size());
178
179     switch (type) {
180     case TEST_OP_READ:
181       oid = *(rand_choose(context.oid_not_in_use));
182       return new ReadOp(m_op, &context, oid, m_balance_reads, m_stats);
183
184     case TEST_OP_WRITE:
185       oid = *(rand_choose(context.oid_not_in_use));
186       cout << m_op << ": " << "write oid " << oid << " current snap is "
187            << context.current_snap << std::endl;
188       return new WriteOp(m_op, &context, oid, false, false, m_stats);
189
190     case TEST_OP_WRITE_EXCL:
191       oid = *(rand_choose(context.oid_not_in_use));
192       cout << m_op << ": " << "write (excl) oid "
193            << oid << " current snap is "
194            << context.current_snap << std::endl;
195       return new WriteOp(m_op, &context, oid, false, true, m_stats);
196
197     case TEST_OP_WRITESAME:
198       oid = *(rand_choose(context.oid_not_in_use));
199       cout << m_op << ": " << "writesame oid "
200            << oid << " current snap is "
201            << context.current_snap << std::endl;
202       return new WriteSameOp(m_op, &context, oid, m_stats);
203
204     case TEST_OP_DELETE:
205       oid = *(rand_choose(context.oid_not_in_use));
206       cout << m_op << ": " << "delete oid " << oid << " current snap is "
207            << context.current_snap << std::endl;
208       return new DeleteOp(m_op, &context, oid, m_stats);
209
210     case TEST_OP_SNAP_CREATE:
211       cout << m_op << ": " << "snap_create" << std::endl;
212       return new SnapCreateOp(m_op, &context, m_stats);
213
214     case TEST_OP_SNAP_REMOVE:
215       if (context.snaps.size() <= context.snaps_in_use.size()) {
216         return NULL;
217       }
218       while (true) {
219         int snap = rand_choose(context.snaps)->first;
220         if (context.snaps_in_use.lookup(snap))
221           continue;  // in use; try again!
222         cout << m_op << ": " << "snap_remove snap " << snap << std::endl;
223         return new SnapRemoveOp(m_op, &context, snap, m_stats);
224       }
225
226     case TEST_OP_ROLLBACK:
227       {
228         string oid = *(rand_choose(context.oid_not_in_use));
229         cout << m_op << ": " << "rollback oid " << oid << " current snap is "
230              << context.current_snap << std::endl;
231         return new RollbackOp(m_op, &context, oid);
232       }
233
234     case TEST_OP_SETATTR:
235       oid = *(rand_choose(context.oid_not_in_use));
236       cout << m_op << ": " << "setattr oid " << oid
237            << " current snap is " << context.current_snap << std::endl;
238       return new SetAttrsOp(m_op, &context, oid, m_stats);
239
240     case TEST_OP_RMATTR:
241       oid = *(rand_choose(context.oid_not_in_use));
242       cout << m_op << ": " << "rmattr oid " << oid
243            << " current snap is " << context.current_snap << std::endl;
244       return new RemoveAttrsOp(m_op, &context, oid, m_stats);
245
246     case TEST_OP_WATCH:
247       oid = *(rand_choose(context.oid_not_in_use));
248       cout << m_op << ": " << "watch oid " << oid
249            << " current snap is " << context.current_snap << std::endl;
250       return new WatchOp(m_op, &context, oid, m_stats);
251
252     case TEST_OP_COPY_FROM:
253       oid = *(rand_choose(context.oid_not_in_use));
254       do {
255         oid2 = *(rand_choose(context.oid_not_in_use));
256       } while (oid == oid2);
257       cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2
258            << " current snap is " << context.current_snap << std::endl;
259       return new CopyFromOp(m_op, &context, oid, oid2, m_stats);
260
261     case TEST_OP_HIT_SET_LIST:
262       {
263         uint32_t hash = rjhash32(rand());
264         cout << m_op << ": " << "hit_set_list " << hash << std::endl;
265         return new HitSetListOp(m_op, &context, hash, m_stats);
266       }
267
268     case TEST_OP_UNDIRTY:
269       {
270         oid = *(rand_choose(context.oid_not_in_use));
271         cout << m_op << ": " << "undirty oid " << oid << std::endl;
272         return new UndirtyOp(m_op, &context, oid, m_stats);
273       }
274
275     case TEST_OP_IS_DIRTY:
276       {
277         oid = *(rand_choose(context.oid_not_flushing));
278         return new IsDirtyOp(m_op, &context, oid, m_stats);
279       }
280
281     case TEST_OP_CACHE_FLUSH:
282       {
283         oid = *(rand_choose(context.oid_not_in_use));
284         return new CacheFlushOp(m_op, &context, oid, m_stats, true);
285       }
286
287     case TEST_OP_CACHE_TRY_FLUSH:
288       {
289         oid = *(rand_choose(context.oid_not_in_use));
290         return new CacheFlushOp(m_op, &context, oid, m_stats, false);
291       }
292
293     case TEST_OP_CACHE_EVICT:
294       {
295         oid = *(rand_choose(context.oid_not_in_use));
296         return new CacheEvictOp(m_op, &context, oid, m_stats);
297       }
298
299     case TEST_OP_APPEND:
300       oid = *(rand_choose(context.oid_not_in_use));
301       cout << "append oid " << oid << " current snap is "
302            << context.current_snap << std::endl;
303       return new WriteOp(m_op, &context, oid, true, false, m_stats);
304
305     case TEST_OP_APPEND_EXCL:
306       oid = *(rand_choose(context.oid_not_in_use));
307       cout << "append oid (excl) " << oid << " current snap is "
308            << context.current_snap << std::endl;
309       return new WriteOp(m_op, &context, oid, true, true, m_stats);
310
311     case TEST_OP_SET_REDIRECT:
312       oid = *(rand_choose(context.oid_not_in_use));
313       oid2 = *(rand_choose(context.oid_redirect_not_in_use));
314       cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl;
315       return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats);
316
317     case TEST_OP_UNSET_REDIRECT:
318       oid = *(rand_choose(context.oid_not_in_use));
319       cout << m_op << ": " << "unset_redirect oid " << oid << std::endl;
320       return new UnsetRedirectOp(m_op, &context, oid, m_stats);
321
322     default:
323       cerr << m_op << ": Invalid op type " << type << std::endl;
324       ceph_abort();
325       return nullptr;
326     }
327   }
328
329   TestOp *m_nextop;
330   int m_op;
331   int m_ops;
332   int m_seconds;
333   int m_objects;
334   time_t m_start;
335   TestOpStat *m_stats;
336   map<TestOpType, unsigned int> m_weight_sums;
337   unsigned int m_total_weight;
338   bool m_ec_pool;
339   bool m_balance_reads;
340   bool m_set_redirect;
341   int m_redirect_objects;
342   int m_initial_redirected_objects; 
343 };
344
345 int main(int argc, char **argv)
346 {
347   int ops = 1000;
348   int objects = 50;
349   int max_in_flight = 16;
350   int64_t size = 4000000; // 4 MB
351   int64_t min_stride_size = -1, max_stride_size = -1;
352   int max_seconds = 0;
353   bool pool_snaps = false;
354   bool write_fadvise_dontneed = false;
355
356   struct {
357     TestOpType op;
358     const char *name;
359     bool ec_pool_valid;
360   } op_types[] = {
361     { TEST_OP_READ, "read", true },
362     { TEST_OP_WRITE, "write", false },
363     { TEST_OP_WRITE_EXCL, "write_excl", false },
364     { TEST_OP_WRITESAME, "writesame", false },
365     { TEST_OP_DELETE, "delete", true },
366     { TEST_OP_SNAP_CREATE, "snap_create", true },
367     { TEST_OP_SNAP_REMOVE, "snap_remove", true },
368     { TEST_OP_ROLLBACK, "rollback", true },
369     { TEST_OP_SETATTR, "setattr", true },
370     { TEST_OP_RMATTR, "rmattr", true },
371     { TEST_OP_WATCH, "watch", true },
372     { TEST_OP_COPY_FROM, "copy_from", true },
373     { TEST_OP_HIT_SET_LIST, "hit_set_list", true },
374     { TEST_OP_IS_DIRTY, "is_dirty", true },
375     { TEST_OP_UNDIRTY, "undirty", true },
376     { TEST_OP_CACHE_FLUSH, "cache_flush", true },
377     { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true },
378     { TEST_OP_CACHE_EVICT, "cache_evict", true },
379     { TEST_OP_APPEND, "append", true },
380     { TEST_OP_APPEND_EXCL, "append_excl", true },
381     { TEST_OP_SET_REDIRECT, "set_redirect", true },
382     { TEST_OP_UNSET_REDIRECT, "unset_redirect", true },
383     { TEST_OP_READ /* grr */, NULL },
384   };
385
386   map<TestOpType, unsigned int> op_weights;
387   string pool_name = "rbd";
388   bool ec_pool = false;
389   bool no_omap = false;
390   bool no_sparse = false;
391   bool balance_reads = false;
392   bool set_redirect = false;
393
394   for (int i = 1; i < argc; ++i) {
395     if (strcmp(argv[i], "--max-ops") == 0)
396       ops = atoi(argv[++i]);
397     else if (strcmp(argv[i], "--pool") == 0)
398       pool_name = argv[++i];
399     else if (strcmp(argv[i], "--max-seconds") == 0)
400       max_seconds = atoi(argv[++i]);
401     else if (strcmp(argv[i], "--objects") == 0)
402       objects = atoi(argv[++i]);
403     else if (strcmp(argv[i], "--max-in-flight") == 0)
404       max_in_flight = atoi(argv[++i]);
405     else if (strcmp(argv[i], "--size") == 0)
406       size = atoi(argv[++i]);
407     else if (strcmp(argv[i], "--min-stride-size") == 0)
408       min_stride_size = atoi(argv[++i]);
409     else if (strcmp(argv[i], "--max-stride-size") == 0)
410       max_stride_size = atoi(argv[++i]);
411     else if (strcmp(argv[i], "--no-omap") == 0)
412       no_omap = true;
413     else if (strcmp(argv[i], "--no-sparse") == 0)
414       no_sparse = true;
415     else if (strcmp(argv[i], "--balance_reads") == 0)
416       balance_reads = true;
417     else if (strcmp(argv[i], "--pool-snaps") == 0)
418       pool_snaps = true;
419     else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0)
420       write_fadvise_dontneed = true;
421     else if (strcmp(argv[i], "--ec-pool") == 0) {
422       if (!op_weights.empty()) {
423         cerr << "--ec-pool must be specified prior to any ops" << std::endl;
424         exit(1);
425       }
426       ec_pool = true;
427       no_omap = true;
428       no_sparse = true;
429     } else if (strcmp(argv[i], "--op") == 0) {
430       i++;
431       if (i == argc) {
432         cerr << "Missing op after --op" << std::endl;
433         return 1;
434       }
435       int j;
436       for (j = 0; op_types[j].name; ++j) {
437         if (strcmp(op_types[j].name, argv[i]) == 0) {
438           break;
439         }
440       }
441       if (!op_types[j].name) {
442         cerr << "unknown op " << argv[i] << std::endl;
443         exit(1);
444       }
445       i++;
446       if (i == argc) {
447         cerr << "Weight unspecified." << std::endl;
448         return 1;
449       }
450       int weight = atoi(argv[i]);
451       if (weight < 0) {
452         cerr << "Weights must be nonnegative." << std::endl;
453         return 1;
454       } else if (weight > 0) {
455         if (ec_pool && !op_types[j].ec_pool_valid) {
456           cerr << "Error: cannot use op type " << op_types[j].name
457                << " with --ec-pool" << std::endl;
458           exit(1);
459         }
460         cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
461         op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
462       }
463     } else if (strcmp(argv[i], "--set_redirect") == 0) {
464       set_redirect = true;
465     } else {
466       cerr << "unknown arg " << argv[i] << std::endl;
467       exit(1);
468     }
469   }
470
471   if (op_weights.empty()) {
472     cerr << "No operations specified" << std::endl;
473     exit(1);
474   }
475
476   if (min_stride_size < 0)
477     min_stride_size = size / 10;
478   if (max_stride_size < 0)
479     max_stride_size = size / 5;
480
481   cout << pretty_version_to_str() << std::endl;
482   cout << "Configuration:" << std::endl
483        << "\tNumber of operations: " << ops << std::endl
484        << "\tNumber of objects: " << objects << std::endl
485        << "\tMax in flight operations: " << max_in_flight << std::endl
486        << "\tObject size (in bytes): " << size << std::endl
487        << "\tWrite stride min: " << min_stride_size << std::endl
488        << "\tWrite stride max: " << max_stride_size << std::endl;
489
490   if (min_stride_size >= max_stride_size) {
491     cerr << "Error: max_stride_size must be more than min_stride_size"
492          << std::endl;
493     return 1;
494   }
495
496   if (min_stride_size > size || max_stride_size > size) {
497     cerr << "Error: min_stride_size and max_stride_size must be "
498          << "smaller than object size" << std::endl;
499     return 1;
500   }
501
502   if (max_in_flight * 2 > objects) {
503     cerr << "Error: max_in_flight must be <= than the number of objects / 2"
504          << std::endl;
505     return 1;
506   }
507
508   char *id = getenv("CEPH_CLIENT_ID");
509   RadosTestContext context(
510     pool_name,
511     max_in_flight,
512     size,
513     min_stride_size,
514     max_stride_size,
515     no_omap,
516     no_sparse,
517     pool_snaps,
518     write_fadvise_dontneed,
519     id);
520
521   TestOpStat stats;
522   WeightedTestGenerator gen = WeightedTestGenerator(
523     ops, objects,
524     op_weights, &stats, max_seconds,
525     ec_pool, balance_reads, set_redirect);
526   int r = context.init();
527   if (r < 0) {
528     cerr << "Error initializing rados test context: "
529          << cpp_strerror(r) << std::endl;
530     exit(1);
531   }
532   context.loop(&gen);
533
534   context.shutdown();
535   cerr << context.errors << " errors." << std::endl;
536   cerr << stats << std::endl;
537   return 0;
538 }