1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 #include "tools/rbd/ArgumentTypes.h"
6 #include "tools/rbd/Shell.h"
7 #include "tools/rbd/Utils.h"
8 #include "include/krbd.h"
9 #include "include/stringify.h"
10 #include "include/uuid.h"
11 #include "common/config.h"
12 #include "common/errno.h"
13 #include "common/safe_io.h"
14 #include "common/strtol.h"
15 #include "common/Formatter.h"
16 #include "msg/msg_types.h"
17 #include "global/global_context.h"
19 #include <boost/algorithm/string/predicate.hpp>
20 #include <boost/scope_exit.hpp>
21 #include <boost/program_options.hpp>
27 namespace at
= argument_types
;
28 namespace po
= boost::program_options
;
32 std::map
<std::string
, std::string
> map_options
; // used for both map and unmap
34 } // anonymous namespace
36 static std::string
map_option_uuid_cb(const char *value_char
)
39 if (!u
.parse(value_char
))
45 static std::string
map_option_ip_cb(const char *value_char
)
49 if (!a
.parse(value_char
, &endptr
) ||
50 endptr
!= value_char
+ strlen(value_char
)) {
54 return stringify(a
.get_sockaddr());
57 static std::string
map_option_int_cb(const char *value_char
)
60 int d
= strict_strtol(value_char
, 10, &err
);
61 if (!err
.empty() || d
< 0)
67 static void put_map_option(const std::string
&key
, std::string val
)
69 map_options
[key
] = val
;
72 static int put_map_option_value(const std::string
&opt
, const char *value_char
,
73 std::string (*parse_cb
)(const char *))
75 if (!value_char
|| *value_char
== '\0') {
76 std::cerr
<< "rbd: " << opt
<< " option requires a value" << std::endl
;
80 std::string value
= parse_cb(value_char
);
82 std::cerr
<< "rbd: invalid " << opt
<< " value '" << value_char
<< "'"
87 put_map_option(opt
, opt
+ "=" + value
);
91 static int parse_map_options(char *options
)
93 for (char *this_char
= strtok(options
, ", ");
95 this_char
= strtok(NULL
, ",")) {
98 if ((value_char
= strchr(this_char
, '=')) != NULL
)
101 if (!strcmp(this_char
, "fsid")) {
102 if (put_map_option_value("fsid", value_char
, map_option_uuid_cb
))
104 } else if (!strcmp(this_char
, "ip")) {
105 if (put_map_option_value("ip", value_char
, map_option_ip_cb
))
107 } else if (!strcmp(this_char
, "share") || !strcmp(this_char
, "noshare")) {
108 put_map_option("share", this_char
);
109 } else if (!strcmp(this_char
, "crc") || !strcmp(this_char
, "nocrc")) {
110 put_map_option("crc", this_char
);
111 } else if (!strcmp(this_char
, "cephx_require_signatures") ||
112 !strcmp(this_char
, "nocephx_require_signatures")) {
113 put_map_option("cephx_require_signatures", this_char
);
114 } else if (!strcmp(this_char
, "tcp_nodelay") ||
115 !strcmp(this_char
, "notcp_nodelay")) {
116 put_map_option("tcp_nodelay", this_char
);
117 } else if (!strcmp(this_char
, "cephx_sign_messages") ||
118 !strcmp(this_char
, "nocephx_sign_messages")) {
119 put_map_option("cephx_sign_messages", this_char
);
120 } else if (!strcmp(this_char
, "mount_timeout")) {
121 if (put_map_option_value("mount_timeout", value_char
, map_option_int_cb
))
123 } else if (!strcmp(this_char
, "osdkeepalive")) {
124 if (put_map_option_value("osdkeepalive", value_char
, map_option_int_cb
))
126 } else if (!strcmp(this_char
, "osd_idle_ttl")) {
127 if (put_map_option_value("osd_idle_ttl", value_char
, map_option_int_cb
))
129 } else if (!strcmp(this_char
, "rw") || !strcmp(this_char
, "ro")) {
130 put_map_option("rw", this_char
);
131 } else if (!strcmp(this_char
, "queue_depth")) {
132 if (put_map_option_value("queue_depth", value_char
, map_option_int_cb
))
134 } else if (!strcmp(this_char
, "lock_on_read")) {
135 put_map_option("lock_on_read", this_char
);
136 } else if (!strcmp(this_char
, "exclusive")) {
137 put_map_option("exclusive", this_char
);
139 std::cerr
<< "rbd: unknown map option '" << this_char
<< "'" << std::endl
;
147 static int parse_unmap_options(char *options
)
149 for (char *this_char
= strtok(options
, ", ");
151 this_char
= strtok(NULL
, ",")) {
154 if ((value_char
= strchr(this_char
, '=')) != NULL
)
155 *value_char
++ = '\0';
157 if (!strcmp(this_char
, "force")) {
158 put_map_option("force", this_char
);
160 std::cerr
<< "rbd: unknown unmap option '" << this_char
<< "'" << std::endl
;
168 static int do_kernel_showmapped(Formatter
*f
)
170 #if defined(WITH_KRBD)
171 struct krbd_ctx
*krbd
;
174 r
= krbd_create_from_context(g_ceph_context
, &krbd
);
178 r
= krbd_showmapped(krbd
, f
);
188 static int get_unsupported_features(librbd::Image
&image
,
189 uint64_t *unsupported_features
)
192 uint64_t features
, supported_features
;
195 r
= safe_read_file("/sys/bus/rbd/", "supported_features", buf
,
202 supported_features
= std::stoull(buf
, nullptr, 16);
207 r
= image
.features(&features
);
211 *unsupported_features
= features
& ~supported_features
;
216 * hint user to check syslog for krbd related messages and provide suggestions
217 * based on errno return by krbd_map(). also note that even if some librbd calls
218 * fail, we at least dump the "try dmesg..." message to aid debugging.
220 static void print_error_description(const char *poolname
, const char *imgname
,
221 const char *snapname
, int maperrno
)
225 librados::Rados rados
;
226 librados::IoCtx ioctx
;
229 if (maperrno
== -ENOENT
)
232 r
= utils::init_and_open_image(poolname
, imgname
, "", snapname
,
233 true, &rados
, &ioctx
, &image
);
237 r
= image
.old_format(&oldformat
);
242 * kernel returns -ENXIO when mapping a V2 image due to unsupported feature
243 * set - so, hint about that too...
245 if (!oldformat
&& (maperrno
== -ENXIO
)) {
246 uint64_t unsupported_features
;
247 bool need_terminate
= true;
249 std::cout
<< "RBD image feature set mismatch. ";
250 r
= get_unsupported_features(image
, &unsupported_features
);
251 if (r
== 0 && (unsupported_features
& ~RBD_FEATURES_ALL
) == 0) {
252 uint64_t immutable
= RBD_FEATURES_ALL
& ~(RBD_FEATURES_MUTABLE
|
253 RBD_FEATURES_DISABLE_ONLY
);
254 if (unsupported_features
& immutable
) {
255 std::cout
<< "This image cannot be mapped because the following "
256 << "immutable features are unsupported by the kernel:";
257 unsupported_features
&= immutable
;
258 need_terminate
= false;
260 std::cout
<< "You can disable features unsupported by the kernel "
261 << "with \"rbd feature disable ";
262 if (poolname
!= at::DEFAULT_POOL_NAME
)
263 std::cout
<< poolname
<< "/";
264 std::cout
<< imgname
;
267 std::cout
<< "Try disabling features unsupported by the kernel "
268 << "with \"rbd feature disable";
269 unsupported_features
= 0;
271 for (auto it
: at::ImageFeatures::FEATURE_MAPPING
) {
272 if (it
.first
& unsupported_features
) {
273 std::cout
<< " " << it
.second
;
278 std::cout
<< "." << std::endl
;
282 std::cout
<< "In some cases useful info is found in syslog - try \"dmesg | tail\"." << std::endl
;
285 static int do_kernel_map(const char *poolname
, const char *imgname
,
286 const char *snapname
)
288 #if defined(WITH_KRBD)
289 struct krbd_ctx
*krbd
;
290 std::ostringstream oss
;
294 r
= krbd_create_from_context(g_ceph_context
, &krbd
);
298 for (std::map
<std::string
, std::string
>::iterator it
= map_options
.begin();
299 it
!= map_options
.end(); ) {
300 // for compatibility with < 3.7 kernels, assume that rw is on by
301 // default and omit it even if it was specified by the user
302 // (see ceph.git commit fb0f1986449b)
303 if (it
->first
== "rw" && it
->second
== "rw") {
304 map_options
.erase(it
);
306 if (it
!= map_options
.begin())
313 r
= krbd_map(krbd
, poolname
, imgname
, snapname
, oss
.str().c_str(), &devnode
);
315 print_error_description(poolname
, imgname
, snapname
, r
);
319 std::cout
<< devnode
<< std::endl
;
330 static int do_kernel_unmap(const char *dev
, const char *poolname
,
331 const char *imgname
, const char *snapname
)
333 #if defined(WITH_KRBD)
334 struct krbd_ctx
*krbd
;
335 std::ostringstream oss
;
338 r
= krbd_create_from_context(g_ceph_context
, &krbd
);
342 for (auto it
= map_options
.cbegin(); it
!= map_options
.cend(); ++it
) {
343 if (it
!= map_options
.cbegin())
349 r
= krbd_unmap(krbd
, dev
, oss
.str().c_str());
351 r
= krbd_unmap_by_spec(krbd
, poolname
, imgname
, snapname
,
362 void get_show_arguments(po::options_description
*positional
,
363 po::options_description
*options
) {
364 at::add_format_options(options
);
367 int execute_show(const po::variables_map
&vm
) {
368 at::Format::Formatter formatter
;
369 int r
= utils::get_formatter(vm
, &formatter
);
374 utils::init_context();
376 r
= do_kernel_showmapped(formatter
.get());
378 std::cerr
<< "rbd: showmapped failed: " << cpp_strerror(r
) << std::endl
;
384 void get_map_arguments(po::options_description
*positional
,
385 po::options_description
*options
) {
386 at::add_image_or_snap_spec_options(positional
, options
,
387 at::ARGUMENT_MODIFIER_NONE
);
388 options
->add_options()
389 ("options,o", po::value
<std::string
>(), "map options")
390 ("read-only", po::bool_switch(), "map read-only")
391 ("exclusive", po::bool_switch(), "disable automatic exclusive lock transitions");
394 int execute_map(const po::variables_map
&vm
) {
395 size_t arg_index
= 0;
396 std::string pool_name
;
397 std::string image_name
;
398 std::string snap_name
;
399 int r
= utils::get_pool_image_snapshot_names(
400 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
401 &snap_name
, utils::SNAPSHOT_PRESENCE_PERMITTED
,
402 utils::SPEC_VALIDATION_NONE
);
407 if (vm
["read-only"].as
<bool>()) {
408 put_map_option("rw", "ro");
410 if (vm
["exclusive"].as
<bool>()) {
411 put_map_option("exclusive", "exclusive");
414 // parse default options first so they can be overwritten by cli options
415 char *default_map_options
= strdup(g_conf
->rbd_default_map_options
.c_str());
416 BOOST_SCOPE_EXIT( (default_map_options
) ) {
417 free(default_map_options
);
418 } BOOST_SCOPE_EXIT_END
;
420 if (parse_map_options(default_map_options
)) {
421 std::cerr
<< "rbd: couldn't parse default map options" << std::endl
;
425 if (vm
.count("options")) {
426 char *cli_map_options
= strdup(vm
["options"].as
<std::string
>().c_str());
427 BOOST_SCOPE_EXIT( (cli_map_options
) ) {
428 free(cli_map_options
);
429 } BOOST_SCOPE_EXIT_END
;
431 if (parse_map_options(cli_map_options
)) {
432 std::cerr
<< "rbd: couldn't parse map options" << std::endl
;
437 utils::init_context();
439 r
= do_kernel_map(pool_name
.c_str(), image_name
.c_str(), snap_name
.c_str());
441 std::cerr
<< "rbd: map failed: " << cpp_strerror(r
) << std::endl
;
448 void get_unmap_arguments(po::options_description
*positional
,
449 po::options_description
*options
) {
450 positional
->add_options()
451 ("image-or-snap-or-device-spec",
452 "image, snapshot, or device specification\n"
453 "[<pool-name>/]<image-name>[@<snapshot-name>] or <device-path>");
454 at::add_pool_option(options
, at::ARGUMENT_MODIFIER_NONE
);
455 at::add_image_option(options
, at::ARGUMENT_MODIFIER_NONE
);
456 at::add_snap_option(options
, at::ARGUMENT_MODIFIER_NONE
);
457 options
->add_options()
458 ("options,o", po::value
<std::string
>(), "unmap options");
461 int execute_unmap(const po::variables_map
&vm
) {
462 std::string device_name
= utils::get_positional_argument(vm
, 0);
463 if (!boost::starts_with(device_name
, "/dev/")) {
467 size_t arg_index
= 0;
468 std::string pool_name
;
469 std::string image_name
;
470 std::string snap_name
;
472 if (device_name
.empty()) {
473 r
= utils::get_pool_image_snapshot_names(
474 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &image_name
,
475 &snap_name
, utils::SNAPSHOT_PRESENCE_PERMITTED
,
476 utils::SPEC_VALIDATION_NONE
, false);
482 if (device_name
.empty() && image_name
.empty()) {
483 std::cerr
<< "rbd: unmap requires either image name or device path"
488 if (vm
.count("options")) {
489 char *cli_unmap_options
= strdup(vm
["options"].as
<std::string
>().c_str());
490 BOOST_SCOPE_EXIT( (cli_unmap_options
) ) {
491 free(cli_unmap_options
);
492 } BOOST_SCOPE_EXIT_END
;
494 if (parse_unmap_options(cli_unmap_options
)) {
495 std::cerr
<< "rbd: couldn't parse unmap options" << std::endl
;
500 utils::init_context();
502 r
= do_kernel_unmap(device_name
.empty() ? nullptr : device_name
.c_str(),
503 pool_name
.c_str(), image_name
.c_str(),
504 snap_name
.empty() ? nullptr : snap_name
.c_str());
506 std::cerr
<< "rbd: unmap failed: " << cpp_strerror(r
) << std::endl
;
512 Shell::SwitchArguments
switched_arguments({"read-only", "exclusive"});
513 Shell::Action
action_show(
514 {"showmapped"}, {}, "Show the rbd images mapped by the kernel.", "",
515 &get_show_arguments
, &execute_show
);
517 Shell::Action
action_map(
518 {"map"}, {}, "Map image to a block device using the kernel.", "",
519 &get_map_arguments
, &execute_map
);
521 Shell::Action
action_unmap(
522 {"unmap"}, {}, "Unmap a rbd device that was used by the kernel.", "",
523 &get_unmap_arguments
, &execute_unmap
);
525 } // namespace kernel
526 } // namespace action