]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/action/Kernel.cc
bump version to 15.2.8-pve2
[ceph.git] / ceph / src / tools / rbd / action / Kernel.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "acconfig.h"
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"
11fdf7f2 11#include "common/config_proxy.h"
7c673cae
FG
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"
18#include <iostream>
19#include <boost/algorithm/string/predicate.hpp>
20#include <boost/scope_exit.hpp>
21#include <boost/program_options.hpp>
22
23namespace rbd {
24namespace action {
25namespace kernel {
26
27namespace at = argument_types;
28namespace po = boost::program_options;
29
30namespace {
31
32std::map<std::string, std::string> map_options; // used for both map and unmap
33
34} // anonymous namespace
35
36static std::string map_option_uuid_cb(const char *value_char)
37{
38 uuid_d u;
39 if (!u.parse(value_char))
40 return "";
41
42 return stringify(u);
43}
44
45static std::string map_option_ip_cb(const char *value_char)
46{
47 entity_addr_t a;
48 const char *endptr;
49 if (!a.parse(value_char, &endptr) ||
50 endptr != value_char + strlen(value_char)) {
51 return "";
52 }
53
54 return stringify(a.get_sockaddr());
55}
56
57static std::string map_option_int_cb(const char *value_char)
58{
59 std::string err;
60 int d = strict_strtol(value_char, 10, &err);
61 if (!err.empty() || d < 0)
62 return "";
63
64 return stringify(d);
65}
66
f6b5b4d7
TL
67static std::string map_option_string_cb(const char *value_char)
68{
69 return value_char;
70}
71
72static std::string map_option_read_from_replica_cb(const char *value_char)
73{
74 if (!strcmp(value_char, "no") || !strcmp(value_char, "balance") ||
75 !strcmp(value_char, "localize")) {
76 return value_char;
77 }
78 return "";
79}
80
81static std::string map_option_compression_hint_cb(const char *value_char)
82{
83 if (!strcmp(value_char, "none") || !strcmp(value_char, "compressible") ||
84 !strcmp(value_char, "incompressible")) {
85 return value_char;
86 }
87 return "";
88}
89
11fdf7f2 90static void put_map_option(const std::string &key, const std::string &val)
7c673cae
FG
91{
92 map_options[key] = val;
93}
94
95static int put_map_option_value(const std::string &opt, const char *value_char,
96 std::string (*parse_cb)(const char *))
97{
98 if (!value_char || *value_char == '\0') {
99 std::cerr << "rbd: " << opt << " option requires a value" << std::endl;
100 return -EINVAL;
101 }
102
103 std::string value = parse_cb(value_char);
104 if (value.empty()) {
105 std::cerr << "rbd: invalid " << opt << " value '" << value_char << "'"
106 << std::endl;
107 return -EINVAL;
108 }
109
110 put_map_option(opt, opt + "=" + value);
111 return 0;
112}
113
11fdf7f2 114static int parse_map_options(const std::string &options_string)
7c673cae 115{
11fdf7f2
TL
116 char *options = strdup(options_string.c_str());
117 BOOST_SCOPE_EXIT(options) {
118 free(options);
119 } BOOST_SCOPE_EXIT_END;
120
7c673cae
FG
121 for (char *this_char = strtok(options, ", ");
122 this_char != NULL;
123 this_char = strtok(NULL, ",")) {
124 char *value_char;
125
126 if ((value_char = strchr(this_char, '=')) != NULL)
127 *value_char++ = '\0';
128
129 if (!strcmp(this_char, "fsid")) {
130 if (put_map_option_value("fsid", value_char, map_option_uuid_cb))
131 return -EINVAL;
132 } else if (!strcmp(this_char, "ip")) {
133 if (put_map_option_value("ip", value_char, map_option_ip_cb))
134 return -EINVAL;
135 } else if (!strcmp(this_char, "share") || !strcmp(this_char, "noshare")) {
136 put_map_option("share", this_char);
137 } else if (!strcmp(this_char, "crc") || !strcmp(this_char, "nocrc")) {
138 put_map_option("crc", this_char);
139 } else if (!strcmp(this_char, "cephx_require_signatures") ||
140 !strcmp(this_char, "nocephx_require_signatures")) {
141 put_map_option("cephx_require_signatures", this_char);
142 } else if (!strcmp(this_char, "tcp_nodelay") ||
143 !strcmp(this_char, "notcp_nodelay")) {
144 put_map_option("tcp_nodelay", this_char);
145 } else if (!strcmp(this_char, "cephx_sign_messages") ||
146 !strcmp(this_char, "nocephx_sign_messages")) {
147 put_map_option("cephx_sign_messages", this_char);
148 } else if (!strcmp(this_char, "mount_timeout")) {
149 if (put_map_option_value("mount_timeout", value_char, map_option_int_cb))
150 return -EINVAL;
11fdf7f2
TL
151 } else if (!strcmp(this_char, "osd_request_timeout")) {
152 if (put_map_option_value("osd_request_timeout", value_char, map_option_int_cb))
153 return -EINVAL;
154 } else if (!strcmp(this_char, "lock_timeout")) {
155 if (put_map_option_value("lock_timeout", value_char, map_option_int_cb))
156 return -EINVAL;
7c673cae
FG
157 } else if (!strcmp(this_char, "osdkeepalive")) {
158 if (put_map_option_value("osdkeepalive", value_char, map_option_int_cb))
159 return -EINVAL;
160 } else if (!strcmp(this_char, "osd_idle_ttl")) {
161 if (put_map_option_value("osd_idle_ttl", value_char, map_option_int_cb))
162 return -EINVAL;
163 } else if (!strcmp(this_char, "rw") || !strcmp(this_char, "ro")) {
164 put_map_option("rw", this_char);
165 } else if (!strcmp(this_char, "queue_depth")) {
166 if (put_map_option_value("queue_depth", value_char, map_option_int_cb))
167 return -EINVAL;
168 } else if (!strcmp(this_char, "lock_on_read")) {
169 put_map_option("lock_on_read", this_char);
170 } else if (!strcmp(this_char, "exclusive")) {
171 put_map_option("exclusive", this_char);
11fdf7f2
TL
172 } else if (!strcmp(this_char, "notrim")) {
173 put_map_option("notrim", this_char);
174 } else if (!strcmp(this_char, "abort_on_full")) {
175 put_map_option("abort_on_full", this_char);
176 } else if (!strcmp(this_char, "alloc_size")) {
177 if (put_map_option_value("alloc_size", value_char, map_option_int_cb))
178 return -EINVAL;
f6b5b4d7
TL
179 } else if (!strcmp(this_char, "crush_location")) {
180 if (put_map_option_value("crush_location", value_char,
181 map_option_string_cb))
182 return -EINVAL;
183 } else if (!strcmp(this_char, "read_from_replica")) {
184 if (put_map_option_value("read_from_replica", value_char,
185 map_option_read_from_replica_cb))
186 return -EINVAL;
187 } else if (!strcmp(this_char, "compression_hint")) {
188 if (put_map_option_value("compression_hint", value_char,
189 map_option_compression_hint_cb))
190 return -EINVAL;
f91f0fd5
TL
191 } else if (!strcmp(this_char, "udev") || !strcmp(this_char, "noudev")) {
192 put_map_option("udev", this_char);
7c673cae
FG
193 } else {
194 std::cerr << "rbd: unknown map option '" << this_char << "'" << std::endl;
195 return -EINVAL;
196 }
197 }
198
199 return 0;
200}
201
11fdf7f2 202static int parse_unmap_options(const std::string &options_string)
7c673cae 203{
11fdf7f2
TL
204 char *options = strdup(options_string.c_str());
205 BOOST_SCOPE_EXIT(options) {
206 free(options);
207 } BOOST_SCOPE_EXIT_END;
208
7c673cae
FG
209 for (char *this_char = strtok(options, ", ");
210 this_char != NULL;
211 this_char = strtok(NULL, ",")) {
212 char *value_char;
213
214 if ((value_char = strchr(this_char, '=')) != NULL)
215 *value_char++ = '\0';
216
217 if (!strcmp(this_char, "force")) {
218 put_map_option("force", this_char);
f91f0fd5
TL
219 } else if (!strcmp(this_char, "udev") || !strcmp(this_char, "noudev")) {
220 put_map_option("udev", this_char);
7c673cae
FG
221 } else {
222 std::cerr << "rbd: unknown unmap option '" << this_char << "'" << std::endl;
223 return -EINVAL;
224 }
225 }
226
227 return 0;
228}
229
11fdf7f2 230static int do_kernel_list(Formatter *f) {
7c673cae
FG
231#if defined(WITH_KRBD)
232 struct krbd_ctx *krbd;
233 int r;
234
f91f0fd5 235 r = krbd_create_from_context(g_ceph_context, 0, &krbd);
7c673cae
FG
236 if (r < 0)
237 return r;
238
239 r = krbd_showmapped(krbd, f);
240
241 krbd_destroy(krbd);
242 return r;
243#else
11fdf7f2
TL
244 std::cerr << "rbd: kernel device is not supported" << std::endl;
245 return -EOPNOTSUPP;
7c673cae 246#endif
7c673cae
FG
247}
248
249static int get_unsupported_features(librbd::Image &image,
250 uint64_t *unsupported_features)
251{
252 char buf[20];
253 uint64_t features, supported_features;
254 int r;
255
256 r = safe_read_file("/sys/bus/rbd/", "supported_features", buf,
257 sizeof(buf) - 1);
258 if (r < 0)
259 return r;
260
261 buf[r] = '\0';
262 try {
263 supported_features = std::stoull(buf, nullptr, 16);
264 } catch (...) {
265 return -EINVAL;
266 }
267
268 r = image.features(&features);
269 if (r < 0)
270 return r;
271
272 *unsupported_features = features & ~supported_features;
273 return 0;
274}
275
276/*
277 * hint user to check syslog for krbd related messages and provide suggestions
278 * based on errno return by krbd_map(). also note that even if some librbd calls
279 * fail, we at least dump the "try dmesg..." message to aid debugging.
280 */
11fdf7f2
TL
281static void print_error_description(const char *poolname,
282 const char *nspace_name,
283 const char *imgname,
284 const char *snapname,
285 int maperrno)
7c673cae
FG
286{
287 int r;
288 uint8_t oldformat;
289 librados::Rados rados;
290 librados::IoCtx ioctx;
291 librbd::Image image;
292
293 if (maperrno == -ENOENT)
294 goto done;
295
11fdf7f2 296 r = utils::init_and_open_image(poolname, nspace_name, imgname, "", snapname,
7c673cae
FG
297 true, &rados, &ioctx, &image);
298 if (r < 0)
299 goto done;
300
301 r = image.old_format(&oldformat);
302 if (r < 0)
303 goto done;
304
305 /*
306 * kernel returns -ENXIO when mapping a V2 image due to unsupported feature
307 * set - so, hint about that too...
308 */
309 if (!oldformat && (maperrno == -ENXIO)) {
310 uint64_t unsupported_features;
311 bool need_terminate = true;
312
313 std::cout << "RBD image feature set mismatch. ";
314 r = get_unsupported_features(image, &unsupported_features);
315 if (r == 0 && (unsupported_features & ~RBD_FEATURES_ALL) == 0) {
316 uint64_t immutable = RBD_FEATURES_ALL & ~(RBD_FEATURES_MUTABLE |
317 RBD_FEATURES_DISABLE_ONLY);
318 if (unsupported_features & immutable) {
319 std::cout << "This image cannot be mapped because the following "
320 << "immutable features are unsupported by the kernel:";
321 unsupported_features &= immutable;
322 need_terminate = false;
323 } else {
324 std::cout << "You can disable features unsupported by the kernel "
325 << "with \"rbd feature disable ";
11fdf7f2 326 if (poolname != utils::get_default_pool_name() || *nspace_name) {
7c673cae 327 std::cout << poolname << "/";
31f18b77 328 }
11fdf7f2
TL
329 if (*nspace_name) {
330 std::cout << nspace_name << "/";
331 }
7c673cae
FG
332 std::cout << imgname;
333 }
334 } else {
335 std::cout << "Try disabling features unsupported by the kernel "
336 << "with \"rbd feature disable";
337 unsupported_features = 0;
338 }
339 for (auto it : at::ImageFeatures::FEATURE_MAPPING) {
340 if (it.first & unsupported_features) {
341 std::cout << " " << it.second;
342 }
343 }
344 if (need_terminate)
345 std::cout << "\"";
346 std::cout << "." << std::endl;
347 }
348
349 done:
350 std::cout << "In some cases useful info is found in syslog - try \"dmesg | tail\"." << std::endl;
351}
352
11fdf7f2
TL
353static int do_kernel_map(const char *poolname, const char *nspace_name,
354 const char *imgname, const char *snapname)
7c673cae
FG
355{
356#if defined(WITH_KRBD)
357 struct krbd_ctx *krbd;
358 std::ostringstream oss;
f91f0fd5 359 uint32_t flags = 0;
7c673cae
FG
360 char *devnode;
361 int r;
362
f91f0fd5 363 for (auto it = map_options.begin(); it != map_options.end(); ) {
7c673cae
FG
364 // for compatibility with < 3.7 kernels, assume that rw is on by
365 // default and omit it even if it was specified by the user
366 // (see ceph.git commit fb0f1986449b)
367 if (it->first == "rw" && it->second == "rw") {
b32b8144 368 it = map_options.erase(it);
f91f0fd5
TL
369 } else if (it->first == "udev") {
370 if (it->second == "noudev") {
371 flags |= KRBD_CTX_F_NOUDEV;
372 }
373 it = map_options.erase(it);
7c673cae
FG
374 } else {
375 if (it != map_options.begin())
376 oss << ",";
377 oss << it->second;
378 ++it;
379 }
380 }
381
f91f0fd5
TL
382 r = krbd_create_from_context(g_ceph_context, flags, &krbd);
383 if (r < 0)
384 return r;
385
11fdf7f2 386 r = krbd_is_mapped(krbd, poolname, nspace_name, imgname, snapname, &devnode);
7c673cae 387 if (r < 0) {
11fdf7f2
TL
388 std::cerr << "rbd: warning: can't get image map information: "
389 << cpp_strerror(r) << std::endl;
390 } else if (r > 0) {
391 std::cerr << "rbd: warning: image already mapped as " << devnode
392 << std::endl;
393 free(devnode);
394 }
395
396 r = krbd_map(krbd, poolname, nspace_name, imgname, snapname,
397 oss.str().c_str(), &devnode);
398 if (r < 0) {
399 print_error_description(poolname, nspace_name, imgname, snapname, r);
7c673cae
FG
400 goto out;
401 }
402
403 std::cout << devnode << std::endl;
404
405 free(devnode);
406out:
407 krbd_destroy(krbd);
408 return r;
409#else
11fdf7f2
TL
410 std::cerr << "rbd: kernel device is not supported" << std::endl;
411 return -EOPNOTSUPP;
7c673cae
FG
412#endif
413}
414
415static int do_kernel_unmap(const char *dev, const char *poolname,
11fdf7f2
TL
416 const char *nspace_name, const char *imgname,
417 const char *snapname)
7c673cae
FG
418{
419#if defined(WITH_KRBD)
420 struct krbd_ctx *krbd;
421 std::ostringstream oss;
f91f0fd5 422 uint32_t flags = 0;
7c673cae
FG
423 int r;
424
f91f0fd5
TL
425 for (auto it = map_options.begin(); it != map_options.end(); ) {
426 if (it->first == "udev") {
427 if (it->second == "noudev") {
428 flags |= KRBD_CTX_F_NOUDEV;
429 }
430 it = map_options.erase(it);
431 } else {
432 if (it != map_options.begin())
433 oss << ",";
434 oss << it->second;
435 ++it;
436 }
437 }
438
439 r = krbd_create_from_context(g_ceph_context, flags, &krbd);
7c673cae
FG
440 if (r < 0)
441 return r;
442
7c673cae
FG
443 if (dev)
444 r = krbd_unmap(krbd, dev, oss.str().c_str());
445 else
11fdf7f2 446 r = krbd_unmap_by_spec(krbd, poolname, nspace_name, imgname, snapname,
7c673cae
FG
447 oss.str().c_str());
448
449 krbd_destroy(krbd);
450 return r;
451#else
11fdf7f2
TL
452 std::cerr << "rbd: kernel device is not supported" << std::endl;
453 return -EOPNOTSUPP;
7c673cae 454#endif
7c673cae
FG
455}
456
11fdf7f2
TL
457int execute_list(const po::variables_map &vm,
458 const std::vector<std::string> &ceph_global_init_args) {
7c673cae
FG
459 at::Format::Formatter formatter;
460 int r = utils::get_formatter(vm, &formatter);
461 if (r < 0) {
462 return r;
463 }
464
465 utils::init_context();
466
11fdf7f2 467 r = do_kernel_list(formatter.get());
7c673cae 468 if (r < 0) {
11fdf7f2 469 std::cerr << "rbd: device list failed: " << cpp_strerror(r) << std::endl;
7c673cae
FG
470 return r;
471 }
472 return 0;
473}
474
11fdf7f2
TL
475int execute_map(const po::variables_map &vm,
476 const std::vector<std::string> &ceph_global_init_args) {
7c673cae
FG
477 size_t arg_index = 0;
478 std::string pool_name;
11fdf7f2 479 std::string nspace_name;
7c673cae
FG
480 std::string image_name;
481 std::string snap_name;
482 int r = utils::get_pool_image_snapshot_names(
11fdf7f2
TL
483 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &nspace_name,
484 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_PERMITTED,
7c673cae
FG
485 utils::SPEC_VALIDATION_NONE);
486 if (r < 0) {
487 return r;
488 }
489
7c673cae 490 // parse default options first so they can be overwritten by cli options
11fdf7f2
TL
491 r = parse_map_options(
492 g_conf().get_val<std::string>("rbd_default_map_options"));
493 if (r < 0) {
7c673cae 494 std::cerr << "rbd: couldn't parse default map options" << std::endl;
11fdf7f2 495 return r;
7c673cae
FG
496 }
497
498 if (vm.count("options")) {
11fdf7f2
TL
499 for (auto &options : vm["options"].as<std::vector<std::string>>()) {
500 r = parse_map_options(options);
501 if (r < 0) {
502 std::cerr << "rbd: couldn't parse map options" << std::endl;
503 return r;
504 }
7c673cae
FG
505 }
506 }
507
f91f0fd5
TL
508 // parse options common to all device types after parsing krbd-specific
509 // options so that common options win (in particular "-o rw --read-only"
510 // should result in read-only mapping)
511 if (vm["read-only"].as<bool>()) {
512 put_map_option("rw", "ro");
513 }
514 if (vm["exclusive"].as<bool>()) {
515 put_map_option("exclusive", "exclusive");
516 }
517
7c673cae
FG
518 utils::init_context();
519
11fdf7f2
TL
520 r = do_kernel_map(pool_name.c_str(), nspace_name.c_str(), image_name.c_str(),
521 snap_name.c_str());
7c673cae
FG
522 if (r < 0) {
523 std::cerr << "rbd: map failed: " << cpp_strerror(r) << std::endl;
524 return r;
525 }
526
527 return 0;
528}
529
11fdf7f2
TL
530int execute_unmap(const po::variables_map &vm,
531 const std::vector<std::string> &ceph_global_init_args) {
7c673cae
FG
532 std::string device_name = utils::get_positional_argument(vm, 0);
533 if (!boost::starts_with(device_name, "/dev/")) {
534 device_name.clear();
535 }
536
537 size_t arg_index = 0;
538 std::string pool_name;
11fdf7f2 539 std::string nspace_name;
7c673cae
FG
540 std::string image_name;
541 std::string snap_name;
542 int r;
543 if (device_name.empty()) {
544 r = utils::get_pool_image_snapshot_names(
11fdf7f2
TL
545 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &nspace_name,
546 &image_name, &snap_name, false, utils::SNAPSHOT_PRESENCE_PERMITTED,
547 utils::SPEC_VALIDATION_NONE);
7c673cae
FG
548 if (r < 0) {
549 return r;
550 }
551 }
552
553 if (device_name.empty() && image_name.empty()) {
554 std::cerr << "rbd: unmap requires either image name or device path"
555 << std::endl;
556 return -EINVAL;
557 }
558
559 if (vm.count("options")) {
11fdf7f2
TL
560 for (auto &options : vm["options"].as<std::vector<std::string>>()) {
561 r = parse_unmap_options(options);
562 if (r < 0) {
563 std::cerr << "rbd: couldn't parse unmap options" << std::endl;
564 return r;
565 }
7c673cae
FG
566 }
567 }
568
569 utils::init_context();
570
571 r = do_kernel_unmap(device_name.empty() ? nullptr : device_name.c_str(),
11fdf7f2
TL
572 pool_name.c_str(), nspace_name.c_str(),
573 image_name.c_str(), snap_name.c_str());
7c673cae
FG
574 if (r < 0) {
575 std::cerr << "rbd: unmap failed: " << cpp_strerror(r) << std::endl;
576 return r;
577 }
578 return 0;
579}
580
7c673cae
FG
581} // namespace kernel
582} // namespace action
583} // namespace rbd