1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
19 #include <boost/algorithm/string.hpp>
20 #include <boost/lexical_cast.hpp>
25 namespace at
= argument_types
;
26 namespace po
= boost::program_options
;
30 static std::string
mgr_command_args_to_str(
31 const std::map
<std::string
, std::string
> &args
) {
34 std::string delimiter
;
35 for (auto &it
: args
) {
36 out
+= delimiter
+ "\"" + it
.first
+ "\": \"" +
37 stringify(json_stream_escaper(it
.second
)) + "\"";
44 } // anonymous namespace
46 int ProgressContext::update_progress(uint64_t offset
, uint64_t total
) {
48 int pc
= get_percentage(offset
, total
);
50 std::cerr
<< "\r" << operation
<< ": "
51 << pc
<< "% complete..." << std::flush
;
58 void ProgressContext::finish() {
60 std::cerr
<< "\r" << operation
<< ": 100% complete...done." << std::endl
;
64 void ProgressContext::fail() {
66 std::cerr
<< "\r" << operation
<< ": " << last_pc
<< "% complete...failed."
71 int get_percentage(uint64_t part
, uint64_t whole
) {
72 return whole
? (100 * part
/ whole
) : 0;
75 void aio_context_callback(librbd::completion_t completion
, void *arg
)
77 librbd::RBD::AioCompletion
*aio_completion
=
78 reinterpret_cast<librbd::RBD::AioCompletion
*>(completion
);
79 Context
*context
= reinterpret_cast<Context
*>(arg
);
80 context
->complete(aio_completion
->get_return_value());
81 aio_completion
->release();
84 int read_string(int fd
, unsigned max
, std::string
*out
) {
87 int r
= safe_read_exact(fd
, buf
, 4);
100 r
= safe_read_exact(fd
, sbuf
, len
);
103 out
->assign(sbuf
, len
);
107 int extract_spec(const std::string
&spec
, std::string
*pool_name
,
108 std::string
*namespace_name
, std::string
*name
,
109 std::string
*snap_name
, SpecValidation spec_validation
) {
110 if (!g_ceph_context
->_conf
.get_val
<bool>("rbd_validate_names")) {
111 spec_validation
= SPEC_VALIDATION_NONE
;
115 switch (spec_validation
) {
116 case SPEC_VALIDATION_FULL
:
117 // disallow "/" and "@" in all names
118 pattern
= "^(?:([^/@]+)/(?:([^/@]+)/)?)?([^/@]+)(?:@([^/@]+))?$";
120 case SPEC_VALIDATION_SNAP
:
121 // disallow "/" and "@" in snap name
122 pattern
= "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$";
124 case SPEC_VALIDATION_NONE
:
125 // relaxed pattern assumes pool is before first "/",
126 // namespace is before second "/", and snap name is after first "@"
127 pattern
= "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@(.+))?$";
135 if (!std::regex_match(spec
, match
, pattern
)) {
136 std::cerr
<< "rbd: invalid spec '" << spec
<< "'" << std::endl
;
140 if (match
[1].matched
) {
141 if (pool_name
!= nullptr) {
142 *pool_name
= match
[1];
144 std::cerr
<< "rbd: pool name specified for a command that doesn't use it"
150 if (match
[2].matched
) {
151 if (namespace_name
!= nullptr) {
152 *namespace_name
= match
[2];
154 std::cerr
<< "rbd: namespace name specified for a command that doesn't "
155 << "use it" << std::endl
;
160 if (name
!= nullptr) {
164 if (match
[4].matched
) {
165 if (snap_name
!= nullptr) {
166 *snap_name
= match
[4];
168 std::cerr
<< "rbd: snapshot name specified for a command that doesn't "
169 << "use it" << std::endl
;
176 std::string
get_positional_argument(const po::variables_map
&vm
, size_t index
) {
177 if (vm
.count(at::POSITIONAL_ARGUMENTS
) == 0) {
181 const std::vector
<std::string
> &args
=
182 boost::any_cast
<std::vector
<std::string
> >(
183 vm
[at::POSITIONAL_ARGUMENTS
].value());
184 if (index
< args
.size()) {
190 void normalize_pool_name(std::string
* pool_name
) {
191 if (pool_name
->empty()) {
192 *pool_name
= get_default_pool_name();
196 std::string
get_default_pool_name() {
197 return g_ceph_context
->_conf
.get_val
<std::string
>("rbd_default_pool");
200 int get_pool_and_namespace_names(
201 const boost::program_options::variables_map
&vm
, bool validate_pool_name
,
202 std::string
* pool_name
, std::string
* namespace_name
, size_t *arg_index
) {
203 if (namespace_name
!= nullptr && vm
.count(at::NAMESPACE_NAME
)) {
204 *namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
207 if (vm
.count(at::POOL_NAME
)) {
208 *pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
210 *pool_name
= get_positional_argument(vm
, *arg_index
);
211 if (!pool_name
->empty()) {
212 if (namespace_name
!= nullptr) {
213 auto slash_pos
= pool_name
->find_last_of('/');
214 if (slash_pos
!= std::string::npos
) {
215 *namespace_name
= pool_name
->substr(slash_pos
+ 1);
217 *pool_name
= pool_name
->substr(0, slash_pos
);
223 if (!g_ceph_context
->_conf
.get_val
<bool>("rbd_validate_names")) {
224 validate_pool_name
= false;
227 if (validate_pool_name
&&
228 pool_name
->find_first_of("/@") != std::string::npos
) {
229 std::cerr
<< "rbd: invalid pool '" << *pool_name
<< "'" << std::endl
;
231 } else if (namespace_name
!= nullptr &&
232 namespace_name
->find_first_of("/@") != std::string::npos
) {
233 std::cerr
<< "rbd: invalid namespace '" << *namespace_name
<< "'"
241 int get_pool_image_id(const po::variables_map
&vm
,
242 size_t *spec_arg_index
,
243 std::string
*pool_name
,
244 std::string
*namespace_name
,
245 std::string
*image_id
) {
247 if (vm
.count(at::POOL_NAME
) && pool_name
!= nullptr) {
248 *pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
250 if (vm
.count(at::NAMESPACE_NAME
) && namespace_name
!= nullptr) {
251 *namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
253 if (vm
.count(at::IMAGE_ID
) && image_id
!= nullptr) {
254 *image_id
= vm
[at::IMAGE_ID
].as
<std::string
>();
258 if (image_id
!= nullptr && spec_arg_index
!= nullptr && image_id
->empty()) {
259 std::string spec
= get_positional_argument(vm
, (*spec_arg_index
)++);
261 r
= extract_spec(spec
, pool_name
, namespace_name
, image_id
, nullptr,
262 SPEC_VALIDATION_FULL
);
269 if (image_id
!= nullptr && image_id
->empty()) {
270 std::cerr
<< "rbd: image id was not specified" << std::endl
;
277 int get_image_or_snap_spec(const po::variables_map
&vm
, std::string
*spec
) {
278 size_t arg_index
= 0;
279 std::string pool_name
;
280 std::string nspace_name
;
281 std::string image_name
;
282 std::string snap_name
;
283 int r
= get_pool_image_snapshot_names(
284 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &nspace_name
,
285 &image_name
, &snap_name
, true, SNAPSHOT_PRESENCE_PERMITTED
,
286 SPEC_VALIDATION_NONE
);
291 if (pool_name
.empty()) {
292 // connect to the cluster to get the default pool
293 librados::Rados rados
;
294 r
= init_rados(&rados
);
299 normalize_pool_name(&pool_name
);
302 spec
->append(pool_name
);
304 if (!nspace_name
.empty()) {
305 spec
->append(nspace_name
);
308 spec
->append(image_name
);
309 if (!snap_name
.empty()) {
311 spec
->append(snap_name
);
317 void append_options_as_args(const std::vector
<std::string
> &options
,
318 std::vector
<std::string
> *args
) {
319 for (auto &opts
: options
) {
320 std::vector
<std::string
> args_
;
321 boost::split(args_
, opts
, boost::is_any_of(","));
322 for (auto &o
: args_
) {
323 args
->push_back("--" + o
);
328 int get_pool_image_snapshot_names(const po::variables_map
&vm
,
329 at::ArgumentModifier mod
,
330 size_t *spec_arg_index
,
331 std::string
*pool_name
,
332 std::string
*namespace_name
,
333 std::string
*image_name
,
334 std::string
*snap_name
,
335 bool image_name_required
,
336 SnapshotPresence snapshot_presence
,
337 SpecValidation spec_validation
) {
338 std::string pool_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
339 at::DEST_POOL_NAME
: at::POOL_NAME
);
340 std::string image_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
341 at::DEST_IMAGE_NAME
: at::IMAGE_NAME
);
342 return get_pool_generic_snapshot_names(vm
, mod
, spec_arg_index
, pool_key
,
343 pool_name
, namespace_name
, image_key
,
344 "image", image_name
, snap_name
,
345 image_name_required
, snapshot_presence
,
349 int get_pool_generic_snapshot_names(const po::variables_map
&vm
,
350 at::ArgumentModifier mod
,
351 size_t *spec_arg_index
,
352 const std::string
& pool_key
,
353 std::string
*pool_name
,
354 std::string
*namespace_name
,
355 const std::string
& generic_key
,
356 const std::string
& generic_key_desc
,
357 std::string
*generic_name
,
358 std::string
*snap_name
,
359 bool generic_name_required
,
360 SnapshotPresence snapshot_presence
,
361 SpecValidation spec_validation
) {
362 std::string namespace_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
363 at::DEST_NAMESPACE_NAME
: at::NAMESPACE_NAME
);
364 std::string snap_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
365 at::DEST_SNAPSHOT_NAME
: at::SNAPSHOT_NAME
);
367 if (vm
.count(pool_key
) && pool_name
!= nullptr) {
368 *pool_name
= vm
[pool_key
].as
<std::string
>();
370 if (vm
.count(namespace_key
) && namespace_name
!= nullptr) {
371 *namespace_name
= vm
[namespace_key
].as
<std::string
>();
373 if (vm
.count(generic_key
) && generic_name
!= nullptr) {
374 *generic_name
= vm
[generic_key
].as
<std::string
>();
376 if (vm
.count(snap_key
) && snap_name
!= nullptr) {
377 *snap_name
= vm
[snap_key
].as
<std::string
>();
381 if ((generic_key
== at::IMAGE_NAME
|| generic_key
== at::DEST_IMAGE_NAME
) &&
382 generic_name
!= nullptr && !generic_name
->empty()) {
383 // despite the separate pool and snapshot name options,
384 // we can also specify them via the image option
385 std::string
image_name_copy(*generic_name
);
386 r
= extract_spec(image_name_copy
, pool_name
, namespace_name
, generic_name
,
387 snap_name
, spec_validation
);
393 if (generic_name
!= nullptr && spec_arg_index
!= nullptr &&
394 generic_name
->empty()) {
395 std::string spec
= get_positional_argument(vm
, (*spec_arg_index
)++);
397 r
= extract_spec(spec
, pool_name
, namespace_name
, generic_name
, snap_name
,
405 if (generic_name
!= nullptr && generic_name_required
&&
406 generic_name
->empty()) {
407 std::string prefix
= at::get_description_prefix(mod
);
409 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
410 << generic_key_desc
<< " name was not specified" << std::endl
;
414 std::regex
pattern("^[^@/]*?$");
415 if (spec_validation
== SPEC_VALIDATION_FULL
) {
416 // validate pool name while creating/renaming/copying/cloning/importing/etc
417 if ((pool_name
!= nullptr) && !std::regex_match (*pool_name
, pattern
)) {
418 std::cerr
<< "rbd: invalid pool name '" << *pool_name
<< "'" << std::endl
;
423 if (namespace_name
!= nullptr && !namespace_name
->empty() &&
424 !std::regex_match (*namespace_name
, pattern
)) {
425 std::cerr
<< "rbd: invalid namespace name '" << *namespace_name
<< "'"
430 if (snap_name
!= nullptr) {
431 r
= validate_snapshot_name(mod
, *snap_name
, snapshot_presence
,
440 int validate_snapshot_name(at::ArgumentModifier mod
,
441 const std::string
&snap_name
,
442 SnapshotPresence snapshot_presence
,
443 SpecValidation spec_validation
) {
444 std::string prefix
= at::get_description_prefix(mod
);
445 switch (snapshot_presence
) {
446 case SNAPSHOT_PRESENCE_PERMITTED
:
448 case SNAPSHOT_PRESENCE_NONE
:
449 if (!snap_name
.empty()) {
451 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
452 << "snapshot name specified for a command that doesn't use it"
457 case SNAPSHOT_PRESENCE_REQUIRED
:
458 if (snap_name
.empty()) {
460 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
461 << "snapshot name was not specified" << std::endl
;
467 if (spec_validation
== SPEC_VALIDATION_SNAP
) {
468 // disallow "/" and "@" in snap name
469 std::regex
pattern("^[^@/]*?$");
470 if (!std::regex_match (snap_name
, pattern
)) {
471 std::cerr
<< "rbd: invalid snap name '" << snap_name
<< "'" << std::endl
;
478 int get_image_options(const boost::program_options::variables_map
&vm
,
479 bool get_format
, librbd::ImageOptions
*opts
) {
480 uint64_t order
= 0, stripe_unit
= 0, stripe_count
= 0, object_size
= 0;
481 uint64_t features
= 0, features_clear
= 0;
482 std::string data_pool
;
483 bool order_specified
= true;
484 bool features_specified
= false;
485 bool features_clear_specified
= false;
486 bool stripe_specified
= false;
488 if (vm
.count(at::IMAGE_ORDER
)) {
489 order
= vm
[at::IMAGE_ORDER
].as
<uint64_t>();
490 } else if (vm
.count(at::IMAGE_OBJECT_SIZE
)) {
491 object_size
= vm
[at::IMAGE_OBJECT_SIZE
].as
<uint64_t>();
492 order
= std::round(std::log2(object_size
));
494 order_specified
= false;
497 if (vm
.count(at::IMAGE_FEATURES
)) {
498 features
= vm
[at::IMAGE_FEATURES
].as
<uint64_t>();
499 features_specified
= true;
502 if (vm
.count(at::IMAGE_STRIPE_UNIT
)) {
503 stripe_unit
= vm
[at::IMAGE_STRIPE_UNIT
].as
<uint64_t>();
504 stripe_specified
= true;
507 if (vm
.count(at::IMAGE_STRIPE_COUNT
)) {
508 stripe_count
= vm
[at::IMAGE_STRIPE_COUNT
].as
<uint64_t>();
509 stripe_specified
= true;
512 if (vm
.count(at::IMAGE_SHARED
) && vm
[at::IMAGE_SHARED
].as
<bool>()) {
513 if (features_specified
) {
514 features
&= ~RBD_FEATURES_SINGLE_CLIENT
;
516 features_clear
|= RBD_FEATURES_SINGLE_CLIENT
;
517 features_clear_specified
= true;
521 if (vm
.count(at::IMAGE_DATA_POOL
)) {
522 data_pool
= vm
[at::IMAGE_DATA_POOL
].as
<std::string
>();
527 bool format_specified
= false;
528 if (vm
.count(at::IMAGE_NEW_FORMAT
)) {
530 format_specified
= true;
531 } else if (vm
.count(at::IMAGE_FORMAT
)) {
532 format
= vm
[at::IMAGE_FORMAT
].as
<uint32_t>();
533 format_specified
= true;
536 std::cerr
<< "rbd: image format 1 is deprecated" << std::endl
;
539 if (features_specified
&& features
!= 0) {
540 if (format_specified
&& format
== 1) {
541 std::cerr
<< "rbd: features not allowed with format 1; "
542 << "use --image-format 2" << std::endl
;
546 format_specified
= true;
550 if ((stripe_unit
|| stripe_count
) &&
551 (stripe_unit
!= (1ull << order
) && stripe_count
!= 1)) {
552 if (format_specified
&& format
== 1) {
553 std::cerr
<< "rbd: non-default striping not allowed with format 1; "
554 << "use --image-format 2" << std::endl
;
558 format_specified
= true;
562 if (!data_pool
.empty()) {
563 if (format_specified
&& format
== 1) {
564 std::cerr
<< "rbd: data pool not allowed with format 1; "
565 << "use --image-format 2" << std::endl
;
569 format_specified
= true;
573 if (format_specified
) {
574 int r
= g_conf().set_val("rbd_default_format", stringify(format
));
576 opts
->set(RBD_IMAGE_OPTION_FORMAT
, format
);
581 opts
->set(RBD_IMAGE_OPTION_ORDER
, order
);
582 if (features_specified
)
583 opts
->set(RBD_IMAGE_OPTION_FEATURES
, features
);
584 if (features_clear_specified
) {
585 opts
->set(RBD_IMAGE_OPTION_FEATURES_CLEAR
, features_clear
);
587 if (stripe_specified
) {
588 opts
->set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
589 opts
->set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
591 if (!data_pool
.empty()) {
592 opts
->set(RBD_IMAGE_OPTION_DATA_POOL
, data_pool
);
594 int r
= get_journal_options(vm
, opts
);
599 r
= get_flatten_option(vm
, opts
);
604 if (vm
.count(at::IMAGE_MIRROR_IMAGE_MODE
)) {
605 opts
->set(RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE
,
606 vm
[at::IMAGE_MIRROR_IMAGE_MODE
].as
<librbd::mirror_image_mode_t
>());
612 int get_journal_options(const boost::program_options::variables_map
&vm
,
613 librbd::ImageOptions
*opts
) {
615 if (vm
.count(at::JOURNAL_OBJECT_SIZE
)) {
616 uint64_t size
= vm
[at::JOURNAL_OBJECT_SIZE
].as
<uint64_t>();
618 while ((1ULL << order
) < size
) {
621 opts
->set(RBD_IMAGE_OPTION_JOURNAL_ORDER
, order
);
623 int r
= g_conf().set_val("rbd_journal_order", stringify(order
));
626 if (vm
.count(at::JOURNAL_SPLAY_WIDTH
)) {
627 opts
->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH
,
628 vm
[at::JOURNAL_SPLAY_WIDTH
].as
<uint64_t>());
630 int r
= g_conf().set_val("rbd_journal_splay_width",
632 vm
[at::JOURNAL_SPLAY_WIDTH
].as
<uint64_t>()));
635 if (vm
.count(at::JOURNAL_POOL
)) {
636 opts
->set(RBD_IMAGE_OPTION_JOURNAL_POOL
,
637 vm
[at::JOURNAL_POOL
].as
<std::string
>());
639 int r
= g_conf().set_val("rbd_journal_pool",
640 vm
[at::JOURNAL_POOL
].as
<std::string
>());
647 int get_flatten_option(const boost::program_options::variables_map
&vm
,
648 librbd::ImageOptions
*opts
) {
649 if (vm
.count(at::IMAGE_FLATTEN
) && vm
[at::IMAGE_FLATTEN
].as
<bool>()) {
650 uint64_t flatten
= 1;
651 opts
->set(RBD_IMAGE_OPTION_FLATTEN
, flatten
);
656 int get_image_size(const boost::program_options::variables_map
&vm
,
658 if (vm
.count(at::IMAGE_SIZE
) == 0) {
659 std::cerr
<< "rbd: must specify --size <M/G/T>" << std::endl
;
663 *size
= vm
[at::IMAGE_SIZE
].as
<uint64_t>();
667 int get_path(const boost::program_options::variables_map
&vm
,
668 size_t *arg_index
, std::string
*path
) {
669 if (vm
.count(at::PATH
)) {
670 *path
= vm
[at::PATH
].as
<std::string
>();
672 *path
= get_positional_argument(vm
, *arg_index
);
673 if (!path
->empty()) {
679 std::cerr
<< "rbd: path was not specified" << std::endl
;
685 int get_formatter(const po::variables_map
&vm
,
686 at::Format::Formatter
*formatter
) {
687 if (vm
.count(at::FORMAT
)) {
688 bool pretty
= vm
[at::PRETTY_FORMAT
].as
<bool>();
689 *formatter
= vm
[at::FORMAT
].as
<at::Format
>().create_formatter(pretty
);
690 if (*formatter
== nullptr && pretty
) {
691 std::cerr
<< "rbd: --pretty-format only works when --format "
692 << "is json or xml" << std::endl
;
694 } else if (*formatter
!= nullptr && !pretty
) {
695 formatter
->get()->enable_line_break();
697 } else if (vm
[at::PRETTY_FORMAT
].as
<bool>()) {
698 std::cerr
<< "rbd: --pretty-format only works when --format "
699 << "is json or xml" << std::endl
;
705 int get_snap_create_flags(const po::variables_map
&vm
, uint32_t *flags
) {
706 if (vm
[at::SKIP_QUIESCE
].as
<bool>() &&
707 vm
[at::IGNORE_QUIESCE_ERROR
].as
<bool>()) {
708 std::cerr
<< "rbd: " << at::IGNORE_QUIESCE_ERROR
709 << " cannot be used together with " << at::SKIP_QUIESCE
715 if (vm
[at::SKIP_QUIESCE
].as
<bool>()) {
716 *flags
|= RBD_SNAP_CREATE_SKIP_QUIESCE
;
717 } else if (vm
[at::IGNORE_QUIESCE_ERROR
].as
<bool>()) {
718 *flags
|= RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
;
723 int get_encryption_options(const boost::program_options::variables_map
&vm
,
724 EncryptionOptions
* result
) {
725 std::vector
<std::string
> passphrase_files
;
726 if (vm
.count(at::ENCRYPTION_PASSPHRASE_FILE
)) {
728 vm
[at::ENCRYPTION_PASSPHRASE_FILE
].as
<std::vector
<std::string
>>();
731 std::vector
<at::EncryptionFormat
> formats
;
732 if (vm
.count(at::ENCRYPTION_FORMAT
)) {
733 formats
= vm
[at::ENCRYPTION_FORMAT
].as
<decltype(formats
)>();
734 } else if (vm
.count(at::ENCRYPTION_PASSPHRASE_FILE
)) {
735 formats
.resize(passphrase_files
.size(),
736 at::EncryptionFormat
{RBD_ENCRYPTION_FORMAT_LUKS
});
739 if (formats
.size() != passphrase_files
.size()) {
740 std::cerr
<< "rbd: encryption formats count does not match "
741 << "passphrase files count" << std::endl
;
745 result
->specs
.clear();
746 result
->specs
.reserve(formats
.size());
747 for (size_t i
= 0; i
< formats
.size(); ++i
) {
748 std::ifstream
file(passphrase_files
[i
], std::ios::in
| std::ios::binary
);
750 std::cerr
<< "rbd: unable to open passphrase file '"
751 << passphrase_files
[i
] << "': " << cpp_strerror(errno
)
755 std::string
passphrase((std::istreambuf_iterator
<char>(file
)),
756 std::istreambuf_iterator
<char>());
759 switch (formats
[i
].format
) {
760 case RBD_ENCRYPTION_FORMAT_LUKS
: {
761 auto opts
= new librbd::encryption_luks_format_options_t
{
762 std::move(passphrase
)};
763 result
->specs
.push_back(
764 {RBD_ENCRYPTION_FORMAT_LUKS
, opts
, sizeof(*opts
)});
767 case RBD_ENCRYPTION_FORMAT_LUKS1
: {
768 auto opts
= new librbd::encryption_luks1_format_options_t
{
769 .passphrase
= std::move(passphrase
)};
770 result
->specs
.push_back(
771 {RBD_ENCRYPTION_FORMAT_LUKS1
, opts
, sizeof(*opts
)});
774 case RBD_ENCRYPTION_FORMAT_LUKS2
: {
775 auto opts
= new librbd::encryption_luks2_format_options_t
{
776 .passphrase
= std::move(passphrase
)};
777 result
->specs
.push_back(
778 {RBD_ENCRYPTION_FORMAT_LUKS2
, opts
, sizeof(*opts
)});
789 void init_context() {
790 g_conf().set_val_or_die("rbd_cache_writethrough_until_flush", "false");
791 g_conf().apply_changes(nullptr);
794 int init_rados(librados::Rados
*rados
) {
797 int r
= rados
->init_with_context(g_ceph_context
);
799 std::cerr
<< "rbd: couldn't initialize rados!" << std::endl
;
803 r
= rados
->connect();
805 std::cerr
<< "rbd: couldn't connect to the cluster!" << std::endl
;
812 int init(const std::string
&pool_name
, const std::string
& namespace_name
,
813 librados::Rados
*rados
, librados::IoCtx
*io_ctx
) {
816 int r
= init_rados(rados
);
821 r
= init_io_ctx(*rados
, pool_name
, namespace_name
, io_ctx
);
828 int init_io_ctx(librados::Rados
&rados
, std::string pool_name
,
829 const std::string
& namespace_name
, librados::IoCtx
*io_ctx
) {
830 normalize_pool_name(&pool_name
);
832 int r
= rados
.ioctx_create(pool_name
.c_str(), *io_ctx
);
834 if (r
== -ENOENT
&& pool_name
== get_default_pool_name()) {
835 std::cerr
<< "rbd: error opening default pool "
836 << "'" << pool_name
<< "'" << std::endl
837 << "Ensure that the default pool has been created or specify "
838 << "an alternate pool name." << std::endl
;
840 std::cerr
<< "rbd: error opening pool '" << pool_name
<< "': "
841 << cpp_strerror(r
) << std::endl
;
846 return set_namespace(namespace_name
, io_ctx
);
849 int set_namespace(const std::string
& namespace_name
, librados::IoCtx
*io_ctx
) {
850 if (!namespace_name
.empty()) {
853 int r
= rbd
.namespace_exists(*io_ctx
, namespace_name
.c_str(), &exists
);
855 std::cerr
<< "rbd: error asserting namespace: "
856 << cpp_strerror(r
) << std::endl
;
860 std::cerr
<< "rbd: namespace '" << namespace_name
<< "' does not exist."
865 io_ctx
->set_namespace(namespace_name
);
869 void disable_cache() {
870 g_conf().set_val_or_die("rbd_cache", "false");
873 int open_image(librados::IoCtx
&io_ctx
, const std::string
&image_name
,
874 bool read_only
, librbd::Image
*image
) {
878 r
= rbd
.open_read_only(io_ctx
, *image
, image_name
.c_str(), NULL
);
880 r
= rbd
.open(io_ctx
, *image
, image_name
.c_str());
884 std::cerr
<< "rbd: error opening image " << image_name
<< ": "
885 << cpp_strerror(r
) << std::endl
;
891 int open_image_by_id(librados::IoCtx
&io_ctx
, const std::string
&image_id
,
892 bool read_only
, librbd::Image
*image
) {
896 r
= rbd
.open_by_id_read_only(io_ctx
, *image
, image_id
.c_str(), NULL
);
898 r
= rbd
.open_by_id(io_ctx
, *image
, image_id
.c_str());
902 std::cerr
<< "rbd: error opening image with id " << image_id
<< ": "
903 << cpp_strerror(r
) << std::endl
;
909 int init_and_open_image(const std::string
&pool_name
,
910 const std::string
&namespace_name
,
911 const std::string
&image_name
,
912 const std::string
&image_id
,
913 const std::string
&snap_name
, bool read_only
,
914 librados::Rados
*rados
, librados::IoCtx
*io_ctx
,
915 librbd::Image
*image
) {
916 int r
= init(pool_name
, namespace_name
, rados
, io_ctx
);
921 if (image_id
.empty()) {
922 r
= open_image(*io_ctx
, image_name
, read_only
, image
);
924 r
= open_image_by_id(*io_ctx
, image_id
, read_only
, image
);
930 if (!snap_name
.empty()) {
931 r
= snap_set(*image
, snap_name
);
940 int snap_set(librbd::Image
&image
, const std::string
&snap_name
) {
941 int r
= image
.snap_set(snap_name
.c_str());
943 std::cerr
<< "error setting snapshot context: " << cpp_strerror(r
)
950 void calc_sparse_extent(const bufferptr
&bp
,
952 size_t buffer_offset
,
953 uint64_t buffer_length
,
954 size_t *write_length
,
956 if (sparse_size
== 0) {
957 // sparse writes are disabled -- write the full extent
958 ceph_assert(buffer_offset
== 0);
959 *write_length
= buffer_length
;
965 size_t original_offset
= buffer_offset
;
966 while (buffer_offset
< buffer_length
) {
967 size_t extent_size
= std::min
<size_t>(
968 sparse_size
, buffer_length
- buffer_offset
);
970 bufferptr
extent(bp
, buffer_offset
, extent_size
);
972 bool extent_is_zero
= extent
.is_zero();
973 if (original_offset
== buffer_offset
) {
974 *zeroed
= extent_is_zero
;
975 } else if (*zeroed
!= extent_is_zero
) {
976 ceph_assert(*write_length
> 0);
980 buffer_offset
+= extent_size
;
981 *write_length
+= extent_size
;
985 std::string
image_id(librbd::Image
& image
) {
987 int r
= image
.get_id(&id
);
989 return std::string();
994 std::string
mirror_image_mode(librbd::mirror_image_mode_t mode
) {
996 case RBD_MIRROR_IMAGE_MODE_JOURNAL
:
998 case RBD_MIRROR_IMAGE_MODE_SNAPSHOT
:
1005 std::string
mirror_image_state(librbd::mirror_image_state_t state
) {
1007 case RBD_MIRROR_IMAGE_DISABLING
:
1009 case RBD_MIRROR_IMAGE_ENABLED
:
1011 case RBD_MIRROR_IMAGE_DISABLED
:
1018 std::string
mirror_image_status_state(
1019 librbd::mirror_image_status_state_t state
) {
1021 case MIRROR_IMAGE_STATUS_STATE_UNKNOWN
:
1023 case MIRROR_IMAGE_STATUS_STATE_ERROR
:
1025 case MIRROR_IMAGE_STATUS_STATE_SYNCING
:
1027 case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
:
1028 return "starting_replay";
1029 case MIRROR_IMAGE_STATUS_STATE_REPLAYING
:
1031 case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
:
1032 return "stopping_replay";
1033 case MIRROR_IMAGE_STATUS_STATE_STOPPED
:
1036 return "unknown (" + stringify(static_cast<uint32_t>(state
)) + ")";
1040 std::string
mirror_image_site_status_state(
1041 const librbd::mirror_image_site_status_t
& status
) {
1042 return (status
.up
? "up+" : "down+") +
1043 mirror_image_status_state(status
.state
);
1046 std::string
mirror_image_global_status_state(
1047 const librbd::mirror_image_global_status_t
& status
) {
1048 librbd::mirror_image_site_status_t local_status
;
1049 int r
= get_local_mirror_image_status(status
, &local_status
);
1051 return "down+unknown";
1054 return mirror_image_site_status_state(local_status
);
1057 int get_local_mirror_image_status(
1058 const librbd::mirror_image_global_status_t
& status
,
1059 librbd::mirror_image_site_status_t
* local_status
) {
1060 auto it
= std::find_if(status
.site_statuses
.begin(),
1061 status
.site_statuses
.end(),
1062 [](auto& site_status
) {
1063 return (site_status
.mirror_uuid
==
1064 RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID
);
1066 if (it
== status
.site_statuses
.end()) {
1070 *local_status
= *it
;
1074 std::string
timestr(time_t t
) {
1081 localtime_r(&t
, &tm
);
1084 strftime(buf
, sizeof(buf
), "%Y-%m-%d %H:%M:%S", &tm
);
1089 uint64_t get_rbd_default_features(CephContext
* cct
) {
1090 auto features
= cct
->_conf
.get_val
<std::string
>("rbd_default_features");
1091 return boost::lexical_cast
<uint64_t>(features
);
1094 bool is_not_user_snap_namespace(librbd::Image
* image
,
1095 const librbd::snap_info_t
&snap_info
)
1097 librbd::snap_namespace_type_t namespace_type
;
1098 int r
= image
->snap_get_namespace_type(snap_info
.id
, &namespace_type
);
1102 return namespace_type
!= RBD_SNAP_NAMESPACE_TYPE_USER
;
1105 void get_mirror_peer_sites(
1106 librados::IoCtx
& io_ctx
,
1107 std::vector
<librbd::mirror_peer_site_t
>* mirror_peers
) {
1108 librados::IoCtx default_io_ctx
;
1109 default_io_ctx
.dup(io_ctx
);
1110 default_io_ctx
.set_namespace("");
1112 mirror_peers
->clear();
1115 int r
= rbd
.mirror_peer_site_list(default_io_ctx
, mirror_peers
);
1116 if (r
< 0 && r
!= -ENOENT
) {
1117 std::cerr
<< "rbd: failed to list mirror peers" << std::endl
;
1121 void get_mirror_peer_mirror_uuids_to_names(
1122 const std::vector
<librbd::mirror_peer_site_t
>& mirror_peers
,
1123 std::map
<std::string
, std::string
>* mirror_uuids_to_name
) {
1124 mirror_uuids_to_name
->clear();
1125 for (auto& peer
: mirror_peers
) {
1126 if (!peer
.mirror_uuid
.empty() && !peer
.site_name
.empty()) {
1127 (*mirror_uuids_to_name
)[peer
.mirror_uuid
] = peer
.site_name
;
1132 void populate_unknown_mirror_image_site_statuses(
1133 const std::vector
<librbd::mirror_peer_site_t
>& mirror_peers
,
1134 librbd::mirror_image_global_status_t
* global_status
) {
1135 std::set
<std::string
> missing_mirror_uuids
;
1136 librbd::mirror_peer_direction_t mirror_peer_direction
=
1137 RBD_MIRROR_PEER_DIRECTION_RX_TX
;
1138 for (auto& peer
: mirror_peers
) {
1139 if (peer
.uuid
== mirror_peers
.begin()->uuid
) {
1140 mirror_peer_direction
= peer
.direction
;
1141 } else if (mirror_peer_direction
!= RBD_MIRROR_PEER_DIRECTION_RX_TX
&&
1142 mirror_peer_direction
!= peer
.direction
) {
1143 mirror_peer_direction
= RBD_MIRROR_PEER_DIRECTION_RX_TX
;
1146 if (!peer
.mirror_uuid
.empty() &&
1147 peer
.direction
!= RBD_MIRROR_PEER_DIRECTION_TX
) {
1148 missing_mirror_uuids
.insert(peer
.mirror_uuid
);
1152 if (mirror_peer_direction
!= RBD_MIRROR_PEER_DIRECTION_TX
) {
1153 missing_mirror_uuids
.insert(RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID
);
1156 std::vector
<librbd::mirror_image_site_status_t
> site_statuses
;
1157 site_statuses
.reserve(missing_mirror_uuids
.size());
1159 for (auto& site_status
: global_status
->site_statuses
) {
1160 if (missing_mirror_uuids
.count(site_status
.mirror_uuid
) > 0) {
1161 missing_mirror_uuids
.erase(site_status
.mirror_uuid
);
1162 site_statuses
.push_back(site_status
);
1166 for (auto& mirror_uuid
: missing_mirror_uuids
) {
1167 site_statuses
.push_back({mirror_uuid
, MIRROR_IMAGE_STATUS_STATE_UNKNOWN
,
1168 "status not found", 0, false});
1171 std::swap(global_status
->site_statuses
, site_statuses
);
1174 int mgr_command(librados::Rados
& rados
, const std::string
& cmd
,
1175 const std::map
<std::string
, std::string
> &args
,
1176 std::ostream
*out_os
, std::ostream
*err_os
) {
1177 std::string command
= R
"(
1179 "prefix
": ")" + cmd + R"(", )" + mgr_command_args_to_str(args
) + R
"(
1185 int r
= rados
.mgr_command(command
, in_bl
, &out_bl
, &outs
);
1187 (*err_os
) << "rbd: " << cmd
<< " failed: " << cpp_strerror(r
);
1188 if (!outs
.empty()) {
1189 (*err_os
) << ": " << outs
;
1191 (*err_os
) << std::endl
;
1195 if (out_bl
.length() != 0) {
1196 (*out_os
) << out_bl
.c_str();
1202 } // namespace utils