]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/erasure-code/ceph_erasure_code_non_regression.cc
add subtree-ish sources for 12.0.3
[ceph.git] / 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("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 */