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