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