]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/Utils.cc
update sources to v12.2.1
[ceph.git] / ceph / src / tools / rbd / Utils.cc
CommitLineData
7c673cae
FG
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
20namespace rbd {
21namespace utils {
22
23namespace at = argument_types;
24namespace po = boost::program_options;
25
26int 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
39void ProgressContext::finish() {
40 if (progress) {
41 cerr << "\r" << operation << ": 100% complete...done." << std::endl;
42 }
43}
44
45void ProgressContext::fail() {
46 if (progress) {
47 cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
48 << std::endl;
49 }
50}
51
52void 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
61int 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
84int extract_spec(const std::string &spec, std::string *pool_name,
85 std::string *image_name, std::string *snap_name,
86 SpecValidation spec_validation) {
181888fb 87 if (!g_ceph_context->_conf->get_val<bool>("rbd_validate_names")) {
7c673cae
FG
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
129int 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
151int 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;
7c673cae
FG
170}
171
172std::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
31f18b77 186std::string get_default_pool_name() {
181888fb 187 return g_ceph_context->_conf->get_val<std::string>("rbd_default_pool");
31f18b77
FG
188}
189
190std::string get_pool_name(const po::variables_map &vm, size_t *arg_index) {
7c673cae
FG
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()) {
31f18b77 202 pool_name = get_default_pool_name();
7c673cae
FG
203 }
204 return pool_name;
205}
206
207int 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()) {
31f18b77 242 *group_pool_name = get_default_pool_name();
7c673cae
FG
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
253int 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()) {
31f18b77 291 *image_pool_name = get_default_pool_name();
7c673cae
FG
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
302int 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
31f18b77
FG
325 if (pool_name != nullptr && pool_name->empty()) {
326 *pool_name = get_default_pool_name();
7c673cae
FG
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
337int 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
31f18b77
FG
366 if (pool_name != nullptr && pool_name->empty()) {
367 *pool_name = get_default_pool_name();
7c673cae
FG
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
381int 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()) {
31f18b77 431 *pool_name = get_default_pool_name();
7c673cae
FG
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
31f18b77 442 //Validate pool name while creating/renaming/copying/cloning/importing/etc
7c673cae 443 if (spec_validation == SPEC_VALIDATION_FULL) {
31f18b77 444 boost::regex pattern("^[^@/]+?$");
7c673cae 445 if (!boost::regex_match (*pool_name, pattern)) {
31f18b77 446 std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl;
7c673cae
FG
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
461int 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()) {
31f18b77 481 *pool_name = get_default_pool_name();
7c673cae
FG
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
494int 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()) {
31f18b77 554 *pool_name = get_default_pool_name();
7c673cae
FG
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
595int 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)) {
31f18b77 626 std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl;
7c673cae
FG
627 return -EINVAL;
628 }
629 }
630 return 0;
631}
632
633int 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
764int 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
799int 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
810int 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
825int 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;
181888fb
FG
834 } else if (*formatter != nullptr && !pretty) {
835 formatter->get()->enable_line_break();
7c673cae 836 }
181888fb
FG
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;
7c673cae
FG
841 }
842 return 0;
843}
844
845void 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
851int 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
874int 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) {
31f18b77
FG
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 }
7c673cae
FG
887 return r;
888 }
889 return 0;
890}
891
892int 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
910int 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
928int 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
957int 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
967void 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
1002std::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
1011std::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
1024std::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
1045std::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
1050std::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
1061uint64_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
1066bool 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