Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / Utils.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/Utils.h"
5 #include "include/assert.h"
6 #include "include/Context.h"
7 #include "include/encoding.h"
8 #include "common/common_init.h"
9 #include "include/stringify.h"
10 #include "include/rbd/features.h"
11 #include "common/config.h"
12 #include "common/errno.h"
13 #include "common/safe_io.h"
14 #include "global/global_context.h"
15 #include <iostream>
16 #include <boost/regex.hpp>
17 #include <boost/algorithm/string.hpp>
18 #include <boost/lexical_cast.hpp>
19
20 namespace rbd {
21 namespace utils {
22
23 namespace at = argument_types;
24 namespace po = boost::program_options;
25
26 int ProgressContext::update_progress(uint64_t offset, uint64_t total) {
27   if (progress) {
28     int pc = total ? (offset * 100ull / total) : 0;
29     if (pc != last_pc) {
30       cerr << "\r" << operation << ": "
31            << pc << "% complete...";
32       cerr.flush();
33       last_pc = pc;
34     }
35   }
36   return 0;
37 }
38
39 void ProgressContext::finish() {
40   if (progress) {
41     cerr << "\r" << operation << ": 100% complete...done." << std::endl;
42   }
43 }
44
45 void ProgressContext::fail() {
46   if (progress) {
47     cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
48          << std::endl;
49   }
50 }
51
52 void aio_context_callback(librbd::completion_t completion, void *arg)
53 {
54   librbd::RBD::AioCompletion *aio_completion =
55     reinterpret_cast<librbd::RBD::AioCompletion*>(completion);
56   Context *context = reinterpret_cast<Context *>(arg);
57   context->complete(aio_completion->get_return_value());
58   aio_completion->release();
59 }
60
61 int read_string(int fd, unsigned max, std::string *out) {
62   char buf[4];
63
64   int r = safe_read_exact(fd, buf, 4);
65   if (r < 0)
66     return r;
67
68   bufferlist bl;
69   bl.append(buf, 4);
70   bufferlist::iterator p = bl.begin();
71   uint32_t len;
72   ::decode(len, p);
73   if (len > max)
74     return -EINVAL;
75
76   char sbuf[len];
77   r = safe_read_exact(fd, sbuf, len);
78   if (r < 0)
79     return r;
80   out->assign(sbuf, len);
81   return len;
82 }
83
84 int extract_spec(const std::string &spec, std::string *pool_name,
85                  std::string *image_name, std::string *snap_name,
86                  SpecValidation spec_validation) {
87   if (!g_ceph_context->_conf->get_val<bool>("rbd_validate_names")) {
88     spec_validation = SPEC_VALIDATION_NONE;
89   }
90
91   boost::regex pattern;
92   switch (spec_validation) {
93   case SPEC_VALIDATION_FULL:
94     // disallow "/" and "@" in image and snap name
95     pattern = "^(?:([^/@]+)/)?([^/@]+)(?:@([^/@]+))?$";
96     break;
97   case SPEC_VALIDATION_SNAP:
98     // disallow "/" and "@" in snap name
99     pattern = "^(?:([^/]+)/)?([^@]+)(?:@([^/@]+))?$";
100     break;
101   case SPEC_VALIDATION_NONE:
102     // relaxed pattern assumes pool is before first "/" and snap
103     // name is after first "@"
104     pattern = "^(?:([^/]+)/)?([^@]+)(?:@(.+))?$";
105     break;
106   default:
107     assert(false);
108     break;
109   }
110
111   boost::smatch match;
112   if (!boost::regex_match(spec, match, pattern)) {
113     std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
114     return -EINVAL;
115   }
116
117   if (pool_name != nullptr && match[1].matched) {
118     *pool_name = match[1];
119   }
120   if (image_name != nullptr) {
121     *image_name = match[2];
122   }
123   if (snap_name != nullptr && match[3].matched) {
124     *snap_name = match[3];
125   }
126   return 0;
127 }
128
129 int extract_group_spec(const std::string &spec,
130                        std::string *pool_name,
131                        std::string *group_name) {
132   boost::regex pattern;
133   pattern = "^(?:([^/]+)/)?(.+)?$";
134
135   boost::smatch match;
136   if (!boost::regex_match(spec, match, pattern)) {
137     std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
138     return -EINVAL;
139   }
140
141   if (pool_name != nullptr && match[1].matched) {
142     *pool_name = match[1];
143   }
144   if (group_name != nullptr) {
145     *group_name = match[2];
146   }
147
148   return 0;
149 }
150
151 int extract_image_id_spec(const std::string &spec, std::string *pool_name,
152                           std::string *image_id) {
153   boost::regex pattern;
154   pattern = "^(?:([^/]+)/)?(.+)?$";
155
156   boost::smatch match;
157   if (!boost::regex_match(spec, match, pattern)) {
158     std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
159     return -EINVAL;
160   }
161
162   if (pool_name != nullptr && match[1].matched) {
163     *pool_name = match[1];
164   }
165   if (image_id != nullptr) {
166     *image_id = match[2];
167   }
168
169   return 0;
170 }
171
172 std::string get_positional_argument(const po::variables_map &vm, size_t index) {
173   if (vm.count(at::POSITIONAL_ARGUMENTS) == 0) {
174     return "";
175   }
176
177   const std::vector<std::string> &args =
178     boost::any_cast<std::vector<std::string> >(
179       vm[at::POSITIONAL_ARGUMENTS].value());
180   if (index < args.size()) {
181     return args[index];
182   }
183   return "";
184 }
185
186 std::string get_default_pool_name() {
187   return g_ceph_context->_conf->get_val<std::string>("rbd_default_pool");
188 }
189
190 std::string get_pool_name(const po::variables_map &vm, size_t *arg_index) {
191   std::string pool_name;
192   if (vm.count(at::POOL_NAME)) {
193     pool_name = vm[at::POOL_NAME].as<std::string>();
194   } else {
195     pool_name = get_positional_argument(vm, *arg_index);
196     if (!pool_name.empty()) {
197        ++(*arg_index);
198     }
199   }
200
201   if (pool_name.empty()) {
202     pool_name = get_default_pool_name();
203   }
204   return pool_name;
205 }
206
207 int get_special_pool_group_names(const po::variables_map &vm,
208                                  size_t *arg_index,
209                                  std::string *group_pool_name,
210                                  std::string *group_name) {
211   if (nullptr == group_pool_name) return -EINVAL;
212   if (nullptr == group_name) return -EINVAL;
213   std::string pool_key = at::POOL_NAME;
214
215   std::string group_pool_key = "group-" + at::POOL_NAME;
216   std::string group_key = at::GROUP_NAME;
217
218   if (vm.count(group_pool_key)) {
219     *group_pool_name = vm[group_pool_key].as<std::string>();
220   }
221
222   if (vm.count(group_key)) {
223     *group_name = vm[group_key].as<std::string>();
224   }
225
226   int r;
227   if (group_name->empty()) {
228     std::string spec = utils::get_positional_argument(vm, (*arg_index)++);
229     if (!spec.empty()) {
230       r = utils::extract_group_spec(spec, group_pool_name, group_name);
231       if (r < 0) {
232         return r;
233       }
234     }
235   }
236
237   if (group_pool_name->empty() && vm.count(pool_key)) {
238     *group_pool_name = vm[pool_key].as<std::string>();
239   }
240
241   if (group_pool_name->empty()) {
242     *group_pool_name = get_default_pool_name();
243   }
244
245   if (group_name->empty()) {
246     std::cerr << "rbd: consistency group name was not specified" << std::endl;
247     return -EINVAL;
248   }
249
250   return 0;
251 }
252
253 int get_special_pool_image_names(const po::variables_map &vm,
254                                  size_t *arg_index,
255                                  std::string *image_pool_name,
256                                  std::string *image_name) {
257   if (nullptr == image_pool_name) return -EINVAL;
258   if (nullptr == image_name) return -EINVAL;
259
260   std::string pool_key = at::POOL_NAME;
261
262   std::string image_pool_key = "image-" + at::POOL_NAME;
263   std::string image_key = at::IMAGE_NAME;
264
265   if (vm.count(image_pool_key)) {
266     *image_pool_name = vm[image_pool_key].as<std::string>();
267   }
268
269   if (vm.count(image_key)) {
270     *image_name = vm[image_key].as<std::string>();
271   }
272
273   int r;
274   if (image_name->empty()) {
275     std::string spec = utils::get_positional_argument(vm, (*arg_index)++);
276     if (!spec.empty()) {
277       r = utils::extract_spec(spec, image_pool_name,
278                               image_name, nullptr,
279                               utils::SPEC_VALIDATION_NONE);
280       if (r < 0) {
281         return r;
282       }
283     }
284   }
285
286   if (image_pool_name->empty() && vm.count(pool_key)) {
287     *image_pool_name = vm[pool_key].as<std::string>();
288   }
289
290   if (image_pool_name->empty()) {
291     *image_pool_name = get_default_pool_name();
292   }
293
294   if (image_name->empty()) {
295     std::cerr << "rbd: image name was not specified" << std::endl;
296     return -EINVAL;
297   }
298
299   return 0;
300 }
301
302 int get_pool_image_id(const po::variables_map &vm,
303                       size_t *spec_arg_index,
304                       std::string *pool_name,
305                       std::string *image_id) {
306
307   if (vm.count(at::POOL_NAME) && pool_name != nullptr) {
308     *pool_name = vm[at::POOL_NAME].as<std::string>();
309   }
310   if (vm.count(at::IMAGE_ID) && image_id != nullptr) {
311     *image_id = vm[at::IMAGE_ID].as<std::string>();
312   }
313
314   int r;
315   if (image_id != nullptr && spec_arg_index != nullptr && image_id->empty()) {
316     std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
317     if (!spec.empty()) {
318       r = extract_image_id_spec(spec, pool_name, image_id);
319       if (r < 0) {
320         return r;
321       }
322     }
323   }
324
325   if (pool_name != nullptr && pool_name->empty()) {
326     *pool_name = get_default_pool_name();
327   }
328
329   if (image_id != nullptr && image_id->empty()) {
330     std::cerr << "rbd: image id was not specified" << std::endl;
331     return -EINVAL;
332   }
333
334   return 0;
335 }
336
337 int get_pool_group_names(const po::variables_map &vm,
338                          at::ArgumentModifier mod,
339                          size_t *spec_arg_index,
340                          std::string *pool_name,
341                          std::string *group_name) {
342   std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
343     at::DEST_POOL_NAME : at::POOL_NAME);
344   std::string group_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
345     at::DEST_GROUP_NAME : at::GROUP_NAME);
346
347   if (vm.count(pool_key) && pool_name != nullptr) {
348     *pool_name = vm[pool_key].as<std::string>();
349   }
350   if (vm.count(group_key) && group_name != nullptr) {
351     *group_name = vm[group_key].as<std::string>();
352   }
353
354   int r;
355   if (group_name != nullptr && spec_arg_index != nullptr &&
356       group_name->empty()) {
357     std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
358     if (!spec.empty()) {
359       r = extract_group_spec(spec, pool_name, group_name);
360       if (r < 0) {
361         return r;
362       }
363     }
364   }
365
366   if (pool_name != nullptr && pool_name->empty()) {
367     *pool_name = get_default_pool_name();
368   }
369
370   if (group_name != nullptr && group_name->empty()) {
371     std::string prefix = at::get_description_prefix(mod);
372     std::cerr << "rbd: "
373               << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
374               << "group name was not specified" << std::endl;
375     return -EINVAL;
376   }
377
378   return 0;
379 }
380
381 int get_pool_image_snapshot_names(const po::variables_map &vm,
382                                   at::ArgumentModifier mod,
383                                   size_t *spec_arg_index,
384                                   std::string *pool_name,
385                                   std::string *image_name,
386                                   std::string *snap_name,
387                                   SnapshotPresence snapshot_presence,
388                                   SpecValidation spec_validation,
389                                   bool image_required) {
390   std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
391     at::DEST_POOL_NAME : at::POOL_NAME);
392   std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
393     at::DEST_IMAGE_NAME : at::IMAGE_NAME);
394   std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
395         at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
396
397   if (vm.count(pool_key) && pool_name != nullptr) {
398     *pool_name = vm[pool_key].as<std::string>();
399   }
400   if (vm.count(image_key) && image_name != nullptr) {
401     *image_name = vm[image_key].as<std::string>();
402   }
403   if (vm.count(snap_key) && snap_name != nullptr) {
404      *snap_name = vm[snap_key].as<std::string>();
405   }
406
407   int r;
408   if (image_name != nullptr && !image_name->empty()) {
409     // despite the separate pool and snapshot name options,
410     // we can also specify them via the image option
411     std::string image_name_copy(*image_name);
412     r = extract_spec(image_name_copy, pool_name, image_name, snap_name,
413                      spec_validation);
414     if (r < 0) {
415       return r;
416     }
417   }
418
419   if (image_name != nullptr && spec_arg_index != nullptr &&
420       image_name->empty()) {
421     std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
422     if (!spec.empty()) {
423       r = extract_spec(spec, pool_name, image_name, snap_name, spec_validation);
424       if (r < 0) {
425         return r;
426       }
427     }
428   }
429
430   if (pool_name != nullptr && pool_name->empty()) {
431     *pool_name = get_default_pool_name();
432   }
433
434   if (image_name != nullptr && image_required && image_name->empty()) {
435     std::string prefix = at::get_description_prefix(mod);
436     std::cerr << "rbd: "
437               << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
438               << "image name was not specified" << std::endl;
439     return -EINVAL;
440   }
441
442   //Validate pool name while creating/renaming/copying/cloning/importing/etc
443   if (spec_validation == SPEC_VALIDATION_FULL) {
444     boost::regex pattern("^[^@/]+?$");
445     if (!boost::regex_match (*pool_name, pattern)) {
446       std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl;
447       return -EINVAL;
448     }
449   }
450
451   if (snap_name != nullptr) {
452     r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
453                                spec_validation);
454     if (r < 0) {
455       return r;
456     }
457   }
458   return 0;
459 }
460
461 int get_pool_snapshot_names(const po::variables_map &vm,
462                             at::ArgumentModifier mod,
463                             size_t *spec_arg_index,
464                             std::string *pool_name,
465                             std::string *snap_name,
466                             SnapshotPresence snapshot_presence,
467                             SpecValidation spec_validation) {
468   std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
469     at::DEST_POOL_NAME : at::POOL_NAME);
470   std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
471         at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
472
473   if (vm.count(pool_key) && pool_name != nullptr) {
474     *pool_name = vm[pool_key].as<std::string>();
475   }
476   if (vm.count(snap_key) && snap_name != nullptr) {
477      *snap_name = vm[snap_key].as<std::string>();
478   }
479
480   if (pool_name != nullptr && pool_name->empty()) {
481     *pool_name = get_default_pool_name();
482   }
483
484   if (snap_name != nullptr) {
485     int r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
486                                    spec_validation);
487     if (r < 0) {
488       return r;
489     }
490   }
491   return 0;
492 }
493
494 int get_pool_journal_names(const po::variables_map &vm,
495                            at::ArgumentModifier mod,
496                            size_t *spec_arg_index,
497                            std::string *pool_name,
498                            std::string *journal_name) {
499   std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
500     at::DEST_POOL_NAME : at::POOL_NAME);
501   std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
502     at::DEST_IMAGE_NAME : at::IMAGE_NAME);
503   std::string journal_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
504     at::DEST_JOURNAL_NAME : at::JOURNAL_NAME);
505
506   if (vm.count(pool_key) && pool_name != nullptr) {
507     *pool_name = vm[pool_key].as<std::string>();
508   }
509   if (vm.count(journal_key) && journal_name != nullptr) {
510     *journal_name = vm[journal_key].as<std::string>();
511   }
512
513   std::string image_name;
514   if (vm.count(image_key)) {
515     image_name = vm[image_key].as<std::string>();
516   }
517
518   int r;
519   if (journal_name != nullptr && !journal_name->empty()) {
520     // despite the separate pool option,
521     // we can also specify them via the journal option
522     std::string journal_name_copy(*journal_name);
523     r = extract_spec(journal_name_copy, pool_name, journal_name, nullptr,
524                      SPEC_VALIDATION_FULL);
525     if (r < 0) {
526       return r;
527     }
528   }
529
530   if (!image_name.empty()) {
531     // despite the separate pool option,
532     // we can also specify them via the image option
533     std::string image_name_copy(image_name);
534     r = extract_spec(image_name_copy, pool_name, &image_name, nullptr,
535                      SPEC_VALIDATION_NONE);
536     if (r < 0) {
537       return r;
538     }
539   }
540
541   if (journal_name != nullptr && spec_arg_index != nullptr &&
542       journal_name->empty()) {
543     std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
544     if (!spec.empty()) {
545       r = extract_spec(spec, pool_name, journal_name, nullptr,
546                        SPEC_VALIDATION_FULL);
547       if (r < 0) {
548         return r;
549       }
550     }
551   }
552
553   if (pool_name != nullptr && pool_name->empty()) {
554     *pool_name = get_default_pool_name();
555   }
556
557   if (pool_name != nullptr && journal_name != nullptr &&
558       journal_name->empty() && !image_name.empty()) {
559     // Try to get journal name from image info.
560     librados::Rados rados;
561     librados::IoCtx io_ctx;
562     librbd::Image image;
563     int r = init_and_open_image(*pool_name, image_name, "", "", true, &rados,
564                                 &io_ctx, &image);
565     if (r < 0) {
566       std::cerr << "rbd: failed to open image " << image_name
567                 << " to get journal name: " << cpp_strerror(r) << std::endl;
568       return r;
569     }
570
571     uint64_t features;
572     r = image.features(&features);
573     if (r < 0) {
574       return r;
575     }
576     if ((features & RBD_FEATURE_JOURNALING) == 0) {
577       std::cerr << "rbd: journaling is not enabled for image " << image_name
578                 << std::endl;
579       return -EINVAL;
580     }
581     *journal_name = image_id(image);
582   }
583
584   if (journal_name != nullptr && journal_name->empty()) {
585     std::string prefix = at::get_description_prefix(mod);
586     std::cerr << "rbd: "
587               << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
588               << "journal was not specified" << std::endl;
589     return -EINVAL;
590   }
591
592   return 0;
593 }
594
595 int validate_snapshot_name(at::ArgumentModifier mod,
596                            const std::string &snap_name,
597                            SnapshotPresence snapshot_presence,
598                            SpecValidation spec_validation) {
599   std::string prefix = at::get_description_prefix(mod);
600   switch (snapshot_presence) {
601   case SNAPSHOT_PRESENCE_PERMITTED:
602     break;
603   case SNAPSHOT_PRESENCE_NONE:
604     if (!snap_name.empty()) {
605       std::cerr << "rbd: "
606                 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
607                 << "snapname specified for a command that doesn't use it"
608                 << std::endl;
609       return -EINVAL;
610     }
611     break;
612   case SNAPSHOT_PRESENCE_REQUIRED:
613     if (snap_name.empty()) {
614       std::cerr << "rbd: "
615                 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
616                 << "snap name was not specified" << std::endl;
617       return -EINVAL;
618     }
619     break;
620   }
621
622   if (spec_validation == SPEC_VALIDATION_SNAP) {
623     // disallow "/" and "@" in snap name
624     boost::regex pattern("^[^@/]*?$");
625     if (!boost::regex_match (snap_name, pattern)) {
626       std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl;
627       return -EINVAL;
628     }
629   }
630   return 0;
631 }
632
633 int get_image_options(const boost::program_options::variables_map &vm,
634                       bool get_format, librbd::ImageOptions *opts) {
635   uint64_t order = 0, stripe_unit = 0, stripe_count = 0, object_size = 0;
636   uint64_t features = 0, features_clear = 0, features_set = 0;
637   std::string data_pool;
638   bool order_specified = true;
639   bool features_specified = false;
640   bool features_clear_specified = false;
641   bool features_set_specified = false;
642   bool stripe_specified = false;
643
644   if (vm.count(at::IMAGE_ORDER)) {
645     order = vm[at::IMAGE_ORDER].as<uint64_t>();
646     std::cerr << "rbd: --order is deprecated, use --object-size"
647               << std::endl;
648   } else if (vm.count(at::IMAGE_OBJECT_SIZE)) {
649     object_size = vm[at::IMAGE_OBJECT_SIZE].as<uint64_t>();
650     order = std::round(std::log2(object_size)); 
651   } else {
652     order_specified = false;
653   }
654
655   if (vm.count(at::IMAGE_FEATURES)) {
656     features = vm[at::IMAGE_FEATURES].as<uint64_t>();
657     features_specified = true;
658   } else {
659     features = get_rbd_default_features(g_ceph_context);
660   }
661
662   if (vm.count(at::IMAGE_STRIPE_UNIT)) {
663     stripe_unit = vm[at::IMAGE_STRIPE_UNIT].as<uint64_t>();
664     stripe_specified = true;
665   }
666
667   if (vm.count(at::IMAGE_STRIPE_COUNT)) {
668     stripe_count = vm[at::IMAGE_STRIPE_COUNT].as<uint64_t>();
669     stripe_specified = true;
670   }
671
672   if (vm.count(at::IMAGE_SHARED) && vm[at::IMAGE_SHARED].as<bool>()) {
673     if (features_specified) {
674       features &= ~RBD_FEATURES_SINGLE_CLIENT;
675     } else {
676       features_clear |= RBD_FEATURES_SINGLE_CLIENT;
677       features_clear_specified = true;
678     }
679   }
680
681   if (vm.count(at::IMAGE_DATA_POOL)) {
682     data_pool = vm[at::IMAGE_DATA_POOL].as<std::string>();
683   }
684
685   if (get_format) {
686     uint64_t format = 0;
687     bool format_specified = false;
688     if (vm.count(at::IMAGE_NEW_FORMAT)) {
689       format = 2;
690       format_specified = true;
691     } else if (vm.count(at::IMAGE_FORMAT)) {
692       format = vm[at::IMAGE_FORMAT].as<uint32_t>();
693       format_specified = true;
694     }
695     if (format == 1) {
696       std::cerr << "rbd: image format 1 is deprecated" << std::endl;
697     }
698
699     if (features_specified && features != 0) {
700       if (format_specified && format == 1) {
701         std::cerr << "rbd: features not allowed with format 1; "
702                   << "use --image-format 2" << std::endl;
703         return -EINVAL;
704       } else {
705         format = 2;
706         format_specified = true;
707       }
708     }
709
710     if ((stripe_unit || stripe_count) &&
711         (stripe_unit != (1ull << order) && stripe_count != 1)) {
712       if (format_specified && format == 1) {
713         std::cerr << "rbd: non-default striping not allowed with format 1; "
714                   << "use --image-format 2" << std::endl;
715         return -EINVAL;
716       } else {
717         format = 2;
718         format_specified = true;
719       }
720     }
721
722     if (!data_pool.empty()) {
723       if (format_specified && format == 1) {
724         std::cerr << "rbd: data pool not allowed with format 1; "
725                   << "use --image-format 2" << std::endl;
726         return -EINVAL;
727       } else {
728         format = 2;
729         format_specified = true;
730       }
731     }
732
733     if (format_specified) {
734       int r = g_conf->set_val("rbd_default_format", stringify(format));
735       assert(r == 0);
736       opts->set(RBD_IMAGE_OPTION_FORMAT, format);
737     }
738   }
739
740   if (order_specified)
741     opts->set(RBD_IMAGE_OPTION_ORDER, order);
742   if (features_specified)
743     opts->set(RBD_IMAGE_OPTION_FEATURES, features);
744   if (features_clear_specified) {
745     opts->set(RBD_IMAGE_OPTION_FEATURES_CLEAR, features_clear);
746   }
747   if (features_set_specified)
748     opts->set(RBD_IMAGE_OPTION_FEATURES_SET, features_set);
749   if (stripe_specified) {
750     opts->set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
751     opts->set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
752   }
753   if (!data_pool.empty()) {
754     opts->set(RBD_IMAGE_OPTION_DATA_POOL, data_pool);
755   }
756   int r = get_journal_options(vm, opts);
757   if (r < 0) {
758     return r;
759   }
760
761   return 0;
762 }
763
764 int get_journal_options(const boost::program_options::variables_map &vm,
765                         librbd::ImageOptions *opts) {
766
767   if (vm.count(at::JOURNAL_OBJECT_SIZE)) {
768     uint64_t size = vm[at::JOURNAL_OBJECT_SIZE].as<uint64_t>();
769     uint64_t order = 12;
770     while ((1ULL << order) < size) {
771       order++;
772     }
773     opts->set(RBD_IMAGE_OPTION_JOURNAL_ORDER, order);
774
775     int r = g_conf->set_val("rbd_journal_order", stringify(order));
776     assert(r == 0);
777   }
778   if (vm.count(at::JOURNAL_SPLAY_WIDTH)) {
779     opts->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH,
780               vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>());
781
782     int r = g_conf->set_val("rbd_journal_splay_width",
783                             stringify(
784                               vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>()));
785     assert(r == 0);
786   }
787   if (vm.count(at::JOURNAL_POOL)) {
788     opts->set(RBD_IMAGE_OPTION_JOURNAL_POOL,
789               vm[at::JOURNAL_POOL].as<std::string>());
790
791     int r = g_conf->set_val("rbd_journal_pool",
792                             vm[at::JOURNAL_POOL].as<std::string>());
793     assert(r == 0);
794   }
795
796   return 0;
797 }
798
799 int get_image_size(const boost::program_options::variables_map &vm,
800                    uint64_t *size) {
801   if (vm.count(at::IMAGE_SIZE) == 0) {
802     std::cerr << "rbd: must specify --size <M/G/T>" << std::endl;
803     return -EINVAL;
804   }
805
806   *size = vm[at::IMAGE_SIZE].as<uint64_t>();
807   return 0;
808 }
809
810 int get_path(const boost::program_options::variables_map &vm,
811              const std::string &positional_path, std::string *path) {
812   if (!positional_path.empty()) {
813     *path = positional_path;
814   } else if (vm.count(at::PATH)) {
815     *path = vm[at::PATH].as<std::string>();
816   }
817
818   if (path->empty()) {
819     std::cerr << "rbd: path was not specified" << std::endl;
820     return -EINVAL;
821   }
822   return 0;
823 }
824
825 int get_formatter(const po::variables_map &vm,
826                   at::Format::Formatter *formatter) {
827   if (vm.count(at::FORMAT)) {
828     bool pretty = vm[at::PRETTY_FORMAT].as<bool>();
829     *formatter = vm[at::FORMAT].as<at::Format>().create_formatter(pretty);
830     if (*formatter == nullptr && pretty) {
831       std::cerr << "rbd: --pretty-format only works when --format "
832                 << "is json or xml" << std::endl;
833       return -EINVAL;
834     } else if (*formatter != nullptr && !pretty) {
835       formatter->get()->enable_line_break();
836     }
837   } else if (vm[at::PRETTY_FORMAT].as<bool>()) {
838     std::cerr << "rbd: --pretty-format only works when --format "
839               << "is json or xml" << std::endl;
840     return -EINVAL;
841   }
842   return 0;
843 }
844
845 void init_context() {
846   g_conf->set_val_or_die("rbd_cache_writethrough_until_flush", "false");
847   g_conf->apply_changes(NULL);
848   common_init_finish(g_ceph_context);
849 }
850
851 int init(const std::string &pool_name, librados::Rados *rados,
852          librados::IoCtx *io_ctx) {
853   init_context();
854
855   int r = rados->init_with_context(g_ceph_context);
856   if (r < 0) {
857     std::cerr << "rbd: couldn't initialize rados!" << std::endl;
858     return r;
859   }
860
861   r = rados->connect();
862   if (r < 0) {
863     std::cerr << "rbd: couldn't connect to the cluster!" << std::endl;
864     return r;
865   }
866
867   r = init_io_ctx(*rados, pool_name, io_ctx);
868   if (r < 0) {
869     return r;
870   }
871   return 0;
872 }
873
874 int init_io_ctx(librados::Rados &rados, const std::string &pool_name,
875                 librados::IoCtx *io_ctx) {
876   int r = rados.ioctx_create(pool_name.c_str(), *io_ctx);
877   if (r < 0) {
878     if (r == -ENOENT && pool_name == get_default_pool_name()) {
879       std::cerr << "rbd: error opening default pool "
880                 << "'" << pool_name << "'" << std::endl
881                 << "Ensure that the default pool has been created or specify "
882                 << "an alternate pool name." << std::endl;
883     } else {
884       std::cerr << "rbd: error opening pool '" << pool_name << "': "
885                 << cpp_strerror(r) << std::endl;
886     }
887     return r;
888   }
889   return 0;
890 }
891
892 int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
893                bool read_only, librbd::Image *image) {
894   int r;
895   librbd::RBD rbd;
896   if (read_only) {
897     r = rbd.open_read_only(io_ctx, *image, image_name.c_str(), NULL);
898   } else {
899     r = rbd.open(io_ctx, *image, image_name.c_str());
900   }
901
902   if (r < 0) {
903     std::cerr << "rbd: error opening image " << image_name << ": "
904               << cpp_strerror(r) << std::endl;
905     return r;
906   }
907   return 0;
908 }
909
910 int open_image_by_id(librados::IoCtx &io_ctx, const std::string &image_id,
911                      bool read_only, librbd::Image *image) {
912   int r;
913   librbd::RBD rbd;
914   if (read_only) {
915     r = rbd.open_by_id_read_only(io_ctx, *image, image_id.c_str(), NULL);
916   } else {
917     r = rbd.open_by_id(io_ctx, *image, image_id.c_str());
918   }
919
920   if (r < 0) {
921     std::cerr << "rbd: error opening image with id " << image_id << ": "
922               << cpp_strerror(r) << std::endl;
923     return r;
924   }
925   return 0;
926 }
927
928 int init_and_open_image(const std::string &pool_name,
929                         const std::string &image_name,
930                         const std::string &image_id,
931                         const std::string &snap_name, bool read_only,
932                         librados::Rados *rados, librados::IoCtx *io_ctx,
933                         librbd::Image *image) {
934   int r = init(pool_name, rados, io_ctx);
935   if (r < 0) {
936     return r;
937   }
938
939   if (image_id.empty()) {
940     r = open_image(*io_ctx, image_name, read_only, image);
941   } else {
942     r = open_image_by_id(*io_ctx, image_id, read_only, image);
943   }
944   if (r < 0) {
945     return r;
946   }
947
948   if (!snap_name.empty()) {
949     r = snap_set(*image, snap_name);
950     if (r < 0) {
951       return r;
952     }
953   }
954   return 0;
955 }
956
957 int snap_set(librbd::Image &image, const std::string &snap_name) {
958   int r = image.snap_set(snap_name.c_str());
959   if (r < 0) {
960     std::cerr << "error setting snapshot context: " << cpp_strerror(r)
961               << std::endl;
962     return r;
963   }
964   return 0;
965 }
966
967 void calc_sparse_extent(const bufferptr &bp,
968                         size_t sparse_size,
969                         size_t buffer_offset,
970                         uint64_t buffer_length,
971                         size_t *write_length,
972                         bool *zeroed) {
973   if (sparse_size == 0) {
974     // sparse writes are disabled -- write the full extent
975     assert(buffer_offset == 0);
976     *write_length = buffer_length;
977     *zeroed = false;
978     return;
979   }
980
981   *write_length = 0;
982   size_t original_offset = buffer_offset;
983   while (buffer_offset < buffer_length) {
984     size_t extent_size = std::min<size_t>(
985       sparse_size, buffer_length - buffer_offset);
986
987     bufferptr extent(bp, buffer_offset, extent_size);
988
989     bool extent_is_zero = extent.is_zero();
990     if (original_offset == buffer_offset) {
991       *zeroed = extent_is_zero;
992     } else if (*zeroed != extent_is_zero) {
993       assert(*write_length > 0);
994       return;
995     }
996
997     buffer_offset += extent_size;
998     *write_length += extent_size;
999   }
1000 }
1001
1002 std::string image_id(librbd::Image& image) {
1003   std::string id;
1004   int r = image.get_id(&id);
1005   if (r < 0) {
1006     return std::string();
1007   }
1008   return id;
1009 }
1010
1011 std::string mirror_image_state(librbd::mirror_image_state_t state) {
1012   switch (state) {
1013     case RBD_MIRROR_IMAGE_DISABLING:
1014       return "disabling";
1015     case RBD_MIRROR_IMAGE_ENABLED:
1016       return "enabled";
1017     case RBD_MIRROR_IMAGE_DISABLED:
1018       return "disabled";
1019     default:
1020       return "unknown";
1021   }
1022 }
1023
1024 std::string mirror_image_status_state(librbd::mirror_image_status_state_t state) {
1025   switch (state) {
1026   case MIRROR_IMAGE_STATUS_STATE_UNKNOWN:
1027     return "unknown";
1028   case MIRROR_IMAGE_STATUS_STATE_ERROR:
1029     return "error";
1030   case MIRROR_IMAGE_STATUS_STATE_SYNCING:
1031     return "syncing";
1032   case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY:
1033     return "starting_replay";
1034   case MIRROR_IMAGE_STATUS_STATE_REPLAYING:
1035     return "replaying";
1036   case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY:
1037     return "stopping_replay";
1038   case MIRROR_IMAGE_STATUS_STATE_STOPPED:
1039     return "stopped";
1040   default:
1041     return "unknown (" + stringify(static_cast<uint32_t>(state)) + ")";
1042   }
1043 }
1044
1045 std::string mirror_image_status_state(librbd::mirror_image_status_t status) {
1046   return (status.up ? "up+" : "down+") +
1047     mirror_image_status_state(status.state);
1048 }
1049
1050 std::string timestr(time_t t) {
1051   struct tm tm;
1052
1053   localtime_r(&t, &tm);
1054
1055   char buf[32];
1056   strftime(buf, sizeof(buf), "%F %T", &tm);
1057
1058   return buf;
1059 }
1060
1061 uint64_t get_rbd_default_features(CephContext* cct) {
1062   auto features = cct->_conf->get_val<std::string>("rbd_default_features");
1063   return boost::lexical_cast<uint64_t>(features);
1064 }
1065
1066 bool check_if_image_spec_present(const po::variables_map &vm,
1067                                  at::ArgumentModifier mod,
1068                                  size_t spec_arg_index) {
1069   std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
1070     at::DEST_IMAGE_NAME : at::IMAGE_NAME);
1071
1072   if (vm.count(image_key)) {
1073     return true;
1074   }
1075
1076   std::string spec = get_positional_argument(vm, spec_arg_index);
1077   if (!spec.empty()) {
1078     return true;
1079   }
1080
1081   return false;
1082 }
1083
1084 } // namespace utils
1085 } // namespace rbd