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