]> git.proxmox.com Git - ceph.git/blob - ceph/src/crush/CrushTester.h
aa3d904b6222fbc3735bd861840a5247a1052904
[ceph.git] / ceph / src / crush / CrushTester.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #ifndef CEPH_CRUSH_TESTER_H
5 #define CEPH_CRUSH_TESTER_H
6
7 #include "crush/CrushWrapper.h"
8
9 #include <fstream>
10
11 class CrushTester {
12 CrushWrapper& crush;
13 std::ostream& err;
14
15 std::map<int, int> device_weight;
16 int min_rule, max_rule;
17 int min_x, max_x;
18 int min_rep, max_rep;
19 int64_t pool_id;
20
21 int num_batches;
22 bool use_crush;
23
24 float mark_down_device_ratio;
25 float mark_down_bucket_ratio;
26
27 bool output_utilization;
28 bool output_utilization_all;
29 bool output_statistics;
30 bool output_mappings;
31 bool output_bad_mappings;
32 bool output_choose_tries;
33
34 bool output_data_file;
35 bool output_csv;
36
37 std::string output_data_file_name;
38
39 /*
40 * mark a ratio of devices down, can be used to simulate placement distributions
41 * under degrated cluster conditions
42 */
43 void adjust_weights(std::vector<__u32>& weight);
44
45 /*
46 * Get the maximum number of devices that could be selected to satisfy ruleno.
47 */
48 int get_maximum_affected_by_rule(int ruleno);
49
50 /*
51 * for maps where in devices have non-sequential id numbers, return a mapping of device id
52 * to a sequential id number. For example, if we have devices with id's 0 1 4 5 6 return a map
53 * where:
54 * 0 = 0
55 * 1 = 1
56 * 4 = 2
57 * 5 = 3
58 * 6 = 4
59 *
60 * which can help make post-processing easier
61 */
62 std::map<int,int> get_collapsed_mapping();
63
64 /*
65 * Essentially a re-implementation of CRUSH. Given a vector of devices
66 * check that the vector represents a valid placement for a given ruleno.
67 */
68 bool check_valid_placement(int ruleno, std::vector<int> in, const std::vector<__u32>& weight);
69
70 /*
71 * Generate a random selection of devices which satisfies ruleno. Essentially a
72 * monte-carlo simulator for CRUSH placements which can be used to compare the
73 * statistical distribution of the CRUSH algorithm to a random number generator
74 */
75 int random_placement(int ruleno, std::vector<int>& out, int maxout, std::vector<__u32>& weight);
76
77 // scaffolding to store data for off-line processing
78 struct tester_data_set {
79 std::vector<std::string> device_utilization;
80 std::vector<std::string> device_utilization_all;
81 std::vector<std::string> placement_information;
82 std::vector<std::string> batch_device_utilization_all;
83 std::vector<std::string> batch_device_expected_utilization_all;
84 std::map<int, float> proportional_weights;
85 std::map<int, float> proportional_weights_all;
86 std::map<int, float> absolute_weights;
87 } ;
88
89 void write_to_csv(std::ofstream& csv_file, std::vector<std::string>& payload)
90 {
91 if (csv_file.good())
92 for (std::vector<std::string>::iterator it = payload.begin(); it != payload.end(); ++it)
93 csv_file << (*it);
94 }
95
96 void write_to_csv(std::ofstream& csv_file, std::map<int, float>& payload)
97 {
98 if (csv_file.good())
99 for (std::map<int, float>::iterator it = payload.begin(); it != payload.end(); ++it)
100 csv_file << (*it).first << ',' << (*it).second << std::endl;
101 }
102
103 void write_data_set_to_csv(std::string user_tag, tester_data_set& tester_data)
104 {
105
106 std::ofstream device_utilization_file((user_tag + (std::string)"-device_utilization.csv").c_str());
107 std::ofstream device_utilization_all_file((user_tag + (std::string)"-device_utilization_all.csv").c_str());
108 std::ofstream placement_information_file((user_tag + (std::string)"-placement_information.csv").c_str());
109 std::ofstream proportional_weights_file((user_tag + (std::string)"-proportional_weights.csv").c_str());
110 std::ofstream proportional_weights_all_file((user_tag + (std::string)"-proportional_weights_all.csv").c_str());
111 std::ofstream absolute_weights_file((user_tag + (std::string)"-absolute_weights.csv").c_str());
112
113 // write the headers
114 device_utilization_file << "Device ID, Number of Objects Stored, Number of Objects Expected" << std::endl;
115 device_utilization_all_file << "Device ID, Number of Objects Stored, Number of Objects Expected" << std::endl;
116 proportional_weights_file << "Device ID, Proportional Weight" << std::endl;
117 proportional_weights_all_file << "Device ID, Proportional Weight" << std::endl;
118 absolute_weights_file << "Device ID, Absolute Weight" << std::endl;
119
120 placement_information_file << "Input";
121 for (int i = 0; i < max_rep; i++) {
122 placement_information_file << ", OSD" << i;
123 }
124 placement_information_file << std::endl;
125
126 write_to_csv(device_utilization_file, tester_data.device_utilization);
127 write_to_csv(device_utilization_all_file, tester_data.device_utilization_all);
128 write_to_csv(placement_information_file, tester_data.placement_information);
129 write_to_csv(proportional_weights_file, tester_data.proportional_weights);
130 write_to_csv(proportional_weights_all_file, tester_data.proportional_weights_all);
131 write_to_csv(absolute_weights_file, tester_data.absolute_weights);
132
133 device_utilization_file.close();
134 device_utilization_all_file.close();
135 placement_information_file.close();
136 proportional_weights_file.close();
137 absolute_weights_file.close();
138
139 if (num_batches > 1) {
140 std::ofstream batch_device_utilization_all_file ((user_tag + (std::string)"-batch_device_utilization_all.csv").c_str());
141 std::ofstream batch_device_expected_utilization_all_file ((user_tag + (std::string)"-batch_device_expected_utilization_all.csv").c_str());
142
143 batch_device_utilization_all_file << "Batch Round";
144 for (unsigned i = 0; i < tester_data.device_utilization.size(); i++) {
145 batch_device_utilization_all_file << ", Objects Stored on OSD" << i;
146 }
147 batch_device_utilization_all_file << std::endl;
148
149 batch_device_expected_utilization_all_file << "Batch Round";
150 for (unsigned i = 0; i < tester_data.device_utilization.size(); i++) {
151 batch_device_expected_utilization_all_file << ", Objects Expected on OSD" << i;
152 }
153 batch_device_expected_utilization_all_file << std::endl;
154
155 write_to_csv(batch_device_utilization_all_file, tester_data.batch_device_utilization_all);
156 write_to_csv(batch_device_expected_utilization_all_file, tester_data.batch_device_expected_utilization_all);
157 batch_device_expected_utilization_all_file.close();
158 batch_device_utilization_all_file.close();
159 }
160 }
161
162 void write_integer_indexed_vector_data_string(std::vector<std::string> &dst, int index, std::vector<int> vector_data);
163 void write_integer_indexed_vector_data_string(std::vector<std::string> &dst, int index, std::vector<float> vector_data);
164 void write_integer_indexed_scalar_data_string(std::vector<std::string> &dst, int index, int scalar_data);
165 void write_integer_indexed_scalar_data_string(std::vector<std::string> &dst, int index, float scalar_data);
166
167 public:
168 CrushTester(CrushWrapper& c, std::ostream& eo)
169 : crush(c), err(eo),
170 min_rule(-1), max_rule(-1),
171 min_x(-1), max_x(-1),
172 min_rep(-1), max_rep(-1),
173 pool_id(-1),
174 num_batches(1),
175 use_crush(true),
176 mark_down_device_ratio(0.0),
177 mark_down_bucket_ratio(1.0),
178 output_utilization(false),
179 output_utilization_all(false),
180 output_statistics(false),
181 output_mappings(false),
182 output_bad_mappings(false),
183 output_choose_tries(false),
184 output_data_file(false),
185 output_csv(false),
186 output_data_file_name("")
187
188 { }
189
190 void set_output_data_file_name(std::string name) {
191 output_data_file_name = name;
192 }
193 std::string get_output_data_file_name() const {
194 return output_data_file_name;
195 }
196
197 void set_output_data_file(bool b) {
198 output_data_file = b;
199 }
200 bool get_output_data_file() const {
201 return output_data_file;
202 }
203
204 void set_output_csv(bool b) {
205 output_csv = b;
206 }
207 bool get_output_csv() const {
208 return output_csv;
209 }
210
211 void set_output_utilization(bool b) {
212 output_utilization = b;
213 }
214 bool get_output_utilization() const {
215 return output_utilization;
216 }
217
218 void set_output_utilization_all(bool b) {
219 output_utilization_all = b;
220 }
221 bool get_output_utilization_all() const {
222 return output_utilization_all;
223 }
224
225 void set_output_statistics(bool b) {
226 output_statistics = b;
227 }
228 bool get_output_statistics() const {
229 return output_statistics;
230 }
231
232 void set_output_mappings(bool b) {
233 output_mappings = b;
234 }
235 bool get_output_mappings() const {
236 return output_mappings;
237 }
238
239 void set_output_bad_mappings(bool b) {
240 output_bad_mappings = b;
241 }
242 bool get_output_bad_mappings() const {
243 return output_bad_mappings;
244 }
245
246 void set_output_choose_tries(bool b) {
247 output_choose_tries = b;
248 }
249 bool get_output_choose_tries() const {
250 return output_choose_tries;
251 }
252
253 void set_batches(int b) {
254 num_batches = b;
255 }
256 int get_batches() const {
257 return num_batches;
258 }
259
260 void set_random_placement() {
261 use_crush = false;
262 }
263 bool get_random_placement() const {
264 return use_crush == false;
265 }
266
267 void set_bucket_down_ratio(float bucket_ratio) {
268 mark_down_bucket_ratio = bucket_ratio;
269 }
270 float get_bucket_down_ratio() const {
271 return mark_down_bucket_ratio;
272 }
273
274 void set_device_down_ratio(float device_ratio) {
275 mark_down_device_ratio = device_ratio;
276 }
277 float set_device_down_ratio() const {
278 return mark_down_device_ratio;
279 }
280
281 void set_device_weight(int dev, float f);
282
283 void set_min_rep(int r) {
284 min_rep = r;
285 }
286 int get_min_rep() const {
287 return min_rep;
288 }
289
290 void set_max_rep(int r) {
291 max_rep = r;
292 }
293 int get_max_rep() const {
294 return max_rep;
295 }
296
297 void set_num_rep(int r) {
298 min_rep = max_rep = r;
299 }
300
301 void set_min_x(int x) {
302 min_x = x;
303 }
304
305 void set_pool_id(int64_t x){
306 pool_id = x;
307 }
308
309 int get_min_x() const {
310 return min_x;
311 }
312
313 void set_max_x(int x) {
314 max_x = x;
315 }
316 int get_max_x() const {
317 return max_x;
318 }
319
320 void set_x(int x) {
321 min_x = max_x = x;
322 }
323
324 void set_min_rule(int rule) {
325 min_rule = rule;
326 }
327 int get_min_rule() const {
328 return min_rule;
329 }
330
331 void set_max_rule(int rule) {
332 max_rule = rule;
333 }
334 int get_max_rule() const {
335 return max_rule;
336 }
337
338 void set_rule(int rule) {
339 min_rule = max_rule = rule;
340 }
341
342 /**
343 * check if any bucket/nodes is referencing an unknown name or type
344 * @param max_id rejects any non-bucket items with id less than this number,
345 * pass 0 to disable this check
346 * @return false if an dangling name/type is referenced or an item id is too
347 * large, true otherwise
348 */
349 bool check_name_maps(unsigned max_id = 0) const;
350 int test();
351 int test_with_fork(int timeout);
352
353 int compare(CrushWrapper& other);
354 };
355
356 #endif