Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / objectstore / DeterministicOpSequence.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2012 New Dream Network
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 */
13 #include <stdio.h>
14 #include <string.h>
15 #include <iostream>
16 #include <fstream>
17 #include <time.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <sstream>
21 #include "os/ObjectStore.h"
22 #include "common/ceph_argparse.h"
23 #include "global/global_init.h"
24 #include "common/debug.h"
25 #include <boost/scoped_ptr.hpp>
26 #include <boost/lexical_cast.hpp>
27
28 #include "DeterministicOpSequence.h"
29 #include "common/config.h"
30 #include "include/assert.h"
31
32 #define dout_context g_ceph_context
33 #define dout_subsys ceph_subsys_filestore
34 #undef dout_prefix
35 #define dout_prefix *_dout << "deterministic_seq "
36
37 DeterministicOpSequence::DeterministicOpSequence(ObjectStore *store,
38                                                  std::string status)
39   : TestObjectStoreState(store),
40     txn(0),
41     m_osr("OSR")
42 {
43   txn_object = hobject_t(sobject_t("txn", CEPH_NOSNAP));
44
45   if (!status.empty())
46     m_status.open(status.c_str());
47 }
48
49 DeterministicOpSequence::~DeterministicOpSequence()
50 {
51   // TODO Auto-generated destructor stub
52 }
53
54 bool DeterministicOpSequence::run_one_op(int op, rngen_t& gen)
55 {
56   bool ok = false;
57   switch (op) {
58   case DSOP_TOUCH:
59     ok = do_touch(gen);
60     break;
61   case DSOP_WRITE:
62     ok = do_write(gen);
63     break;
64   case DSOP_CLONE:
65     ok = do_clone(gen);
66     break;
67   case DSOP_CLONE_RANGE:
68     ok = do_clone_range(gen);
69     break;
70   case DSOP_OBJ_REMOVE:
71     ok = do_remove(gen);
72     break;
73   case DSOP_COLL_MOVE:
74     ok = do_coll_move(gen);
75     break;
76   case DSOP_SET_ATTRS:
77     ok = do_set_attrs(gen);
78     break;
79   case DSOP_COLL_CREATE:
80     ok = do_coll_create(gen);
81     break;
82
83   default:
84     assert(0 == "bad op");
85   }
86   return ok;
87 }
88
89 void DeterministicOpSequence::generate(int seed, int num_txs)
90 {
91   std::ostringstream ss;
92   ss << "generate run " << num_txs << " --seed " << seed;
93
94   if (m_status.is_open()) {
95     m_status << ss.str() << std::endl;
96     m_status.flush();
97   }
98
99   dout(0) << ss.str() << dendl;
100
101   rngen_t gen(seed);
102   boost::uniform_int<> op_rng(DSOP_FIRST, DSOP_LAST);
103
104   for (txn = 1; txn <= num_txs; ) {
105     int op = op_rng(gen);
106     _print_status(txn, op);
107     dout(0) << "generate seq " << txn << " op " << op << dendl;
108     if (run_one_op(op, gen))
109       txn++;
110   }
111 }
112
113 void DeterministicOpSequence::_print_status(int seq, int op)
114 {
115   if (!m_status.is_open())
116     return;
117   m_status << seq << " " << op << std::endl;
118   m_status.flush();
119 }
120
121 int DeterministicOpSequence::_gen_coll_id(rngen_t& gen)
122 {
123   boost::uniform_int<> coll_rng(0, m_collections_ids.size()-1);
124   return coll_rng(gen);
125 }
126
127 int DeterministicOpSequence::_gen_obj_id(rngen_t& gen)
128 {
129   boost::uniform_int<> obj_rng(0, m_num_objects - 1);
130   return obj_rng(gen);
131 }
132
133 void DeterministicOpSequence::note_txn(ObjectStore::Transaction *t)
134 {
135   bufferlist bl;
136   ::encode(txn, bl);
137   t->truncate(txn_coll, ghobject_t(txn_object), 0);
138   t->write(txn_coll, ghobject_t(txn_object), 0, bl.length(), bl);
139   dout(10) << __func__ << " " << txn << dendl;
140 }
141
142 bool DeterministicOpSequence::do_touch(rngen_t& gen)
143 {
144   int coll_id = _gen_coll_id(gen);
145   int obj_id = _gen_obj_id(gen);
146
147   coll_entry_t *entry = get_coll_at(coll_id);
148   ceph_assert(entry != NULL);
149
150   // Don't care about other collections if already exists
151   if (!entry->check_for_obj(obj_id)) {
152     bool other_found = false;
153     map<int, coll_entry_t*>::iterator it = m_collections.begin();
154     for (; it != m_collections.end(); ++it) {
155       if (it->second->check_for_obj(obj_id)) {
156         ceph_assert(it->first != coll_id);
157         other_found = true;
158       }
159     }
160     if (other_found) {
161       dout(0) << "do_touch new object in collection and exists in another" << dendl;
162       return false;
163     }
164   }
165   hobject_t *obj = entry->touch_obj(obj_id);
166
167   dout(0) << "do_touch " << entry->m_coll.to_str() << "/" << obj->oid.name << dendl;
168
169   _do_touch(entry->m_coll, *obj);
170   return true;
171 }
172
173 bool DeterministicOpSequence::do_remove(rngen_t& gen)
174 {
175   int coll_id = _gen_coll_id(gen);
176
177   coll_entry_t *entry = get_coll_at(coll_id);
178   ceph_assert(entry != NULL);
179
180   if (entry->m_objects.size() == 0) {
181     dout(0) << "do_remove no objects in collection" << dendl;
182     return false;
183   }
184   int obj_id = entry->get_random_obj_id(gen);
185   hobject_t *obj = entry->touch_obj(obj_id);
186   ceph_assert(obj);
187
188   dout(0) << "do_remove " << entry->m_coll.to_str() << "/" << obj->oid.name << dendl;
189
190   _do_remove(entry->m_coll, *obj);
191   hobject_t *rmobj = entry->remove_obj(obj_id);
192   ceph_assert(rmobj);
193   delete rmobj;
194   return true;
195 }
196
197 static void _gen_random(rngen_t& gen,
198                         size_t size, bufferlist& bl) {
199
200   static const char alphanum[] = "0123456789"
201     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
202     "abcdefghijklmnopqrstuvwxyz";
203
204   boost::uniform_int<> char_rng(0, sizeof(alphanum));
205   bufferptr bp(size);
206   for (unsigned int i = 0; i < size - 1; i++) {
207     bp[i] = alphanum[char_rng(gen)];
208   }
209   bp[size - 1] = '\0';
210   bl.append(bp);
211 }
212
213 static void gen_attrs(rngen_t &gen,
214                       map<string, bufferlist> *out) {
215   boost::uniform_int<> num_rng(10, 30);
216   boost::uniform_int<> key_size_rng(5, 10);
217   boost::uniform_int<> val_size_rng(100, 1000);
218   size_t num_attrs = static_cast<size_t>(num_rng(gen));
219   for (size_t i = 0; i < num_attrs; ++i) {
220     size_t key_size = static_cast<size_t>(num_rng(gen));
221     size_t val_size = static_cast<size_t>(num_rng(gen));
222     bufferlist keybl;
223     _gen_random(gen, key_size, keybl);
224     string key(keybl.c_str(), keybl.length());
225     _gen_random(gen, val_size, (*out)[key]);
226   }
227 }
228
229 bool DeterministicOpSequence::do_set_attrs(rngen_t& gen) 
230 {
231   int coll_id = _gen_coll_id(gen);
232
233   coll_entry_t *entry = get_coll_at(coll_id);
234   ceph_assert(entry != NULL);
235
236   if (entry->m_objects.size() == 0) {
237     dout(0) << "do_set_attrs no objects in collection" << dendl;
238     return false;
239   }
240   int obj_id = entry->get_random_obj_id(gen);
241   hobject_t *obj = entry->touch_obj(obj_id);
242   ceph_assert(obj);
243
244   map<string, bufferlist> out;
245   gen_attrs(gen, &out);
246
247   dout(0) << "do_set_attrs " << out.size() << " entries" << dendl;
248   _do_set_attrs(entry->m_coll, *obj, out);
249   return true;
250 }
251
252 bool DeterministicOpSequence::do_write(rngen_t& gen)
253 {
254   int coll_id = _gen_coll_id(gen);
255
256   coll_entry_t *entry = get_coll_at(coll_id);
257   ceph_assert(entry != NULL);
258
259   if (entry->m_objects.size() == 0) {
260     dout(0) << "do_write no objects in collection" << dendl;
261     return false;
262   }
263   int obj_id = entry->get_random_obj_id(gen);
264   hobject_t *obj = entry->touch_obj(obj_id);
265   ceph_assert(obj);
266
267   boost::uniform_int<> size_rng(100, (2 << 19));
268   size_t size = (size_t) size_rng(gen);
269   bufferlist bl;
270   _gen_random(gen, size, bl);
271
272   dout(0) << "do_write " << entry->m_coll.to_str() << "/" << obj->oid.name
273           << " 0~" << size << dendl;
274
275   _do_write(entry->m_coll, *obj, 0, bl.length(), bl);
276   return true;
277 }
278
279 bool DeterministicOpSequence::_prepare_clone(rngen_t& gen,
280                                              coll_t& coll_ret, hobject_t& orig_obj_ret, hobject_t& new_obj_ret)
281 {
282   int coll_id = _gen_coll_id(gen);
283
284   coll_entry_t *entry = get_coll_at(coll_id);
285   ceph_assert(entry != NULL);
286
287   if (entry->m_objects.size() >= 2) {
288     dout(0) << "_prepare_clone coll " << entry->m_coll.to_str()
289             << " doesn't have 2 or more objects" << dendl;
290     return false;
291   }
292
293   int orig_obj_id = entry->get_random_obj_id(gen);
294   hobject_t *orig_obj = entry->touch_obj(orig_obj_id);
295   ceph_assert(orig_obj);
296
297   int id;
298   do {
299     id = entry->get_random_obj_id(gen);
300   } while (id == orig_obj_id);
301   hobject_t *new_obj = entry->touch_obj(id);
302   ceph_assert(new_obj);
303
304   coll_ret = entry->m_coll;
305   orig_obj_ret = *orig_obj;
306   new_obj_ret = *new_obj;
307
308   return true;
309 }
310
311 bool DeterministicOpSequence::do_clone(rngen_t& gen)
312 {
313   coll_t coll;
314   hobject_t orig_obj, new_obj;
315   if (!_prepare_clone(gen, coll, orig_obj, new_obj)) {
316     return false;
317   }
318
319   dout(0) << "do_clone " << coll.to_str() << "/" << orig_obj.oid.name
320       << " => " << coll.to_str() << "/" << new_obj.oid.name << dendl;
321
322   _do_clone(coll, orig_obj, new_obj);
323   return true;
324 }
325
326 bool DeterministicOpSequence::do_clone_range(rngen_t& gen)
327 {
328   coll_t coll;
329   hobject_t orig_obj, new_obj;
330   if (!_prepare_clone(gen, coll, orig_obj, new_obj)) {
331     return false;
332   }
333
334   /* Whenever we have to make a clone_range() operation, just write to the
335    * object first, so we know we have something to clone in the said range.
336    * This may not be the best solution ever, but currently we're not keeping
337    * track of the written-to objects, and until we know for sure we really
338    * need to, let's just focus on the task at hand.
339    */
340
341   boost::uniform_int<> write_size_rng(100, (2 << 19));
342   size_t size = (size_t) write_size_rng(gen);
343   bufferlist bl;
344   _gen_random(gen, size, bl);
345
346   boost::uniform_int<> clone_len(1, bl.length());
347   size = (size_t) clone_len(gen);
348
349   dout(0) << "do_clone_range " << coll.to_str() << "/" << orig_obj.oid.name
350       << " (0~" << size << ")"
351       << " => " << coll.to_str() << "/" << new_obj.oid.name
352       << " (0)" << dendl;
353   _do_write_and_clone_range(coll, orig_obj, new_obj, 0, size, 0, bl);
354   return true;
355 }
356
357 bool DeterministicOpSequence::_prepare_colls(rngen_t& gen,
358                                              coll_entry_t* &orig_coll, coll_entry_t* &new_coll)
359 {
360   ceph_assert(m_collections_ids.size() > 1);
361   int orig_coll_id = _gen_coll_id(gen);
362   int new_coll_id;
363   do {
364     new_coll_id = _gen_coll_id(gen);
365   } while (new_coll_id == orig_coll_id);
366
367   dout(0) << "_prepare_colls from coll id " << orig_coll_id
368       << " to coll id " << new_coll_id << dendl;
369
370   orig_coll = get_coll_at(orig_coll_id);
371   ceph_assert(orig_coll != NULL);
372   new_coll = get_coll_at(new_coll_id);
373   ceph_assert(new_coll != NULL);
374
375   if (!orig_coll->m_objects.size()) {
376     dout(0) << "_prepare_colls coll " << orig_coll->m_coll.to_str()
377         << " has no objects to use" << dendl;
378     return false;
379   }
380
381   return true;
382 }
383
384
385 bool DeterministicOpSequence::do_coll_move(rngen_t& gen)
386 {
387   coll_entry_t *orig_coll = NULL, *new_coll = NULL;
388   if (!_prepare_colls(gen, orig_coll, new_coll))
389     return false;
390
391   ceph_assert(orig_coll && new_coll);
392
393   boost::uniform_int<> obj_rng(0, orig_coll->m_objects.size()-1);
394   int obj_pos = obj_rng(gen);
395   int obj_key = -1;
396   hobject_t *obj = orig_coll->get_obj_at(obj_pos, &obj_key);
397   if (!obj) {
398     dout(0) << "do_coll_move coll " << orig_coll->m_coll.to_str()
399         << " has no object as pos #" << obj_pos << " (key " << obj_key << ")"
400         << dendl;
401     return false;
402   }
403   if (new_coll->check_for_obj(obj_key)) {
404     dout(0) << "do_coll_move coll " << orig_coll->m_coll.to_str()
405         << " already has object as pos #" << obj_pos << " (key " << obj_key << ")"
406         << dendl;
407     return false;
408   }
409   dout(0) << "do_coll_move " << orig_coll->m_coll.to_str() << "/" << obj->oid.name
410         << " => " << new_coll->m_coll.to_str() << "/" << obj->oid.name << dendl;
411   new_coll->touch_obj(obj_key);
412
413   orig_coll->remove_obj(obj_key);
414
415   _do_coll_move(orig_coll->m_coll, new_coll->m_coll, *obj);
416
417   return true;
418 }
419
420 bool DeterministicOpSequence::do_coll_create(rngen_t& gen)
421 {
422   boost::uniform_int<> pg_num_range(0, 512);
423   int pg_num = pg_num_range(gen);
424
425   // Assume there is 7 OSDs in total, the PGs are evenly distributed across those OSDs
426   int pgs = pg_num / 7;
427
428   boost::uniform_int<> num_objs_range(1, 1024);
429   int num_objs = num_objs_range(gen);
430
431   int pool_id = get_next_pool_id();
432   std::set<int> pg_created;
433   for (int i = 0; i < pgs; i++) {
434     boost::uniform_int<> pg_range(0, pg_num - 1);
435     int pg_id = pg_range(gen);
436     if (pg_created.count(pg_id) > 0)
437       continue;
438     _do_coll_create(coll_t(spg_t(pg_t(pg_id,pool_id),shard_id_t::NO_SHARD)),
439                     (uint32_t) pg_num, (uint64_t) num_objs);
440     pg_created.insert(pg_id);
441   }
442   return true;
443 }
444
445 void DeterministicOpSequence::_do_coll_create(coll_t cid, uint32_t pg_num, uint64_t num_objs)
446 {
447   ObjectStore::Transaction t;
448   note_txn(&t);
449   t.create_collection(cid, 32);
450   bufferlist hint;
451   ::encode(pg_num, hint);
452   ::encode(num_objs, hint);
453   t.collection_hint(cid, ObjectStore::Transaction::COLL_HINT_EXPECTED_NUM_OBJECTS, hint);
454   dout(0) << "Give collection: " << cid << " a hint, pg_num is: " << pg_num << ", num_objs is: "
455     << num_objs << dendl;
456
457   m_store->apply_transaction(&m_osr, std::move(t));
458 }
459
460 void DeterministicOpSequence::_do_touch(coll_t coll, hobject_t& obj)
461 {
462   ObjectStore::Transaction t;
463   note_txn(&t);
464   t.touch(coll, ghobject_t(obj));
465   m_store->apply_transaction(&m_osr, std::move(t));
466 }
467
468 void DeterministicOpSequence::_do_remove(coll_t coll, hobject_t& obj)
469 {
470   ObjectStore::Transaction t;
471   note_txn(&t);
472   t.remove(coll, ghobject_t(obj));
473   m_store->apply_transaction(&m_osr, std::move(t));
474 }
475
476 void DeterministicOpSequence::_do_set_attrs(coll_t coll,
477                                             hobject_t &obj,
478                                             const map<string, bufferlist> &attrs)
479 {
480   ObjectStore::Transaction t;
481   note_txn(&t);
482   t.omap_setkeys(coll, ghobject_t(obj), attrs);
483   m_store->apply_transaction(&m_osr, std::move(t));
484 }
485
486 void DeterministicOpSequence::_do_write(coll_t coll, hobject_t& obj,
487                                         uint64_t off, uint64_t len, const bufferlist& data)
488 {
489   ObjectStore::Transaction t;
490   note_txn(&t);
491   t.write(coll, ghobject_t(obj), off, len, data);
492   m_store->apply_transaction(&m_osr, std::move(t));
493 }
494
495 void DeterministicOpSequence::_do_clone(coll_t coll, hobject_t& orig_obj,
496                                         hobject_t& new_obj)
497 {
498   ObjectStore::Transaction t;
499   note_txn(&t);
500   t.clone(coll, ghobject_t(orig_obj), ghobject_t(new_obj));
501   m_store->apply_transaction(&m_osr, std::move(t));
502 }
503
504 void DeterministicOpSequence::_do_clone_range(coll_t coll,
505                                               hobject_t& orig_obj, hobject_t& new_obj, uint64_t srcoff,
506                                               uint64_t srclen, uint64_t dstoff)
507 {
508   ObjectStore::Transaction t;
509   note_txn(&t);
510   t.clone_range(coll, ghobject_t(orig_obj), ghobject_t(new_obj),
511                 srcoff, srclen, dstoff);
512   m_store->apply_transaction(&m_osr, std::move(t));
513 }
514
515 void DeterministicOpSequence::_do_write_and_clone_range(coll_t coll,
516                                                         hobject_t& orig_obj,
517                                                         hobject_t& new_obj,
518                                                         uint64_t srcoff,
519                                                         uint64_t srclen,
520                                                         uint64_t dstoff,
521                                                         bufferlist& bl)
522 {
523   ObjectStore::Transaction t;
524   note_txn(&t);
525   t.write(coll, ghobject_t(orig_obj), srcoff, bl.length(), bl);
526   t.clone_range(coll, ghobject_t(orig_obj), ghobject_t(new_obj),
527                 srcoff, srclen, dstoff);
528   m_store->apply_transaction(&m_osr, std::move(t));
529 }
530
531 void DeterministicOpSequence::_do_coll_move(coll_t orig_coll, coll_t new_coll,
532                                             hobject_t& obj)
533 {
534   ObjectStore::Transaction t;
535   note_txn(&t);
536   t.remove(new_coll, ghobject_t(obj));
537   t.collection_move_rename(orig_coll, ghobject_t(obj), new_coll, ghobject_t(obj));
538   m_store->apply_transaction(&m_osr, std::move(t));
539 }
540