]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/osd/TestRados.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / 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 bool set_chunk) :
35 m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
36 m_objects(objects), m_stats(stats),
37 m_total_weight(0),
38 m_ec_pool(ec_pool),
39 m_balance_reads(balance_reads),
40 m_set_redirect(set_redirect),
41 m_set_chunk(set_chunk)
42 {
43 m_start = time(0);
44 for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
45 it != op_weights.end();
46 ++it) {
47 m_total_weight += it->second;
48 m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
49 m_total_weight));
50 }
51 if (m_set_redirect || m_set_chunk) {
52 if (m_set_redirect) {
53 m_ops = ops+m_objects+m_objects;
54 } else {
55 /* create 10 chunks per an object*/
56 m_ops = ops+m_objects+m_objects*10;
57 }
58 }
59 }
60
61 TestOp *next(RadosTestContext &context) override
62 {
63 TestOp *retval = NULL;
64
65 ++m_op;
66 if (m_op <= m_objects && !m_set_redirect && !m_set_chunk ) {
67 stringstream oid;
68 oid << m_op;
69 if (m_op % 2) {
70 // make it a long name
71 oid << " " << string(300, 'o');
72 }
73 cout << m_op << ": write initial oid " << oid.str() << std::endl;
74 context.oid_not_flushing.insert(oid.str());
75 if (m_ec_pool) {
76 return new WriteOp(m_op, &context, oid.str(), true, true);
77 } else {
78 return new WriteOp(m_op, &context, oid.str(), false, true);
79 }
80 } else if (m_op >= m_ops) {
81 return NULL;
82 }
83
84 if (m_set_redirect || m_set_chunk) {
85 if (init_extensible_tier(context, retval)) {
86 return retval;
87 }
88 }
89
90 if (m_nextop) {
91 retval = m_nextop;
92 m_nextop = NULL;
93 return retval;
94 }
95
96 while (retval == NULL) {
97 unsigned int rand_val = rand() % m_total_weight;
98
99 time_t now = time(0);
100 if (m_seconds && now - m_start > m_seconds)
101 break;
102
103 for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin();
104 it != m_weight_sums.end();
105 ++it) {
106 if (rand_val < it->second) {
107 retval = gen_op(context, it->first);
108 break;
109 }
110 }
111 }
112 return retval;
113 }
114
115 bool init_extensible_tier(RadosTestContext &context, TestOp *& op) {
116 /*
117 * set-redirect or set-chunk test (manifest test)
118 * 0. make default objects (using create op)
119 * 1. set-redirect or set-chunk
120 * 2. initialize target objects (using write op)
121 * 3. wait for set-* completion
122 */
123 int copy_manifest_end = 0;
124 if (m_set_chunk) {
125 copy_manifest_end = m_objects*2;
126 } else {
127 copy_manifest_end = m_objects*3;
128 }
129 int make_manifest_end = copy_manifest_end;
130 if (m_set_chunk) {
131 /* make 10 chunks per an object*/
132 make_manifest_end = make_manifest_end + m_objects * 10;
133 } else {
134 /* redirect */
135 make_manifest_end = make_manifest_end + m_objects;
136 }
137
138 if (m_op <= m_objects) {
139 stringstream oid;
140 oid << m_op;
141 if (m_op % 2) {
142 oid << " " << string(300, 'o');
143 }
144 cout << m_op << ": write initial oid " << oid.str() << std::endl;
145 context.oid_not_flushing.insert(oid.str());
146 if (m_ec_pool) {
147 op = new WriteOp(m_op, &context, oid.str(), true, true);
148 } else {
149 op = new WriteOp(m_op, &context, oid.str(), false, true);
150 }
151 return true;
152 } else if (m_op <= copy_manifest_end) {
153 stringstream oid, oid2;
154 //int _oid = m_op-m_objects;
155 int _oid = m_op % m_objects + 1;
156 oid << _oid;
157 if ((_oid) % 2) {
158 oid << " " << string(300, 'o');
159 }
160 int _oid2 = m_op - m_objects + 1;
161 if (_oid2 > copy_manifest_end - m_objects) {
162 _oid2 -= (copy_manifest_end - m_objects);
163 }
164 oid2 << _oid2 << " " << context.low_tier_pool_name;
165 if ((_oid2) % 2) {
166 oid2 << " " << string(300, 'm');
167 }
168 cout << m_op << ": " << "copy oid " << oid.str() << " target oid "
169 << oid2.str() << std::endl;
170 op = new CopyOp(m_op, &context, oid.str(), oid2.str(), context.low_tier_pool_name);
171 return true;
172 } else if (m_op <= make_manifest_end) {
173 if (m_set_redirect) {
174 stringstream oid, oid2;
175 int _oid = m_op-copy_manifest_end;
176 oid << _oid;
177 if ((_oid) % 2) {
178 oid << " " << string(300, 'o');
179 }
180 oid2 << _oid << " " << context.low_tier_pool_name;
181 if ((_oid) % 2) {
182 oid2 << " " << string(300, 'm');
183 }
184 if (context.oid_in_use.count(oid.str())) {
185 /* previous copy is not finished */
186 op = NULL;
187 m_op--;
188 cout << m_op << " retry set_redirect !" << std::endl;
189 return true;
190 }
191 cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid "
192 << oid2.str() << std::endl;
193 op = new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name);
194 return true;
195 } else if (m_set_chunk) {
196 stringstream oid;
197 int _oid = m_op % m_objects +1;
198 oid << _oid;
199 if ((_oid) % 2) {
200 oid << " " << string(300, 'o');
201 }
202 if (context.oid_in_use.count(oid.str())) {
203 /* previous set-chunk is not finished */
204 op = NULL;
205 m_op--;
206 cout << m_op << " retry set_chunk !" << std::endl;
207 return true;
208 }
209 stringstream oid2;
210 oid2 << _oid << " " << context.low_tier_pool_name;
211 if ((_oid) % 2) {
212 oid2 << " " << string(300, 'm');
213 }
214
215 /* make a chunk (random offset, random length -->
216 * target object's random offset)
217 */
218 ObjectDesc contents, contents2;
219 context.find_object(oid.str(), &contents);
220 uint32_t max_len = contents.most_recent_gen()->get_length(contents.most_recent());
221 uint32_t rand_offset = rand() % max_len;
222 uint32_t rand_length = rand() % max_len;
223 rand_offset = rand_offset - (rand_offset % 512);
224 rand_length = rand_length - (rand_length % 512);
225
226 while (rand_offset + rand_length > max_len || rand_length == 0) {
227 rand_offset = rand() % max_len;
228 rand_length = rand() % max_len;
229 rand_offset = rand_offset - (rand_offset % 512);
230 rand_length = rand_length - (rand_length % 512);
231 }
232 uint32_t rand_tgt_offset = rand_offset;
233 cout << m_op << ": " << "set_chunk oid " << oid.str() << " offset: " << rand_offset
234 << " length: " << rand_length << " target oid " << oid2.str()
235 << " tgt_offset: " << rand_tgt_offset << std::endl;
236 op = new SetChunkOp(m_op, &context, oid.str(), rand_offset, rand_length, oid2.str(),
237 context.low_tier_pool_name, rand_tgt_offset, m_stats);
238 return true;
239 }
240 } else if (m_op == make_manifest_end + 1) {
241 int set_size = context.oid_not_in_use.size();
242 int set_manifest_size = context.oid_redirect_not_in_use.size();
243 cout << m_op << " oid_not_in_use " << set_size << " oid_redirect_not_in_use " << set_manifest_size << std::endl;
244 /* wait for redirect or set_chunk initialization */
245 if (set_size != m_objects || set_manifest_size != 0) {
246 op = NULL;
247 m_op--;
248 cout << m_op << " wait for manifest initialization " << std::endl;
249 return true;
250 }
251 for (int t_op = m_objects+1; t_op <= m_objects*2; t_op++) {
252 stringstream oid;
253 oid << t_op << " " << context.low_tier_pool_name;
254 if (t_op % 2) {
255 oid << " " << string(300, 'm');
256 }
257 cout << " redirect_not_in_use: " << oid.str() << std::endl;
258 context.oid_redirect_not_in_use.insert(oid.str());
259 }
260 }
261
262 return false;
263 }
264
265 private:
266
267 TestOp *gen_op(RadosTestContext &context, TestOpType type)
268 {
269 string oid, oid2;
270 ceph_assert(context.oid_not_in_use.size());
271
272 switch (type) {
273 case TEST_OP_READ:
274 oid = *(rand_choose(context.oid_not_in_use));
275 return new ReadOp(m_op, &context, oid, m_balance_reads, m_stats);
276
277 case TEST_OP_WRITE:
278 oid = *(rand_choose(context.oid_not_in_use));
279 cout << m_op << ": " << "write oid " << oid << " current snap is "
280 << context.current_snap << std::endl;
281 return new WriteOp(m_op, &context, oid, false, false, m_stats);
282
283 case TEST_OP_WRITE_EXCL:
284 oid = *(rand_choose(context.oid_not_in_use));
285 cout << m_op << ": " << "write (excl) oid "
286 << oid << " current snap is "
287 << context.current_snap << std::endl;
288 return new WriteOp(m_op, &context, oid, false, true, m_stats);
289
290 case TEST_OP_WRITESAME:
291 oid = *(rand_choose(context.oid_not_in_use));
292 cout << m_op << ": " << "writesame oid "
293 << oid << " current snap is "
294 << context.current_snap << std::endl;
295 return new WriteSameOp(m_op, &context, oid, m_stats);
296
297 case TEST_OP_DELETE:
298 oid = *(rand_choose(context.oid_not_in_use));
299 cout << m_op << ": " << "delete oid " << oid << " current snap is "
300 << context.current_snap << std::endl;
301 return new DeleteOp(m_op, &context, oid, m_stats);
302
303 case TEST_OP_SNAP_CREATE:
304 cout << m_op << ": " << "snap_create" << std::endl;
305 return new SnapCreateOp(m_op, &context, m_stats);
306
307 case TEST_OP_SNAP_REMOVE:
308 if (context.snaps.size() <= context.snaps_in_use.size()) {
309 return NULL;
310 }
311 while (true) {
312 int snap = rand_choose(context.snaps)->first;
313 if (context.snaps_in_use.lookup(snap))
314 continue; // in use; try again!
315 cout << m_op << ": " << "snap_remove snap " << snap << std::endl;
316 return new SnapRemoveOp(m_op, &context, snap, m_stats);
317 }
318
319 case TEST_OP_ROLLBACK:
320 {
321 string oid = *(rand_choose(context.oid_not_in_use));
322 cout << m_op << ": " << "rollback oid " << oid << " current snap is "
323 << context.current_snap << std::endl;
324 return new RollbackOp(m_op, &context, oid);
325 }
326
327 case TEST_OP_SETATTR:
328 oid = *(rand_choose(context.oid_not_in_use));
329 cout << m_op << ": " << "setattr oid " << oid
330 << " current snap is " << context.current_snap << std::endl;
331 return new SetAttrsOp(m_op, &context, oid, m_stats);
332
333 case TEST_OP_RMATTR:
334 oid = *(rand_choose(context.oid_not_in_use));
335 cout << m_op << ": " << "rmattr oid " << oid
336 << " current snap is " << context.current_snap << std::endl;
337 return new RemoveAttrsOp(m_op, &context, oid, m_stats);
338
339 case TEST_OP_WATCH:
340 oid = *(rand_choose(context.oid_not_in_use));
341 cout << m_op << ": " << "watch oid " << oid
342 << " current snap is " << context.current_snap << std::endl;
343 return new WatchOp(m_op, &context, oid, m_stats);
344
345 case TEST_OP_COPY_FROM:
346 oid = *(rand_choose(context.oid_not_in_use));
347 do {
348 oid2 = *(rand_choose(context.oid_not_in_use));
349 } while (oid == oid2);
350 cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2
351 << " current snap is " << context.current_snap << std::endl;
352 return new CopyFromOp(m_op, &context, oid, oid2, m_stats);
353
354 case TEST_OP_HIT_SET_LIST:
355 {
356 uint32_t hash = rjhash32(rand());
357 cout << m_op << ": " << "hit_set_list " << hash << std::endl;
358 return new HitSetListOp(m_op, &context, hash, m_stats);
359 }
360
361 case TEST_OP_UNDIRTY:
362 {
363 oid = *(rand_choose(context.oid_not_in_use));
364 cout << m_op << ": " << "undirty oid " << oid << std::endl;
365 return new UndirtyOp(m_op, &context, oid, m_stats);
366 }
367
368 case TEST_OP_IS_DIRTY:
369 {
370 oid = *(rand_choose(context.oid_not_flushing));
371 return new IsDirtyOp(m_op, &context, oid, m_stats);
372 }
373
374 case TEST_OP_CACHE_FLUSH:
375 {
376 oid = *(rand_choose(context.oid_not_in_use));
377 return new CacheFlushOp(m_op, &context, oid, m_stats, true);
378 }
379
380 case TEST_OP_CACHE_TRY_FLUSH:
381 {
382 oid = *(rand_choose(context.oid_not_in_use));
383 return new CacheFlushOp(m_op, &context, oid, m_stats, false);
384 }
385
386 case TEST_OP_CACHE_EVICT:
387 {
388 oid = *(rand_choose(context.oid_not_in_use));
389 return new CacheEvictOp(m_op, &context, oid, m_stats);
390 }
391
392 case TEST_OP_APPEND:
393 oid = *(rand_choose(context.oid_not_in_use));
394 cout << "append oid " << oid << " current snap is "
395 << context.current_snap << std::endl;
396 return new WriteOp(m_op, &context, oid, true, false, m_stats);
397
398 case TEST_OP_APPEND_EXCL:
399 oid = *(rand_choose(context.oid_not_in_use));
400 cout << "append oid (excl) " << oid << " current snap is "
401 << context.current_snap << std::endl;
402 return new WriteOp(m_op, &context, oid, true, true, m_stats);
403
404 case TEST_OP_CHUNK_READ:
405 oid = *(rand_choose(context.oid_not_in_use));
406 cout << m_op << ": " << "chunk read oid " << oid << " target oid " << oid2 << std::endl;
407 return new ChunkReadOp(m_op, &context, oid, context.pool_name, false, m_stats);
408
409 case TEST_OP_TIER_PROMOTE:
410 oid = *(rand_choose(context.oid_not_in_use));
411 cout << m_op << ": " << "tier_promote oid " << oid << std::endl;
412 return new TierPromoteOp(m_op, &context, oid, m_stats);
413
414 case TEST_OP_SET_REDIRECT:
415 oid = *(rand_choose(context.oid_not_in_use));
416 oid2 = *(rand_choose(context.oid_redirect_not_in_use));
417 cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl;
418 return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats);
419
420 case TEST_OP_UNSET_REDIRECT:
421 oid = *(rand_choose(context.oid_not_in_use));
422 cout << m_op << ": " << "unset_redirect oid " << oid << std::endl;
423 return new UnsetRedirectOp(m_op, &context, oid, m_stats);
424
425 default:
426 cerr << m_op << ": Invalid op type " << type << std::endl;
427 ceph_abort();
428 return nullptr;
429 }
430 }
431
432 TestOp *m_nextop;
433 int m_op;
434 int m_ops;
435 int m_seconds;
436 int m_objects;
437 time_t m_start;
438 TestOpStat *m_stats;
439 map<TestOpType, unsigned int> m_weight_sums;
440 unsigned int m_total_weight;
441 bool m_ec_pool;
442 bool m_balance_reads;
443 bool m_set_redirect;
444 bool m_set_chunk;
445 };
446
447 int main(int argc, char **argv)
448 {
449 int ops = 1000;
450 int objects = 50;
451 int max_in_flight = 16;
452 int64_t size = 4000000; // 4 MB
453 int64_t min_stride_size = -1, max_stride_size = -1;
454 int max_seconds = 0;
455 bool pool_snaps = false;
456 bool write_fadvise_dontneed = false;
457
458 struct {
459 TestOpType op;
460 const char *name;
461 bool ec_pool_valid;
462 } op_types[] = {
463 { TEST_OP_READ, "read", true },
464 { TEST_OP_WRITE, "write", false },
465 { TEST_OP_WRITE_EXCL, "write_excl", false },
466 { TEST_OP_WRITESAME, "writesame", false },
467 { TEST_OP_DELETE, "delete", true },
468 { TEST_OP_SNAP_CREATE, "snap_create", true },
469 { TEST_OP_SNAP_REMOVE, "snap_remove", true },
470 { TEST_OP_ROLLBACK, "rollback", true },
471 { TEST_OP_SETATTR, "setattr", true },
472 { TEST_OP_RMATTR, "rmattr", true },
473 { TEST_OP_WATCH, "watch", true },
474 { TEST_OP_COPY_FROM, "copy_from", true },
475 { TEST_OP_HIT_SET_LIST, "hit_set_list", true },
476 { TEST_OP_IS_DIRTY, "is_dirty", true },
477 { TEST_OP_UNDIRTY, "undirty", true },
478 { TEST_OP_CACHE_FLUSH, "cache_flush", true },
479 { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true },
480 { TEST_OP_CACHE_EVICT, "cache_evict", true },
481 { TEST_OP_APPEND, "append", true },
482 { TEST_OP_APPEND_EXCL, "append_excl", true },
483 { TEST_OP_SET_REDIRECT, "set_redirect", true },
484 { TEST_OP_UNSET_REDIRECT, "unset_redirect", true },
485 { TEST_OP_CHUNK_READ, "chunk_read", true },
486 { TEST_OP_TIER_PROMOTE, "tier_promote", true },
487 { TEST_OP_READ /* grr */, NULL },
488 };
489
490 map<TestOpType, unsigned int> op_weights;
491 string pool_name = "rbd";
492 string low_tier_pool_name = "";
493 bool ec_pool = false;
494 bool no_omap = false;
495 bool no_sparse = false;
496 bool balance_reads = false;
497 bool set_redirect = false;
498 bool set_chunk = false;
499
500 for (int i = 1; i < argc; ++i) {
501 if (strcmp(argv[i], "--max-ops") == 0)
502 ops = atoi(argv[++i]);
503 else if (strcmp(argv[i], "--pool") == 0)
504 pool_name = argv[++i];
505 else if (strcmp(argv[i], "--max-seconds") == 0)
506 max_seconds = atoi(argv[++i]);
507 else if (strcmp(argv[i], "--objects") == 0)
508 objects = atoi(argv[++i]);
509 else if (strcmp(argv[i], "--max-in-flight") == 0)
510 max_in_flight = atoi(argv[++i]);
511 else if (strcmp(argv[i], "--size") == 0)
512 size = atoi(argv[++i]);
513 else if (strcmp(argv[i], "--min-stride-size") == 0)
514 min_stride_size = atoi(argv[++i]);
515 else if (strcmp(argv[i], "--max-stride-size") == 0)
516 max_stride_size = atoi(argv[++i]);
517 else if (strcmp(argv[i], "--no-omap") == 0)
518 no_omap = true;
519 else if (strcmp(argv[i], "--no-sparse") == 0)
520 no_sparse = true;
521 else if (strcmp(argv[i], "--balance_reads") == 0)
522 balance_reads = true;
523 else if (strcmp(argv[i], "--pool-snaps") == 0)
524 pool_snaps = true;
525 else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0)
526 write_fadvise_dontneed = true;
527 else if (strcmp(argv[i], "--ec-pool") == 0) {
528 if (!op_weights.empty()) {
529 cerr << "--ec-pool must be specified prior to any ops" << std::endl;
530 exit(1);
531 }
532 ec_pool = true;
533 no_omap = true;
534 no_sparse = true;
535 } else if (strcmp(argv[i], "--op") == 0) {
536 i++;
537 if (i == argc) {
538 cerr << "Missing op after --op" << std::endl;
539 return 1;
540 }
541 int j;
542 for (j = 0; op_types[j].name; ++j) {
543 if (strcmp(op_types[j].name, argv[i]) == 0) {
544 break;
545 }
546 }
547 if (!op_types[j].name) {
548 cerr << "unknown op " << argv[i] << std::endl;
549 exit(1);
550 }
551 i++;
552 if (i == argc) {
553 cerr << "Weight unspecified." << std::endl;
554 return 1;
555 }
556 int weight = atoi(argv[i]);
557 if (weight < 0) {
558 cerr << "Weights must be nonnegative." << std::endl;
559 return 1;
560 } else if (weight > 0) {
561 if (ec_pool && !op_types[j].ec_pool_valid) {
562 cerr << "Error: cannot use op type " << op_types[j].name
563 << " with --ec-pool" << std::endl;
564 exit(1);
565 }
566 cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
567 op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
568 }
569 } else if (strcmp(argv[i], "--set_redirect") == 0) {
570 set_redirect = true;
571 } else if (strcmp(argv[i], "--set_chunk") == 0) {
572 set_chunk = true;
573 } else if (strcmp(argv[i], "--low_tier_pool") == 0) {
574 /*
575 * disallow redirect or chunk object into the same pool
576 * to prevent the race. see https://github.com/ceph/ceph/pull/20096
577 */
578 low_tier_pool_name = argv[++i];
579 } else {
580 cerr << "unknown arg " << argv[i] << std::endl;
581 exit(1);
582 }
583 }
584
585 if (set_redirect || set_chunk) {
586 if (low_tier_pool_name == "") {
587 cerr << "low_tier_pool_name is needed" << std::endl;
588 exit(1);
589 }
590 }
591
592 if (op_weights.empty()) {
593 cerr << "No operations specified" << std::endl;
594 exit(1);
595 }
596
597 if (min_stride_size < 0)
598 min_stride_size = size / 10;
599 if (max_stride_size < 0)
600 max_stride_size = size / 5;
601
602 cout << pretty_version_to_str() << std::endl;
603 cout << "Configuration:" << std::endl
604 << "\tNumber of operations: " << ops << std::endl
605 << "\tNumber of objects: " << objects << std::endl
606 << "\tMax in flight operations: " << max_in_flight << std::endl
607 << "\tObject size (in bytes): " << size << std::endl
608 << "\tWrite stride min: " << min_stride_size << std::endl
609 << "\tWrite stride max: " << max_stride_size << std::endl;
610
611 if (min_stride_size >= max_stride_size) {
612 cerr << "Error: max_stride_size must be more than min_stride_size"
613 << std::endl;
614 return 1;
615 }
616
617 if (min_stride_size > size || max_stride_size > size) {
618 cerr << "Error: min_stride_size and max_stride_size must be "
619 << "smaller than object size" << std::endl;
620 return 1;
621 }
622
623 if (max_in_flight * 2 > objects) {
624 cerr << "Error: max_in_flight must be <= than the number of objects / 2"
625 << std::endl;
626 return 1;
627 }
628
629 char *id = getenv("CEPH_CLIENT_ID");
630 RadosTestContext context(
631 pool_name,
632 max_in_flight,
633 size,
634 min_stride_size,
635 max_stride_size,
636 no_omap,
637 no_sparse,
638 pool_snaps,
639 write_fadvise_dontneed,
640 low_tier_pool_name,
641 id);
642
643 TestOpStat stats;
644 WeightedTestGenerator gen = WeightedTestGenerator(
645 ops, objects,
646 op_weights, &stats, max_seconds,
647 ec_pool, balance_reads, set_redirect, set_chunk);
648 int r = context.init();
649 if (r < 0) {
650 cerr << "Error initializing rados test context: "
651 << cpp_strerror(r) << std::endl;
652 exit(1);
653 }
654 context.loop(&gen);
655
656 context.shutdown();
657 cerr << context.errors << " errors." << std::endl;
658 cerr << stats << std::endl;
659 return 0;
660 }