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_context.h"
32 #include "common/ceph_argparse.h"
33 #include "common/config.h"
34 #include "erasure-code/ErasureCodePlugin.h"
36 namespace po
= boost::program_options
;
39 class ErasureCodeNonRegression
{
40 unsigned stripe_width
;
46 ErasureCodeProfile profile
;
47 boost::intrusive_ptr
<CephContext
> cct
;
49 int setup(int argc
, char** argv
);
53 int decode_erasures(ErasureCodeInterfaceRef erasure_code
,
55 map
<int,bufferlist
> chunks
);
56 string
content_path();
57 string
chunk_path(unsigned int chunk
);
60 int ErasureCodeNonRegression::setup(int argc
, char** argv
) {
62 po::options_description
desc("Allowed 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")
78 po::parsed_options parsed
=
79 po::command_line_parser(argc
, argv
).options(desc
).allow_unregistered().run();
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();
92 ceph_options
.push_back(i
->c_str());
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);
101 if (vm
.count("help")) {
102 cout
<< desc
<< std::endl
;
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;
112 if (!check
&& !create
) {
113 cerr
<< "must specifify either --check, or --create" << endl
;
119 path
<< base
<< "/" << "plugin=" << plugin
<< " stripe-width=" << stripe_width
;
120 directory
= path
.str();
123 if (vm
.count("parameter")) {
124 const vector
<string
> &p
= vm
["parameter"].as
< vector
<string
> >();
125 for (vector
<string
>::const_iterator i
= p
.begin();
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
;
133 profile
[strs
[0]] = strs
[1];
135 directory
+= " " + *i
;
142 int ErasureCodeNonRegression::run()
145 if(create
&& (ret
= run_create()))
147 if(check
&& (ret
= run_check()))
152 int ErasureCodeNonRegression::run_create()
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
);
161 cerr
<< messages
.str() << endl
;
165 if (::mkdir(directory
.c_str(), 0755)) {
166 cerr
<< "mkdir(" << directory
<< "): " << cpp_strerror(errno
) << endl
;
169 unsigned payload_chunk_size
= 37;
171 for (unsigned j
= 0; j
< payload_chunk_size
; ++j
)
172 payload
.push_back('a' + (rand() % 26));
174 for (unsigned j
= 0; j
< stripe_width
; j
+= payload_chunk_size
)
176 if (stripe_width
< in
.length())
177 in
.splice(stripe_width
, in
.length() - stripe_width
);
178 if (in
.write_file(content_path().c_str()))
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
);
184 map
<int,bufferlist
> encoded
;
185 code
= erasure_code
->encode(want_to_encode
, in
, &encoded
);
188 for (map
<int,bufferlist
>::iterator chunk
= encoded
.begin();
189 chunk
!= encoded
.end();
191 if (chunk
->second
.write_file(chunk_path(chunk
->first
).c_str()))
197 int ErasureCodeNonRegression::decode_erasures(ErasureCodeInterfaceRef erasure_code
,
199 map
<int,bufferlist
> chunks
)
201 map
<int,bufferlist
> available
;
202 for (map
<int,bufferlist
>::iterator chunk
= chunks
.begin();
203 chunk
!= chunks
.end();
205 if (erasures
.count(chunk
->first
) == 0)
206 available
[chunk
->first
] = chunk
->second
;
209 map
<int,bufferlist
> decoded
;
210 int code
= erasure_code
->decode(erasures
, available
, &decoded
, available
.begin()->second
.length());
213 for (set
<int>::iterator erasure
= erasures
.begin();
214 erasure
!= erasures
.end();
216 if (!chunks
[*erasure
].contents_equal(decoded
[*erasure
])) {
217 cerr
<< "chunk " << *erasure
<< " incorrectly recovered" << endl
;
224 int ErasureCodeNonRegression::run_check()
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
);
233 cerr
<< messages
.str() << endl
;
238 if (in
.read_file(content_path().c_str(), &errors
)) {
239 cerr
<< errors
<< endl
;
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
);
247 map
<int,bufferlist
> encoded
;
248 code
= erasure_code
->encode(want_to_encode
, in
, &encoded
);
252 for (map
<int,bufferlist
>::iterator chunk
= encoded
.begin();
253 chunk
!= encoded
.end();
256 if (existing
.read_file(chunk_path(chunk
->first
).c_str(), &errors
)) {
257 cerr
<< errors
<< endl
;
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
;
268 // erasing a single chunk is likely to use a specific code path in every plugin
272 code
= decode_erasures(erasure_code
, erasures
, encoded
);
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
280 erasures
.insert(erasure_code
->get_chunk_count() - 1);
281 code
= decode_erasures(erasure_code
, erasures
, encoded
);
289 string
ErasureCodeNonRegression::content_path()
292 path
<< directory
<< "/content";
296 string
ErasureCodeNonRegression::chunk_path(unsigned int chunk
)
299 path
<< directory
<< "/" << chunk
;
303 int main(int argc
, char** argv
) {
304 ErasureCodeNonRegression non_regression
;
305 int err
= non_regression
.setup(argc
, argv
);
308 return non_regression
.run();
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 \
321 * --directory /tmp/ceph_erasure_code_non_regression \
322 * --stripe-width 3181 \