1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "common/Formatter.h"
5 #include "common/TextTable.h"
6 #include "common/ceph_context.h"
7 #include "common/ceph_json.h"
8 #include "common/escape.h"
9 #include "common/errno.h"
10 #include "common/options.h"
11 #include "global/global_context.h"
12 #include "include/stringify.h"
14 #include "tools/rbd/ArgumentTypes.h"
15 #include "tools/rbd/Shell.h"
16 #include "tools/rbd/Utils.h"
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/program_options.hpp>
23 #include "json_spirit/json_spirit.h"
29 namespace at
= argument_types
;
30 namespace po
= boost::program_options
;
34 const std::string METADATA_CONF_PREFIX
= "conf_";
36 void add_config_entity_option(
37 boost::program_options::options_description
*positional
) {
38 positional
->add_options()
39 ("config-entity", "config entity (global, client, client.<id>)");
42 void add_pool_option(boost::program_options::options_description
*positional
) {
43 positional
->add_options()
44 ("pool-name", "pool name");
47 void add_key_option(po::options_description
*positional
) {
48 positional
->add_options()
49 ("key", "config key");
52 int get_config_entity(const po::variables_map
&vm
, std::string
*config_entity
) {
53 *config_entity
= utils::get_positional_argument(vm
, 0);
55 if (*config_entity
!= "global" && *config_entity
!= "client" &&
56 !boost::starts_with(*config_entity
, ("client."))) {
57 std::cerr
<< "rbd: invalid config entity: " << *config_entity
58 << " (must be global, client or client.<id>)" << std::endl
;
65 int get_pool(const po::variables_map
&vm
, std::string
*pool_name
) {
66 *pool_name
= utils::get_positional_argument(vm
, 0);
67 if (pool_name
->empty()) {
68 std::cerr
<< "rbd: pool name was not specified" << std::endl
;
75 int get_key(const po::variables_map
&vm
, size_t *arg_index
,
77 *key
= utils::get_positional_argument(vm
, *arg_index
);
79 std::cerr
<< "rbd: config key was not specified" << std::endl
;
85 if (!boost::starts_with(*key
, "rbd_")) {
86 std::cerr
<< "rbd: not rbd option: " << *key
<< std::endl
;
91 int r
= g_ceph_context
->_conf
.get_val(key
->c_str(), &value
);
93 std::cerr
<< "rbd: invalid config key: " << *key
<< std::endl
;
100 std::ostream
& operator<<(std::ostream
& os
,
101 const librbd::config_source_t
& source
) {
103 case RBD_CONFIG_SOURCE_CONFIG
:
106 case RBD_CONFIG_SOURCE_POOL
:
109 case RBD_CONFIG_SOURCE_IMAGE
:
113 os
<< "unknown (" << static_cast<uint32_t>(source
) << ")";
119 int config_global_list(
120 librados::Rados
&rados
, const std::string
&config_entity
,
121 std::map
<std::string
, std::pair
<std::string
, std::string
>> *options
) {
122 bool client_id_config_entity
=
123 boost::starts_with(config_entity
, ("client."));
126 "\"prefix\": \"config dump\", "
127 "\"format\": \"json\" "
132 int r
= rados
.mon_command(cmd
, in_bl
, &out_bl
, &ss
);
134 std::cerr
<< "rbd: error reading config: " << ss
<< std::endl
;
138 json_spirit::mValue json_root
;
139 if (!json_spirit::read(out_bl
.to_str(), json_root
)) {
140 std::cerr
<< "rbd: error parsing config dump" << std::endl
;
145 auto &json_array
= json_root
.get_array();
146 for (auto& e
: json_array
) {
147 auto &json_obj
= e
.get_obj();
152 for (auto &pairs
: json_obj
) {
153 if (pairs
.first
== "section") {
154 section
= pairs
.second
.get_str();
155 } else if (pairs
.first
== "name") {
156 name
= pairs
.second
.get_str();
157 } else if (pairs
.first
== "value") {
158 value
= pairs
.second
.get_str();
162 if (!boost::starts_with(name
, "rbd_")) {
165 if (section
!= "global" && section
!= "client" &&
166 (!client_id_config_entity
|| section
!= config_entity
)) {
169 if (config_entity
== "global" && section
!= "global") {
172 auto it
= options
->find(name
);
173 if (it
== options
->end()) {
174 (*options
)[name
] = {value
, section
};
177 if (section
== "client") {
178 if (it
->second
.second
== "global") {
179 it
->second
= {value
, section
};
181 } else if (client_id_config_entity
) {
182 it
->second
= {value
, section
};
185 } catch (std::runtime_error
&e
) {
186 std::cerr
<< "rbd: error parsing config dump: " << e
.what() << std::endl
;
193 } // anonymous namespace
195 void get_global_get_arguments(po::options_description
*positional
,
196 po::options_description
*options
) {
197 add_config_entity_option(positional
);
198 add_key_option(positional
);
201 int execute_global_get(const po::variables_map
&vm
,
202 const std::vector
<std::string
> &ceph_global_init_args
) {
203 std::string config_entity
;
204 int r
= get_config_entity(vm
, &config_entity
);
210 size_t arg_index
= 1;
211 r
= get_key(vm
, &arg_index
, &key
);
216 librados::Rados rados
;
217 r
= utils::init_rados(&rados
);
222 std::map
<std::string
, std::pair
<std::string
, std::string
>> options
;
223 r
= config_global_list(rados
, config_entity
, &options
);
228 auto it
= options
.find(key
);
230 if (it
== options
.end() || it
->second
.second
!= config_entity
) {
231 std::cerr
<< "rbd: " << key
<< " is not set" << std::endl
;
235 std::cout
<< it
->second
.first
<< std::endl
;
239 void get_global_set_arguments(po::options_description
*positional
,
240 po::options_description
*options
) {
241 add_config_entity_option(positional
);
242 add_key_option(positional
);
243 positional
->add_options()
244 ("value", "config value");
247 int execute_global_set(const po::variables_map
&vm
,
248 const std::vector
<std::string
> &ceph_global_init_args
) {
249 std::string config_entity
;
250 int r
= get_config_entity(vm
, &config_entity
);
256 size_t arg_index
= 1;
257 r
= get_key(vm
, &arg_index
, &key
);
262 librados::Rados rados
;
263 r
= utils::init_rados(&rados
);
268 std::string value
= utils::get_positional_argument(vm
, 2);
271 "\"prefix\": \"config set\", "
272 "\"who\": \"" + stringify(json_stream_escaper(config_entity
)) + "\", "
273 "\"name\": \"" + key
+ "\", "
274 "\"value\": \"" + stringify(json_stream_escaper(value
)) + "\""
278 r
= rados
.mon_command(cmd
, in_bl
, nullptr, &ss
);
280 std::cerr
<< "rbd: error setting " << key
<< ": " << ss
<< std::endl
;
287 void get_global_remove_arguments(po::options_description
*positional
,
288 po::options_description
*options
) {
289 add_config_entity_option(positional
);
290 add_key_option(positional
);
293 int execute_global_remove(
294 const po::variables_map
&vm
,
295 const std::vector
<std::string
> &ceph_global_init_args
) {
296 std::string config_entity
;
297 int r
= get_config_entity(vm
, &config_entity
);
303 size_t arg_index
= 1;
304 r
= get_key(vm
, &arg_index
, &key
);
309 librados::Rados rados
;
310 r
= utils::init_rados(&rados
);
317 "\"prefix\": \"config rm\", "
318 "\"who\": \"" + stringify(json_stream_escaper(config_entity
)) + "\", "
319 "\"name\": \"" + key
+ "\""
323 r
= rados
.mon_command(cmd
, in_bl
, nullptr, &ss
);
325 std::cerr
<< "rbd: error removing " << key
<< ": " << ss
<< std::endl
;
332 void get_global_list_arguments(po::options_description
*positional
,
333 po::options_description
*options
) {
334 add_config_entity_option(positional
);
335 at::add_format_options(options
);
338 int execute_global_list(const po::variables_map
&vm
,
339 const std::vector
<std::string
> &ceph_global_init_args
) {
340 std::string config_entity
;
341 int r
= get_config_entity(vm
, &config_entity
);
346 at::Format::Formatter f
;
347 r
= utils::get_formatter(vm
, &f
);
352 librados::Rados rados
;
353 r
= utils::init_rados(&rados
);
358 std::map
<std::string
, std::pair
<std::string
, std::string
>> options
;
359 r
= config_global_list(rados
, config_entity
, &options
);
364 if (options
.empty() && !f
) {
371 f
->open_array_section("config");
373 tbl
.define_column("Name", TextTable::LEFT
, TextTable::LEFT
);
374 tbl
.define_column("Value", TextTable::LEFT
, TextTable::LEFT
);
375 tbl
.define_column("Section", TextTable::LEFT
, TextTable::LEFT
);
378 for (const auto &it
: options
) {
380 f
->open_object_section("option");
381 f
->dump_string("name", it
.first
);
382 f
->dump_string("value", it
.second
.first
);
383 f
->dump_string("section", it
.second
.second
);
386 tbl
<< it
.first
<< it
.second
.first
<< it
.second
.second
387 << TextTable::endrow
;
401 void get_pool_get_arguments(po::options_description
*positional
,
402 po::options_description
*options
) {
403 add_pool_option(positional
);
404 add_key_option(positional
);
407 int execute_pool_get(const po::variables_map
&vm
,
408 const std::vector
<std::string
> &ceph_global_init_args
) {
409 std::string pool_name
;
410 int r
= get_pool(vm
, &pool_name
);
416 size_t arg_index
= 1;
417 r
= get_key(vm
, &arg_index
, &key
);
422 librados::Rados rados
;
423 librados::IoCtx io_ctx
;
424 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
432 r
= rbd
.pool_metadata_get(io_ctx
, METADATA_CONF_PREFIX
+ key
, &value
);
435 std::cerr
<< "rbd: " << key
<< " is not set" << std::endl
;
437 std::cerr
<< "rbd: failed to get " << key
<< ": " << cpp_strerror(r
)
443 std::cout
<< value
<< std::endl
;
447 void get_pool_set_arguments(po::options_description
*positional
,
448 po::options_description
*options
) {
449 add_pool_option(positional
);
450 add_key_option(positional
);
451 positional
->add_options()
452 ("value", "config value");
455 int execute_pool_set(const po::variables_map
&vm
,
456 const std::vector
<std::string
> &ceph_global_init_args
) {
457 std::string pool_name
;
458 int r
= get_pool(vm
, &pool_name
);
464 size_t arg_index
= 1;
465 r
= get_key(vm
, &arg_index
, &key
);
470 std::string value
= utils::get_positional_argument(vm
, 2);
472 librados::Rados rados
;
473 librados::IoCtx io_ctx
;
474 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
480 r
= rbd
.pool_metadata_set(io_ctx
, METADATA_CONF_PREFIX
+ key
, value
);
482 std::cerr
<< "rbd: failed to set " << key
<< ": " << cpp_strerror(r
)
490 void get_pool_remove_arguments(po::options_description
*positional
,
491 po::options_description
*options
) {
492 add_pool_option(positional
);
493 add_key_option(positional
);
496 int execute_pool_remove(const po::variables_map
&vm
,
497 const std::vector
<std::string
> &ceph_global_init_args
) {
498 std::string pool_name
;
499 int r
= get_pool(vm
, &pool_name
);
505 size_t arg_index
= 1;
506 r
= get_key(vm
, &arg_index
, &key
);
511 librados::Rados rados
;
512 librados::IoCtx io_ctx
;
513 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
519 r
= rbd
.pool_metadata_remove(io_ctx
, METADATA_CONF_PREFIX
+ key
);
521 std::cerr
<< "rbd: failed to remove " << key
<< ": " << cpp_strerror(r
)
529 void get_pool_list_arguments(po::options_description
*positional
,
530 po::options_description
*options
) {
531 add_pool_option(positional
);
532 at::add_format_options(options
);
535 int execute_pool_list(const po::variables_map
&vm
,
536 const std::vector
<std::string
> &ceph_global_init_args
) {
537 std::string pool_name
;
538 int r
= get_pool(vm
, &pool_name
);
543 at::Format::Formatter f
;
544 r
= utils::get_formatter(vm
, &f
);
549 librados::Rados rados
;
550 librados::IoCtx io_ctx
;
551 r
= utils::init(pool_name
, "", &rados
, &io_ctx
);
558 std::vector
<librbd::config_option_t
> options
;
560 r
= rbd
.config_list(io_ctx
, &options
);
562 std::cerr
<< "rbd: failed to list config: " << cpp_strerror(r
) << std::endl
;
567 f
->open_array_section("config");
569 tbl
.define_column("Name", TextTable::LEFT
, TextTable::LEFT
);
570 tbl
.define_column("Value", TextTable::LEFT
, TextTable::LEFT
);
571 tbl
.define_column("Source", TextTable::LEFT
, TextTable::LEFT
);
574 for (auto &option
: options
) {
576 f
->open_object_section("option");
577 f
->dump_string("name", option
.name
);
578 f
->dump_string("value", option
.value
);
579 f
->dump_stream("source") << option
.source
;
582 std::ostringstream source
;
583 source
<< option
.source
;
584 tbl
<< option
.name
<< option
.value
<< source
.str() << TextTable::endrow
;
598 void get_image_get_arguments(po::options_description
*positional
,
599 po::options_description
*options
) {
600 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
601 add_key_option(positional
);
604 int execute_image_get(const po::variables_map
&vm
,
605 const std::vector
<std::string
> &ceph_global_init_args
) {
606 size_t arg_index
= 0;
607 std::string pool_name
;
608 std::string namespace_name
;
609 std::string image_name
;
610 std::string snap_name
;
611 int r
= utils::get_pool_image_snapshot_names(
612 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
613 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
614 utils::SPEC_VALIDATION_NONE
);
620 r
= get_key(vm
, &arg_index
, &key
);
625 librados::Rados rados
;
626 librados::IoCtx io_ctx
;
628 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
629 false, &rados
, &io_ctx
, &image
);
636 r
= image
.metadata_get(METADATA_CONF_PREFIX
+ key
, &value
);
639 std::cerr
<< "rbd: " << key
<< " is not set" << std::endl
;
641 std::cerr
<< "rbd: failed to get " << key
<< ": " << cpp_strerror(r
)
647 std::cout
<< value
<< std::endl
;
651 void get_image_set_arguments(po::options_description
*positional
,
652 po::options_description
*options
) {
653 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
654 add_key_option(positional
);
655 positional
->add_options()
656 ("value", "config value");
659 int execute_image_set(const po::variables_map
&vm
,
660 const std::vector
<std::string
> &ceph_global_init_args
) {
661 size_t arg_index
= 0;
662 std::string pool_name
;
663 std::string namespace_name
;
664 std::string image_name
;
665 std::string snap_name
;
666 int r
= utils::get_pool_image_snapshot_names(
667 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
668 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
669 utils::SPEC_VALIDATION_NONE
);
675 r
= get_key(vm
, &arg_index
, &key
);
680 std::string value
= utils::get_positional_argument(vm
, arg_index
);
682 std::cerr
<< "rbd: image config value was not specified" << std::endl
;
686 librados::Rados rados
;
687 librados::IoCtx io_ctx
;
689 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
690 false, &rados
, &io_ctx
, &image
);
695 r
= image
.metadata_set(METADATA_CONF_PREFIX
+ key
, value
);
697 std::cerr
<< "rbd: failed to set " << key
<< ": " << cpp_strerror(r
)
705 void get_image_remove_arguments(po::options_description
*positional
,
706 po::options_description
*options
) {
707 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
708 add_key_option(positional
);
711 int execute_image_remove(
712 const po::variables_map
&vm
,
713 const std::vector
<std::string
> &ceph_global_init_args
) {
714 size_t arg_index
= 0;
715 std::string pool_name
;
716 std::string namespace_name
;
717 std::string image_name
;
718 std::string snap_name
;
719 int r
= utils::get_pool_image_snapshot_names(
720 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
721 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
722 utils::SPEC_VALIDATION_NONE
);
728 r
= get_key(vm
, &arg_index
, &key
);
733 librados::Rados rados
;
734 librados::IoCtx io_ctx
;
736 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
737 false, &rados
, &io_ctx
, &image
);
742 r
= image
.metadata_remove(METADATA_CONF_PREFIX
+ key
);
744 std::cerr
<< "rbd: failed to remove " << key
<< ": " << cpp_strerror(r
)
752 void get_image_list_arguments(po::options_description
*positional
,
753 po::options_description
*options
) {
754 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
755 at::add_format_options(options
);
758 int execute_image_list(const po::variables_map
&vm
,
759 const std::vector
<std::string
> &ceph_global_init_args
) {
760 size_t arg_index
= 0;
761 std::string pool_name
;
762 std::string namespace_name
;
763 std::string image_name
;
764 std::string snap_name
;
765 int r
= utils::get_pool_image_snapshot_names(
766 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
767 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
768 utils::SPEC_VALIDATION_NONE
);
773 at::Format::Formatter f
;
774 r
= utils::get_formatter(vm
, &f
);
779 librados::Rados rados
;
780 librados::IoCtx io_ctx
;
782 r
= utils::init_and_open_image(pool_name
, namespace_name
, image_name
, "", "",
783 false, &rados
, &io_ctx
, &image
);
789 std::vector
<librbd::config_option_t
> options
;
791 r
= image
.config_list(&options
);
793 std::cerr
<< "rbd: failed to list config: " << cpp_strerror(r
) << std::endl
;
797 if (options
.empty()) {
799 std::cout
<< "There are no values" << std::endl
;
805 f
->open_array_section("config");
807 tbl
.define_column("Name", TextTable::LEFT
, TextTable::LEFT
);
808 tbl
.define_column("Value", TextTable::LEFT
, TextTable::LEFT
);
809 tbl
.define_column("Source", TextTable::LEFT
, TextTable::LEFT
);
812 for (auto &option
: options
) {
814 f
->open_object_section("option");
815 f
->dump_string("name", option
.name
);
816 f
->dump_string("value", option
.value
);
817 f
->dump_stream("source") << option
.source
;
820 std::ostringstream source
;
821 source
<< option
.source
;
822 tbl
<< option
.name
<< option
.value
<< source
.str() << TextTable::endrow
;
827 bool single
= (options
.size() == 1);
828 std::cout
<< "There " << (single
? "is" : "are") << " " << options
.size()
829 << " " << (single
? "value" : "values") << ":" << std::endl
;
842 Shell::Action
action_global_get(
843 {"config", "global", "get"}, {},
844 "Get a global-level configuration override.", "",
845 &get_global_get_arguments
, &execute_global_get
);
846 Shell::Action
action_global_set(
847 {"config", "global", "set"}, {},
848 "Set a global-level configuration override.", "",
849 &get_global_set_arguments
, &execute_global_set
);
850 Shell::Action
action_global_remove(
851 {"config", "global", "remove"}, {"config", "global", "rm"},
852 "Remove a global-level configuration override.", "",
853 &get_global_remove_arguments
, &execute_global_remove
);
854 Shell::Action
action_global_list(
855 {"config", "global", "list"}, {"config", "global", "ls"},
856 "List global-level configuration overrides.", "",
857 &get_global_list_arguments
, &execute_global_list
);
859 Shell::Action
action_pool_get(
860 {"config", "pool", "get"}, {}, "Get a pool-level configuration override.", "",
861 &get_pool_get_arguments
, &execute_pool_get
);
862 Shell::Action
action_pool_set(
863 {"config", "pool", "set"}, {}, "Set a pool-level configuration override.", "",
864 &get_pool_set_arguments
, &execute_pool_set
);
865 Shell::Action
action_pool_remove(
866 {"config", "pool", "remove"}, {"config", "pool", "rm"},
867 "Remove a pool-level configuration override.", "",
868 &get_pool_remove_arguments
, &execute_pool_remove
);
869 Shell::Action
action_pool_list(
870 {"config", "pool", "list"}, {"config", "pool", "ls"},
871 "List pool-level configuration overrides.", "",
872 &get_pool_list_arguments
, &execute_pool_list
);
874 Shell::Action
action_image_get(
875 {"config", "image", "get"}, {}, "Get an image-level configuration override.",
876 "", &get_image_get_arguments
, &execute_image_get
);
877 Shell::Action
action_image_set(
878 {"config", "image", "set"}, {}, "Set an image-level configuration override.",
879 "", &get_image_set_arguments
, &execute_image_set
);
880 Shell::Action
action_image_remove(
881 {"config", "image", "remove"}, {"config", "image", "rm"},
882 "Remove an image-level configuration override.", "",
883 &get_image_remove_arguments
, &execute_image_remove
);
884 Shell::Action
action_image_list(
885 {"config", "image", "list"}, {"config", "image", "ls"},
886 "List image-level configuration overrides.", "",
887 &get_image_list_arguments
, &execute_image_list
);
889 } // namespace config
890 } // namespace action