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