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