1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph distributed storage system
6 * Red Hat (C) 2014, 2015 Red Hat <contact@redhat.com>
8 * Author: Loic Dachary <loic@dachary.org>
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.
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>
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"
35 namespace po = boost::program_options;
38 class ErasureCodeNonRegression {
39 unsigned stripe_width;
45 ErasureCodeProfile profile;
46 boost::intrusive_ptr<CephContext> cct;
48 int setup(int argc, char** argv);
52 int decode_erasures(ErasureCodeInterfaceRef erasure_code,
54 map<int,bufferlist> chunks);
55 string content_path();
56 string chunk_path(unsigned int chunk);
59 int ErasureCodeNonRegression::setup(int argc, char** argv) {
61 po::options_description desc("Allowed 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")
77 po::parsed_options parsed =
78 po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
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();
91 ceph_options.push_back(i->c_str());
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);
103 if (vm.count("help")) {
104 cout << desc << std::endl;
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;
114 if (!check && !create) {
115 cerr << "must specifify either --check, or --create" << endl;
121 path << base << "/" << "plugin=" << plugin << " stripe-width=" << stripe_width;
122 directory = path.str();
125 if (vm.count("parameter")) {
126 const vector<string> &p = vm["parameter"].as< vector<string> >();
127 for (vector<string>::const_iterator i = p.begin();
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;
135 profile[strs[0]] = strs[1];
137 directory += " " + *i;
144 int ErasureCodeNonRegression::run()
147 if(create && (ret = run_create()))
149 if(check && (ret = run_check()))
154 int ErasureCodeNonRegression::run_create()
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);
163 cerr << messages.str() << endl;
167 if (::mkdir(directory.c_str(), 0755)) {
168 cerr << "mkdir(" << directory << "): " << cpp_strerror(errno) << endl;
171 unsigned payload_chunk_size = 37;
173 for (unsigned j = 0; j < payload_chunk_size; ++j)
174 payload.push_back('a' + (rand() % 26));
176 for (unsigned j = 0; j < stripe_width; j += payload_chunk_size)
178 if (stripe_width < in.length())
179 in.splice(stripe_width, in.length() - stripe_width);
180 if (in.write_file(content_path().c_str()))
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);
186 map<int,bufferlist> encoded;
187 code = erasure_code->encode(want_to_encode, in, &encoded);
190 for (map<int,bufferlist>::iterator chunk = encoded.begin();
191 chunk != encoded.end();
193 if (chunk->second.write_file(chunk_path(chunk->first).c_str()))
199 int ErasureCodeNonRegression::decode_erasures(ErasureCodeInterfaceRef erasure_code,
201 map<int,bufferlist> chunks)
203 map<int,bufferlist> available;
204 for (map<int,bufferlist>::iterator chunk = chunks.begin();
205 chunk != chunks.end();
207 if (erasures.count(chunk->first) == 0)
208 available[chunk->first] = chunk->second;
211 map<int,bufferlist> decoded;
212 int code = erasure_code->decode(erasures, available, &decoded);
215 for (set<int>::iterator erasure = erasures.begin();
216 erasure != erasures.end();
218 if (!chunks[*erasure].contents_equal(decoded[*erasure])) {
219 cerr << "chunk " << *erasure << " incorrectly recovered" << endl;
226 int ErasureCodeNonRegression::run_check()
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);
235 cerr << messages.str() << endl;
240 if (in.read_file(content_path().c_str(), &errors)) {
241 cerr << errors << endl;
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);
249 map<int,bufferlist> encoded;
250 code = erasure_code->encode(want_to_encode, in, &encoded);
254 for (map<int,bufferlist>::iterator chunk = encoded.begin();
255 chunk != encoded.end();
258 if (existing.read_file(chunk_path(chunk->first).c_str(), &errors)) {
259 cerr << errors << endl;
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;
270 // erasing a single chunk is likely to use a specific code path in every plugin
274 code = decode_erasures(erasure_code, erasures, encoded);
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
282 erasures.insert(erasure_code->get_chunk_count() - 1);
283 code = decode_erasures(erasure_code, erasures, encoded);
291 string ErasureCodeNonRegression::content_path()
294 path << directory << "/content";
298 string ErasureCodeNonRegression::chunk_path(unsigned int chunk)
301 path << directory << "/" << chunk;
305 int main(int argc, char** argv) {
306 ErasureCodeNonRegression non_regression;
307 int err = non_regression.setup(argc, argv);
310 return non_regression.run();
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 \
323 * --directory /tmp/ceph_erasure_code_non_regression \
324 * --stripe-width 3181 \