Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / erasure-code / ceph_erasure_code_non_regression.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 distributed storage system
5  *
6  * Red Hat (C) 2014, 2015 Red Hat <contact@redhat.com>
7  *
8  * Author: Loic Dachary <loic@dachary.org>
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  */
16
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <boost/scoped_ptr.hpp>
20 #include <boost/lexical_cast.hpp>
21 #include <boost/program_options/option.hpp>
22 #include <boost/program_options/options_description.hpp>
23 #include <boost/program_options/variables_map.hpp>
24 #include <boost/program_options/cmdline.hpp>
25 #include <boost/program_options/parsers.hpp>
26 #include <boost/algorithm/string.hpp>
27
28 #include "global/global_context.h"
29 #include "global/global_init.h"
30 #include "common/errno.h"
31 #include "common/ceph_argparse.h"
32 #include "common/config.h"
33 #include "erasure-code/ErasureCodePlugin.h"
34
35 namespace po = boost::program_options;
36 using namespace std;
37
38 class ErasureCodeNonRegression {
39   unsigned stripe_width;
40   string plugin;
41   bool create;
42   bool check;
43   string base;
44   string directory;
45   ErasureCodeProfile profile;
46   boost::intrusive_ptr<CephContext> cct;
47 public:
48   int setup(int argc, char** argv);
49   int run();
50   int run_create();
51   int run_check();
52   int decode_erasures(ErasureCodeInterfaceRef erasure_code,
53                       set<int> erasures,
54                       map<int,bufferlist> chunks);
55   string content_path();
56   string chunk_path(unsigned int chunk);
57 };
58
59 int ErasureCodeNonRegression::setup(int argc, char** argv) {
60
61   po::options_description desc("Allowed options");
62   desc.add_options()
63     ("help,h", "produce help message")
64     ("stripe-width,s", po::value<int>()->default_value(4 * 1024),
65      "stripe_width, i.e. the size of the buffer to be encoded")
66     ("plugin,p", po::value<string>()->default_value("jerasure"),
67      "erasure code plugin name")
68     ("base", po::value<string>()->default_value("."),
69      "prefix all paths with base")
70     ("parameter,P", po::value<vector<string> >(),
71      "add a parameter to the erasure code profile")
72     ("create", "create the erasure coded content in the directory")
73     ("check", "check the content in the directory matches the chunks and vice versa")
74     ;
75
76   po::variables_map vm;
77   po::parsed_options parsed =
78     po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
79   po::store(
80     parsed,
81     vm);
82   po::notify(vm);
83
84   vector<const char *> ceph_options, def_args;
85   vector<string> ceph_option_strings = po::collect_unrecognized(
86     parsed.options, po::include_positional);
87   ceph_options.reserve(ceph_option_strings.size());
88   for (vector<string>::iterator i = ceph_option_strings.begin();
89        i != ceph_option_strings.end();
90        ++i) {
91     ceph_options.push_back(i->c_str());
92   }
93
94   cct = global_init(&def_args, ceph_options, CEPH_ENTITY_TYPE_CLIENT,
95                     CODE_ENVIRONMENT_UTILITY,
96                     CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
97   common_init_finish(g_ceph_context);
98   g_ceph_context->_conf->apply_changes(NULL);
99   const char* env = getenv("CEPH_LIB");
100   std::string libs_dir(env ? env : ".libs");
101   g_conf->set_val_or_die("erasure_code_dir", libs_dir, false);
102
103   if (vm.count("help")) {
104     cout << desc << std::endl;
105     return 1;
106   }
107
108   stripe_width = vm["stripe-width"].as<int>();
109   plugin = vm["plugin"].as<string>();
110   base = vm["base"].as<string>();
111   check = vm.count("check") > 0;
112   create = vm.count("create") > 0;
113
114   if (!check && !create) {
115     cerr << "must specifify either --check, or --create" << endl;
116     return 1;
117   }
118
119   {
120     stringstream path;
121     path << base << "/" << "plugin=" << plugin << " stripe-width=" << stripe_width;
122     directory = path.str();
123   }
124
125   if (vm.count("parameter")) {
126     const vector<string> &p = vm["parameter"].as< vector<string> >();
127     for (vector<string>::const_iterator i = p.begin();
128          i != p.end();
129          ++i) {
130       std::vector<std::string> strs;
131       boost::split(strs, *i, boost::is_any_of("="));
132       if (strs.size() != 2) {
133         cerr << "--parameter " << *i << " ignored because it does not contain exactly one =" << endl;
134       } else {
135         profile[strs[0]] = strs[1];
136       }
137       directory += " " + *i;
138     }
139   }
140
141   return 0;
142 }
143
144 int ErasureCodeNonRegression::run()
145   {
146   int ret = 0;
147   if(create && (ret = run_create()))
148     return ret;
149   if(check && (ret = run_check()))
150     return ret;
151   return ret;
152 }
153
154 int ErasureCodeNonRegression::run_create()
155 {
156   ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
157   ErasureCodeInterfaceRef erasure_code;
158   stringstream messages;
159   int code = instance.factory(plugin,
160                               g_conf->get_val<std::string>("erasure_code_dir"),
161                               profile, &erasure_code, &messages);
162   if (code) {
163     cerr << messages.str() << endl;
164     return code;
165   }
166
167   if (::mkdir(directory.c_str(), 0755)) {
168     cerr << "mkdir(" << directory << "): " << cpp_strerror(errno) << endl;
169     return 1;
170   }
171   unsigned payload_chunk_size = 37;
172   string payload;
173   for (unsigned j = 0; j < payload_chunk_size; ++j)
174     payload.push_back('a' + (rand() % 26));
175   bufferlist in;
176   for (unsigned j = 0; j < stripe_width; j += payload_chunk_size)
177     in.append(payload);
178   if (stripe_width < in.length())
179     in.splice(stripe_width, in.length() - stripe_width);
180   if (in.write_file(content_path().c_str()))
181     return 1;
182   set<int> want_to_encode;
183   for (unsigned int i = 0; i < erasure_code->get_chunk_count(); i++) {
184     want_to_encode.insert(i);
185   }
186   map<int,bufferlist> encoded;
187   code = erasure_code->encode(want_to_encode, in, &encoded);
188   if (code)
189     return code;
190   for (map<int,bufferlist>::iterator chunk = encoded.begin();
191        chunk != encoded.end();
192        ++chunk) {
193     if (chunk->second.write_file(chunk_path(chunk->first).c_str()))
194       return 1;
195   }
196   return 0;
197 }
198
199 int ErasureCodeNonRegression::decode_erasures(ErasureCodeInterfaceRef erasure_code,
200                                               set<int> erasures,
201                                               map<int,bufferlist> chunks)
202 {
203   map<int,bufferlist> available;
204   for (map<int,bufferlist>::iterator chunk = chunks.begin();
205        chunk != chunks.end();
206        ++chunk) {
207     if (erasures.count(chunk->first) == 0)
208       available[chunk->first] = chunk->second;
209       
210   }
211   map<int,bufferlist> decoded;
212   int code = erasure_code->decode(erasures, available, &decoded);
213   if (code)
214     return code;
215   for (set<int>::iterator erasure = erasures.begin();
216        erasure != erasures.end();
217        ++erasure) {
218     if (!chunks[*erasure].contents_equal(decoded[*erasure])) {
219       cerr << "chunk " << *erasure << " incorrectly recovered" << endl;
220       return 1;
221     }
222   }
223   return 0;
224 }
225
226 int ErasureCodeNonRegression::run_check()
227 {
228   ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
229   ErasureCodeInterfaceRef erasure_code;
230   stringstream messages;
231   int code = instance.factory(plugin,
232                               g_conf->get_val<std::string>("erasure_code_dir"),
233                               profile, &erasure_code, &messages);
234   if (code) {
235     cerr << messages.str() << endl;
236     return code;
237   }
238   string errors;
239   bufferlist in;
240   if (in.read_file(content_path().c_str(), &errors)) {
241     cerr << errors << endl;
242     return 1;
243   }
244   set<int> want_to_encode;
245   for (unsigned int i = 0; i < erasure_code->get_chunk_count(); i++) {
246     want_to_encode.insert(i);
247   }
248
249   map<int,bufferlist> encoded;
250   code = erasure_code->encode(want_to_encode, in, &encoded);
251   if (code)
252     return code;
253
254   for (map<int,bufferlist>::iterator chunk = encoded.begin();
255        chunk != encoded.end();
256        ++chunk) {
257     bufferlist existing;
258     if (existing.read_file(chunk_path(chunk->first).c_str(), &errors)) {
259       cerr << errors << endl;
260       return 1;
261     }
262     bufferlist &old = chunk->second;
263     if (existing.length() != old.length() ||
264         memcmp(existing.c_str(), old.c_str(), old.length())) {
265       cerr << "chunk " << chunk->first << " encodes differently" << endl;
266       return 1;
267     }
268   }
269
270   // erasing a single chunk is likely to use a specific code path in every plugin
271   set<int> erasures;
272   erasures.clear();
273   erasures.insert(0);
274   code = decode_erasures(erasure_code, erasures, encoded);
275   if (code)
276     return code;
277
278   if (erasure_code->get_chunk_count() - erasure_code->get_data_chunk_count() > 1) {
279     // erasing two chunks is likely to be the general case
280     erasures.clear();
281     erasures.insert(0);
282     erasures.insert(erasure_code->get_chunk_count() - 1);
283     code = decode_erasures(erasure_code, erasures, encoded);
284     if (code)
285       return code;
286   }
287   
288   return 0;
289 }
290
291 string ErasureCodeNonRegression::content_path()
292 {
293   stringstream path;
294   path << directory << "/content";
295   return path.str();
296 }
297
298 string ErasureCodeNonRegression::chunk_path(unsigned int chunk)
299 {
300   stringstream path;
301   path << directory << "/" << chunk;
302   return path.str();
303 }
304
305 int main(int argc, char** argv) {
306   ErasureCodeNonRegression non_regression;
307   int err = non_regression.setup(argc, argv);
308   if (err)
309     return err;
310   return non_regression.run();
311 }
312
313 /*
314  * Local Variables:
315  * compile-command: "cd ../.. ; make -j4 &&
316  *   make ceph_erasure_code_non_regression &&
317  *   libtool --mode=execute valgrind --tool=memcheck --leak-check=full \
318  *      ./ceph_erasure_code_non_regression \
319  *      --plugin jerasure \
320  *      --parameter technique=reed_sol_van \
321  *      --parameter k=2 \
322  *      --parameter m=2 \
323  *      --directory /tmp/ceph_erasure_code_non_regression \
324  *      --stripe-width 3181 \
325  *      --create \
326  *      --check
327  * "
328  * End:
329  */