Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / ArgumentTypes.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "include/rbd/features.h"
8 #include "common/config.h"
9 #include "common/strtol.h"
10 #include "common/Formatter.h"
11 #include "global/global_context.h"
12 #include <iostream>
13 #include <boost/tokenizer.hpp>
14
15 namespace rbd {
16 namespace argument_types {
17
18 namespace po = boost::program_options;
19
20 const std::map<uint64_t, std::string> ImageFeatures::FEATURE_MAPPING = {
21   {RBD_FEATURE_LAYERING, RBD_FEATURE_NAME_LAYERING},
22   {RBD_FEATURE_STRIPINGV2, RBD_FEATURE_NAME_STRIPINGV2},
23   {RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_NAME_EXCLUSIVE_LOCK},
24   {RBD_FEATURE_OBJECT_MAP, RBD_FEATURE_NAME_OBJECT_MAP},
25   {RBD_FEATURE_FAST_DIFF, RBD_FEATURE_NAME_FAST_DIFF},
26   {RBD_FEATURE_DEEP_FLATTEN, RBD_FEATURE_NAME_DEEP_FLATTEN},
27   {RBD_FEATURE_JOURNALING, RBD_FEATURE_NAME_JOURNALING},
28   {RBD_FEATURE_DATA_POOL, RBD_FEATURE_NAME_DATA_POOL},
29 };
30
31 Format::Formatter Format::create_formatter(bool pretty) const {
32   if (value == "json") {
33     return Formatter(new JSONFormatter(pretty));
34   } else if (value == "xml") {
35     return Formatter(new XMLFormatter(pretty));
36   }
37   return Formatter();
38 }
39
40 std::string get_name_prefix(ArgumentModifier modifier) {
41   switch (modifier) {
42   case ARGUMENT_MODIFIER_SOURCE:
43     return SOURCE_PREFIX;
44   case ARGUMENT_MODIFIER_DEST:
45     return DEST_PREFIX;
46   default:
47     return "";
48   }
49 }
50
51 std::string get_description_prefix(ArgumentModifier modifier) {
52   switch (modifier) {
53   case ARGUMENT_MODIFIER_SOURCE:
54     return "source ";
55   case ARGUMENT_MODIFIER_DEST:
56     return "destination ";
57   default:
58     return "";
59   }
60 }
61
62 void add_special_pool_option(po::options_description *opt,
63                              std::string prefix) {
64   std::string name = prefix + "-" + POOL_NAME;
65   std::string description = prefix + " pool name";
66
67   opt->add_options()
68     (name.c_str(), po::value<std::string>(), description.c_str());
69 }
70
71 void add_pool_option(po::options_description *opt,
72                      ArgumentModifier modifier,
73                      const std::string &desc_suffix) {
74   std::string name = POOL_NAME + ",p";
75   std::string description = "pool name";
76   switch (modifier) {
77   case ARGUMENT_MODIFIER_NONE:
78     break;
79   case ARGUMENT_MODIFIER_SOURCE:
80     description = "source " + description;
81     break;
82   case ARGUMENT_MODIFIER_DEST:
83     name = DEST_POOL_NAME;
84     description = "destination " + description;
85     break;
86   }
87   description += desc_suffix;
88
89   // TODO add validator
90   opt->add_options()
91     (name.c_str(), po::value<std::string>(), description.c_str());
92 }
93
94 void add_image_option(po::options_description *opt,
95                       ArgumentModifier modifier,
96                       const std::string &desc_suffix) {
97   std::string name = IMAGE_NAME;
98   std::string description = "image name";
99   switch (modifier) {
100   case ARGUMENT_MODIFIER_NONE:
101     break;
102   case ARGUMENT_MODIFIER_SOURCE:
103     description = "source " + description;
104     break;
105   case ARGUMENT_MODIFIER_DEST:
106     name = DEST_IMAGE_NAME;
107     description = "destination " + description;
108     break;
109   }
110   description += desc_suffix;
111
112   // TODO add validator
113   opt->add_options()
114     (name.c_str(), po::value<std::string>(), description.c_str());
115 }
116
117 void add_image_id_option(po::options_description *opt,
118                          const std::string &desc_suffix) {
119   std::string name = IMAGE_ID;
120   std::string description = "image id";
121   description += desc_suffix;
122
123   // TODO add validator
124   opt->add_options()
125     (name.c_str(), po::value<std::string>(), description.c_str());
126 }
127
128 void add_group_option(po::options_description *opt,
129                       ArgumentModifier modifier,
130                       const std::string &desc_suffix) {
131   std::string name = GROUP_NAME;
132   std::string description = "group name";
133   switch (modifier) {
134   case ARGUMENT_MODIFIER_NONE:
135     break;
136   case ARGUMENT_MODIFIER_SOURCE:
137     description = "source " + description;
138     break;
139   case ARGUMENT_MODIFIER_DEST:
140     name = DEST_GROUP_NAME;
141     description = "destination " + description;
142     break;
143   }
144   description += desc_suffix;
145
146   // TODO add validator
147   opt->add_options()
148     (name.c_str(), po::value<std::string>(), description.c_str());
149 }
150
151 void add_snap_option(po::options_description *opt,
152                       ArgumentModifier modifier) {
153
154   std::string name = SNAPSHOT_NAME;
155   std::string description = "snapshot name";
156   switch (modifier) {
157   case ARGUMENT_MODIFIER_NONE:
158     break;
159   case ARGUMENT_MODIFIER_DEST:
160     name = DEST_SNAPSHOT_NAME;
161     description = "destination " + description;
162     break;
163   case ARGUMENT_MODIFIER_SOURCE:
164     description = "source " + description;
165     break;
166   }
167
168   // TODO add validator
169   opt->add_options()
170     (name.c_str(), po::value<std::string>(), description.c_str());
171 }
172
173 void add_journal_option(po::options_description *opt,
174                       ArgumentModifier modifier,
175                       const std::string &desc_suffix) {
176   std::string name = JOURNAL_NAME;
177   std::string description = "journal name";
178   switch (modifier) {
179   case ARGUMENT_MODIFIER_NONE:
180     break;
181   case ARGUMENT_MODIFIER_SOURCE:
182     description = "source " + description;
183     break;
184   case ARGUMENT_MODIFIER_DEST:
185     name = DEST_JOURNAL_NAME;
186     description = "destination " + description;
187     break;
188   }
189   description += desc_suffix;
190
191   // TODO add validator
192   opt->add_options()
193     (name.c_str(), po::value<std::string>(), description.c_str());
194 }
195
196 void add_pool_options(boost::program_options::options_description *pos,
197                       boost::program_options::options_description *opt) {
198   pos->add_options()
199     ("pool-name", "pool name");
200   opt->add_options()
201     ((POOL_NAME + ",p").c_str(), po::value<std::string>(), "pool name");
202 }
203
204 void add_image_spec_options(po::options_description *pos,
205                             po::options_description *opt,
206                             ArgumentModifier modifier) {
207   pos->add_options()
208     ((get_name_prefix(modifier) + IMAGE_SPEC).c_str(),
209      (get_description_prefix(modifier) + "image specification\n" +
210       "(example: [<pool-name>/]<image-name>)").c_str());
211   add_pool_option(opt, modifier);
212   add_image_option(opt, modifier);
213 }
214
215 void add_group_spec_options(po::options_description *pos,
216                             po::options_description *opt,
217                             ArgumentModifier modifier) {
218   pos->add_options()
219     ((get_name_prefix(modifier) + GROUP_SPEC).c_str(),
220      (get_description_prefix(modifier) + "group specification\n" +
221       "(example: [<pool-name>/]<group-name>)").c_str());
222   add_pool_option(opt, modifier);
223   add_group_option(opt, modifier);
224 }
225
226 void add_snap_spec_options(po::options_description *pos,
227                            po::options_description *opt,
228                            ArgumentModifier modifier) {
229   pos->add_options()
230     ((get_name_prefix(modifier) + SNAPSHOT_SPEC).c_str(),
231      (get_description_prefix(modifier) + "snapshot specification\n" +
232       "(example: [<pool-name>/]<image-name>@<snapshot-name>)").c_str());
233   add_pool_option(opt, modifier);
234   add_image_option(opt, modifier);
235   add_snap_option(opt, modifier);
236 }
237
238 void add_image_or_snap_spec_options(po::options_description *pos,
239                                     po::options_description *opt,
240                                     ArgumentModifier modifier) {
241   pos->add_options()
242     ((get_name_prefix(modifier) + IMAGE_OR_SNAPSHOT_SPEC).c_str(),
243      (get_description_prefix(modifier) + "image or snapshot specification\n" +
244       "(example: [<pool-name>/]<image-name>[@<snap-name>])").c_str());
245   add_pool_option(opt, modifier);
246   add_image_option(opt, modifier);
247   add_snap_option(opt, modifier);
248 }
249
250 void add_journal_spec_options(po::options_description *pos,
251                               po::options_description *opt,
252                               ArgumentModifier modifier) {
253
254   pos->add_options()
255     ((get_name_prefix(modifier) + JOURNAL_SPEC).c_str(),
256      (get_description_prefix(modifier) + "journal specification\n" +
257       "(example: [<pool-name>/]<journal-name>)").c_str());
258   add_pool_option(opt, modifier);
259   add_image_option(opt, modifier);
260   add_journal_option(opt, modifier);
261 }
262
263 void add_create_image_options(po::options_description *opt,
264                               bool include_format) {
265   // TODO get default image format from conf
266   if (include_format) {
267     opt->add_options()
268       (IMAGE_FORMAT.c_str(), po::value<ImageFormat>(),
269        "image format [1 (deprecated) or 2]")
270       (IMAGE_NEW_FORMAT.c_str(),
271        po::value<ImageNewFormat>()->zero_tokens(),
272        "use image format 2\n(deprecated)");
273   }
274
275   opt->add_options()
276     (IMAGE_ORDER.c_str(), po::value<ImageOrder>(),
277      "object order [12 <= order <= 25]")
278     (IMAGE_OBJECT_SIZE.c_str(), po::value<ImageObjectSize>(),
279      "object size in B/K/M [4K <= object size <= 32M]")
280     (IMAGE_FEATURES.c_str(), po::value<ImageFeatures>()->composing(),
281      ("image features\n" + get_short_features_help(true)).c_str())
282     (IMAGE_SHARED.c_str(), po::bool_switch(), "shared image")
283     (IMAGE_STRIPE_UNIT.c_str(), po::value<ImageObjectSize>(), "stripe unit in B/K/M")
284     (IMAGE_STRIPE_COUNT.c_str(), po::value<uint64_t>(), "stripe count")
285     (IMAGE_DATA_POOL.c_str(), po::value<std::string>(), "data pool");
286
287   add_create_journal_options(opt);
288 }
289
290 void add_create_journal_options(po::options_description *opt) {
291   opt->add_options()
292     (JOURNAL_SPLAY_WIDTH.c_str(), po::value<uint64_t>(),
293      "number of active journal objects")
294     (JOURNAL_OBJECT_SIZE.c_str(), po::value<JournalObjectSize>(),
295      "size of journal objects")
296     (JOURNAL_POOL.c_str(), po::value<std::string>(),
297      "pool for journal objects");
298 }
299
300 void add_size_option(boost::program_options::options_description *opt) {
301   opt->add_options()
302     ((IMAGE_SIZE + ",s").c_str(), po::value<ImageSize>()->required(),
303      "image size (in M/G/T) [default: M]");
304 }
305
306 void add_sparse_size_option(boost::program_options::options_description *opt) {
307   opt->add_options()
308     (IMAGE_SPARSE_SIZE.c_str(), po::value<ImageObjectSize>(),
309     "sparse size in B/K/M [default: 4K]");
310 }
311
312 void add_path_options(boost::program_options::options_description *pos,
313                       boost::program_options::options_description *opt,
314                       const std::string &description) {
315   pos->add_options()
316     (PATH_NAME.c_str(), po::value<std::string>(), description.c_str());
317   opt->add_options()
318     (PATH.c_str(), po::value<std::string>(), description.c_str());
319 }
320
321 void add_limit_option(po::options_description *opt) {
322   std::string description = "maximum allowed snapshot count";
323
324   opt->add_options()
325     (LIMIT.c_str(), po::value<uint64_t>(), description.c_str());
326 }
327
328 void add_no_progress_option(boost::program_options::options_description *opt) {
329   opt->add_options()
330     (NO_PROGRESS.c_str(), po::bool_switch(), "disable progress output");
331 }
332
333 void add_format_options(boost::program_options::options_description *opt) {
334   opt->add_options()
335     (FORMAT.c_str(), po::value<Format>(), "output format (plain, json, or xml) [default: plain]")
336     (PRETTY_FORMAT.c_str(), po::bool_switch(),
337      "pretty formatting (json and xml)");
338 }
339
340 void add_verbose_option(boost::program_options::options_description *opt) {
341   opt->add_options()
342     (VERBOSE.c_str(), po::bool_switch(), "be verbose");
343 }
344
345 void add_no_error_option(boost::program_options::options_description *opt) {
346   opt->add_options()
347     (NO_ERROR.c_str(), po::bool_switch(), "continue after error");
348 }
349
350 void add_export_format_option(boost::program_options::options_description *opt) {
351   opt->add_options()
352     ("export-format", po::value<ExportFormat>(), "format of image file");
353 }
354
355 std::string get_short_features_help(bool append_suffix) {
356   std::ostringstream oss;
357   bool first_feature = true;
358   oss << "[";
359   for (auto &pair : ImageFeatures::FEATURE_MAPPING) {
360     if (!first_feature) {
361       oss << ", ";
362     }
363     first_feature = false;
364
365     std::string suffix;
366     if (append_suffix) {
367       if ((pair.first & rbd::utils::get_rbd_default_features(g_ceph_context)) != 0) {
368         suffix += "+";
369       }
370       if ((pair.first & RBD_FEATURES_MUTABLE) != 0) {
371         suffix += "*";
372       } else if ((pair.first & RBD_FEATURES_DISABLE_ONLY) != 0) {
373         suffix += "-";
374       }
375       if (!suffix.empty()) {
376         suffix = "(" + suffix + ")";
377       }
378     }
379     oss << pair.second << suffix;
380   }
381   oss << "]";
382   return oss.str();
383 }
384
385 std::string get_long_features_help() {
386   std::ostringstream oss;
387   oss << "Image Features:" << std::endl
388       << "  (*) supports enabling/disabling on existing images" << std::endl
389       << "  (-) supports disabling-only on existing images" << std::endl
390       << "  (+) enabled by default for new images if features not specified"
391       << std::endl;
392   return oss.str();
393 }
394
395 void validate(boost::any& v, const std::vector<std::string>& values,
396               ImageSize *target_type, int) {
397   po::validators::check_first_occurrence(v);
398   const std::string &s = po::validators::get_single_string(values);
399
400   std::string parse_error;
401   uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
402   if (!parse_error.empty()) {
403     throw po::validation_error(po::validation_error::invalid_option_value);
404   }
405
406   //NOTE: We can remove below given three lines of code once all applications,
407   //which use this CLI will adopt B/K/M/G/T/P/E with size value
408   if (isdigit(*s.rbegin())) {
409     size = size << 20;   // Default MB to Bytes
410   }
411   v = boost::any(size);
412 }
413
414 void validate(boost::any& v, const std::vector<std::string>& values,
415               ImageOrder *target_type, int dummy) {
416   po::validators::check_first_occurrence(v);
417   const std::string &s = po::validators::get_single_string(values);
418   try {
419     uint64_t order = boost::lexical_cast<uint64_t>(s);
420     if (order >= 12 && order <= 25) {
421       v = boost::any(order);
422       return;
423     }
424   } catch (const boost::bad_lexical_cast &) {
425   }
426   throw po::validation_error(po::validation_error::invalid_option_value);
427 }
428
429 void validate(boost::any& v, const std::vector<std::string>& values,
430               ImageObjectSize *target_type, int dummy) {
431   po::validators::check_first_occurrence(v);
432   const std::string &s = po::validators::get_single_string(values);
433   
434   std::string parse_error;
435   uint64_t objectsize = strict_sistrtoll(s.c_str(), &parse_error);
436   if (!parse_error.empty()) {
437     throw po::validation_error(po::validation_error::invalid_option_value);
438   }
439   v = boost::any(objectsize);
440 }
441
442 void validate(boost::any& v, const std::vector<std::string>& values,
443               ImageFormat *target_type, int dummy) {
444   po::validators::check_first_occurrence(v);
445   const std::string &s = po::validators::get_single_string(values);
446   try {
447     uint32_t format = boost::lexical_cast<uint32_t>(s);
448     if (format == 1 || format == 2) {
449       v = boost::any(format);
450       return;
451     }
452   } catch (const boost::bad_lexical_cast &) {
453   }
454   throw po::validation_error(po::validation_error::invalid_option_value);
455 }
456
457 void validate(boost::any& v, const std::vector<std::string>& values,
458               ImageNewFormat *target_type, int dummy) {
459   std::cout << "rbd: --new-format is deprecated, use --image-format"
460             << std::endl;
461   v = boost::any(true);
462 }
463
464 void validate(boost::any& v, const std::vector<std::string>& values,
465               ImageFeatures *target_type, int) {
466   if (v.empty()) {
467     v = boost::any(static_cast<uint64_t>(0));
468   }
469
470   uint64_t &features = boost::any_cast<uint64_t &>(v);
471   for (auto &value : values) {
472     boost::char_separator<char> sep(",");
473     boost::tokenizer<boost::char_separator<char> > tok(value, sep);
474     for (auto &token : tok) {
475       bool matched = false;
476       for (auto &it : ImageFeatures::FEATURE_MAPPING) {
477         if (token == it.second) {
478           features |= it.first;
479           matched = true;
480           break;
481         }
482       }
483
484       if (!matched) {
485         throw po::validation_error(po::validation_error::invalid_option_value);
486       }
487     }
488   }
489 }
490
491 void validate(boost::any& v, const std::vector<std::string>& values,
492               Format *target_type, int) {
493   po::validators::check_first_occurrence(v);
494   const std::string &s = po::validators::get_single_string(values);
495   if (s == "plain" || s == "json" || s == "xml") {
496     v = boost::any(Format(s));
497   } else {
498     throw po::validation_error(po::validation_error::invalid_option_value);
499   }
500 }
501
502 void validate(boost::any& v, const std::vector<std::string>& values,
503               JournalObjectSize *target_type, int) {
504   po::validators::check_first_occurrence(v);
505   const std::string &s = po::validators::get_single_string(values);
506
507   std::string parse_error;
508   uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
509   if (parse_error.empty() && (size >= (1 << 12))) {
510     v = boost::any(size);
511     return;
512   }
513   throw po::validation_error(po::validation_error::invalid_option_value);
514 }
515
516 void validate(boost::any& v, const std::vector<std::string>& values,
517               ExportFormat *target_type, int) {
518   po::validators::check_first_occurrence(v);
519   const std::string &s = po::validators::get_single_string(values);
520
521   std::string parse_error;
522   uint64_t format = strict_sistrtoll(s.c_str(), &parse_error);
523   if (!parse_error.empty() || (format != 1 && format != 2)) {
524     throw po::validation_error(po::validation_error::invalid_option_value);
525   }
526
527   v = boost::any(format);
528 }
529
530 void validate(boost::any& v, const std::vector<std::string>& values,
531               Secret *target_type, int) {
532   std::cerr << "rbd: --secret is deprecated, use --keyfile" << std::endl;
533
534   po::validators::check_first_occurrence(v);
535   const std::string &s = po::validators::get_single_string(values);
536   g_conf->set_val_or_die("keyfile", s.c_str());
537   v = boost::any(s);
538 }
539
540 } // namespace argument_types
541 } // namespace rbd