1+ #include " ConsistencyChecker.h"
2+
3+ #include " RadosCommands.h"
4+ #include " Pool.h"
5+ #include " ECReader.h"
6+ #include " ECEncoder.h"
7+ #include " ECEncoderSwitch.h"
8+
9+ using ConsistencyChecker = ceph::consistency::ConsistencyChecker;
10+
11+ using Read = ceph::consistency::Read;
12+ using ReadResult = ceph::consistency::ReadResult;
13+ using bufferlist = ceph::bufferlist;
14+
15+ ConsistencyChecker::ConsistencyChecker (librados::Rados &rados,
16+ boost::asio::io_context& asio,
17+ const std::string& pool_name,
18+ int stripe_unit) :
19+ rados(rados),
20+ asio(asio),
21+ reader(ceph::consistency::ECReader(rados, asio, pool_name)),
22+ commands(ceph::consistency::RadosCommands(rados)),
23+ pool(pool_name,
24+ commands.get_ec_profile_for_pool(pool_name),
25+ commands.get_pool_allow_ec_optimizations(pool_name)),
26+ encoder(ceph::consistency::ECEncoderSwitch(pool.get_ec_profile(),
27+ stripe_unit,
28+ commands.get_pool_allow_ec_optimizations(pool_name)
29+ )) {}
30+
31+ /* *
32+ * Perform an end-to-end read and consistency check on a single object.
33+ * Current implementation only supports reading the entire object, so length and
34+ * offset should normally be 0.
35+ *
36+ * @param oid string Name of the pool to perform inject on
37+ * @param block_size int Block size for the data being read
38+ * @param offset int Which offset to read from
39+ * @param length int How much data of each shard to read
40+ * @return bool true if consistent, otherwise false
41+ */
42+ bool ConsistencyChecker::single_read_and_check_consistency (const std::string& oid,
43+ int block_size,
44+ int offset,
45+ int length)
46+ {
47+ clear_results ();
48+ std::string error_message = " " ;
49+ bool success = true ;
50+
51+ auto read = Read (oid, block_size, offset, length);
52+ queue_ec_read (read);
53+
54+ auto read_results = reader.get_results ();
55+ int result_count = read_results->size ();
56+ if (result_count != 1 ) {
57+ error_message = " Incorrect number of RADOS read results returned, count: "
58+ + std::to_string (result_count);
59+ success = false ;
60+ }
61+
62+ ReadResult read_result = (*read_results)[0 ];
63+ boost::system::error_code ec = read_result.get_ec ();
64+ if (success && ec != boost::system::errc::success) {
65+ error_message = " RADOS Read failed, error message: " + ec.message ();
66+ success = false ;
67+ }
68+
69+ if (success && read_result.get_data ().length () == 0 ) {
70+ error_message = " Empty object returned from RADOS read." ;
71+ success = false ;
72+ }
73+
74+ if (success && !check_object_consistency (oid, read_result.get_data ())) {
75+ error_message = " Generated parity did not match read in parity shards." ;
76+ success = false ;
77+ }
78+
79+ results.push_back ({oid, error_message, success});
80+ commands.inject_clear_parity_read_on_primary_osd (pool.get_pool_name (),
81+ oid);
82+ return success;
83+ }
84+
85+ /* *
86+ * Queue up an EC read with the parity read inject set
87+ *
88+ * @param read Object containing information about the read
89+ */
90+ void ConsistencyChecker::queue_ec_read (Read read)
91+ {
92+ commands.inject_parity_read_on_primary_osd (pool.get_pool_name (),
93+ read.get_oid ());
94+ reader.do_read (read);
95+ }
96+
97+ /* *
98+ * Generate parities from the data and compare to the parity shards
99+ *
100+ * @param oid string The object ID of the object being checked
101+ * @param inbl bufferlist The entire contents of the object, including parities
102+ * @param stripe_unit int The chunk size for the object
103+ */
104+ bool ConsistencyChecker::check_object_consistency (const std::string& oid,
105+ const bufferlist& inbl)
106+ {
107+ bool is_optimized = pool.has_optimizations_enabled ();
108+ std::pair<bufferlist, bufferlist> data_and_parity;
109+ data_and_parity = split_data_and_parity (oid, inbl, encoder.get_k (),
110+ encoder.get_m (), is_optimized);
111+
112+ std::optional<bufferlist> outbl;
113+ outbl = encoder.do_encode (data_and_parity.first );
114+
115+ if (!outbl.has_value ()) {
116+ return false ;
117+ }
118+
119+ return buffers_match (outbl.value (), data_and_parity.second );
120+ }
121+
122+ void ConsistencyChecker::print_results (std::ostream& out)
123+ {
124+ out << " Results:" << std::endl;
125+ for (const auto &r : results) {
126+ std::string result_str = (r.get_result ()) ? " Passed" : " Failed" ;
127+ std::string error_str = r.get_error_message ();
128+ out << " Object ID " << r.get_oid () << " : " << result_str << std::endl;
129+ if (!error_str.empty ()) {
130+ out << " Error Message: " << error_str << std::endl;
131+ }
132+ }
133+
134+ int count = results.size ();
135+ std::string obj_str = (count == 1 ) ? " object checked." : " objects checked." ;
136+ out << " Total: " << count << " " << obj_str << std::endl;
137+ }
138+
139+ std::pair<bufferlist, bufferlist>
140+ ConsistencyChecker::split_data_and_parity (const std::string& oid,
141+ const bufferlist& read,
142+ int k, int m,
143+ bool is_optimized)
144+ {
145+ uint64_t data_size, parity_size;
146+
147+ // Optimized EC parity read should return the exact object size + parity shards
148+ // Legacy EC parity read will return the entire padded data shards + parity shards
149+ data_size = is_optimized ? reader.get_object_size (oid) : (read.length () / (k + m)) * k;
150+ parity_size = read.length () - data_size;
151+
152+ bufferlist data, parity;
153+ auto it = read.begin ();
154+ it.copy (data_size, data);
155+ it.copy (parity_size, parity);
156+ return std::pair<bufferlist, bufferlist>(data, parity);
157+ }
158+
159+ bool ConsistencyChecker::buffers_match (const bufferlist& b1,
160+ const bufferlist& b2)
161+ {
162+ return (b1.contents_equal (b2));
163+ }
164+
165+ void ConsistencyChecker::clear_results ()
166+ {
167+ reader.clear_results ();
168+ results.clear ();
169+ }
0 commit comments