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