Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / action / Kernel.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 "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"
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"
18 #include <iostream>
19 #include <boost/algorithm/string/predicate.hpp>
20 #include <boost/scope_exit.hpp>
21 #include <boost/program_options.hpp>
22
23 namespace rbd {
24 namespace action {
25 namespace kernel {
26
27 namespace at = argument_types;
28 namespace po = boost::program_options;
29
30 namespace {
31
32 std::map<std::string, std::string> map_options; // used for both map and unmap
33
34 } // anonymous namespace
35
36 static 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
45 static 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
57 static 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
67 static void put_map_option(const std::string &key, std::string val)
68 {
69   map_options[key] = val;
70 }
71
72 static int put_map_option_value(const std::string &opt, const char *value_char,
73                                 std::string (*parse_cb)(const char *))
74 {
75   if (!value_char || *value_char == '\0') {
76     std::cerr << "rbd: " << opt << " option requires a value" << std::endl;
77     return -EINVAL;
78   }
79
80   std::string value = parse_cb(value_char);
81   if (value.empty()) {
82     std::cerr << "rbd: invalid " << opt << " value '" << value_char << "'"
83               << std::endl;
84     return -EINVAL;
85   }
86
87   put_map_option(opt, opt + "=" + value);
88   return 0;
89 }
90
91 static int parse_map_options(char *options)
92 {
93   for (char *this_char = strtok(options, ", ");
94        this_char != NULL;
95        this_char = strtok(NULL, ",")) {
96     char *value_char;
97
98     if ((value_char = strchr(this_char, '=')) != NULL)
99       *value_char++ = '\0';
100
101     if (!strcmp(this_char, "fsid")) {
102       if (put_map_option_value("fsid", value_char, map_option_uuid_cb))
103         return -EINVAL;
104     } else if (!strcmp(this_char, "ip")) {
105       if (put_map_option_value("ip", value_char, map_option_ip_cb))
106         return -EINVAL;
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))
122         return -EINVAL;
123     } else if (!strcmp(this_char, "osdkeepalive")) {
124       if (put_map_option_value("osdkeepalive", value_char, map_option_int_cb))
125         return -EINVAL;
126     } else if (!strcmp(this_char, "osd_idle_ttl")) {
127       if (put_map_option_value("osd_idle_ttl", value_char, map_option_int_cb))
128         return -EINVAL;
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))
133         return -EINVAL;
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);
138     } else {
139       std::cerr << "rbd: unknown map option '" << this_char << "'" << std::endl;
140       return -EINVAL;
141     }
142   }
143
144   return 0;
145 }
146
147 static int parse_unmap_options(char *options)
148 {
149   for (char *this_char = strtok(options, ", ");
150        this_char != NULL;
151        this_char = strtok(NULL, ",")) {
152     char *value_char;
153
154     if ((value_char = strchr(this_char, '=')) != NULL)
155       *value_char++ = '\0';
156
157     if (!strcmp(this_char, "force")) {
158       put_map_option("force", this_char);
159     } else {
160       std::cerr << "rbd: unknown unmap option '" << this_char << "'" << std::endl;
161       return -EINVAL;
162     }
163   }
164
165   return 0;
166 }
167
168 static int do_kernel_showmapped(Formatter *f)
169 {
170 #if defined(WITH_KRBD)
171   struct krbd_ctx *krbd;
172   int r;
173
174   r = krbd_create_from_context(g_ceph_context, &krbd);
175   if (r < 0)
176     return r;
177
178   r = krbd_showmapped(krbd, f);
179
180   krbd_destroy(krbd);
181   return r;
182 #else
183   return -1;
184 #endif
185
186 }
187
188 static int get_unsupported_features(librbd::Image &image,
189                                     uint64_t *unsupported_features)
190 {
191   char buf[20];
192   uint64_t features, supported_features;
193   int r;
194
195   r = safe_read_file("/sys/bus/rbd/", "supported_features", buf,
196                      sizeof(buf) - 1);
197   if (r < 0)
198     return r;
199
200   buf[r] = '\0';
201   try {
202     supported_features = std::stoull(buf, nullptr, 16);
203   } catch (...) {
204     return -EINVAL;
205   }
206
207   r = image.features(&features);
208   if (r < 0)
209     return r;
210
211   *unsupported_features = features & ~supported_features;
212   return 0;
213 }
214
215 /*
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.
219  */
220 static void print_error_description(const char *poolname, const char *imgname,
221                                     const char *snapname, int maperrno)
222 {
223   int r;
224   uint8_t oldformat;
225   librados::Rados rados;
226   librados::IoCtx ioctx;
227   librbd::Image image;
228
229   if (maperrno == -ENOENT)
230     goto done;
231
232   r = utils::init_and_open_image(poolname, imgname, "", snapname,
233                                  true, &rados, &ioctx, &image);
234   if (r < 0)
235     goto done;
236
237   r = image.old_format(&oldformat);
238   if (r < 0)
239     goto done;
240
241   /*
242    * kernel returns -ENXIO when mapping a V2 image due to unsupported feature
243    * set - so, hint about that too...
244    */
245   if (!oldformat && (maperrno == -ENXIO)) {
246     uint64_t unsupported_features;
247     bool need_terminate = true;
248
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;
259       } else {
260         std::cout << "You can disable features unsupported by the kernel "
261                   << "with \"rbd feature disable ";
262
263         if (poolname != utils::get_default_pool_name()) {
264           std::cout << poolname << "/";
265         }
266         std::cout << imgname;
267       }
268     } else {
269       std::cout << "Try disabling features unsupported by the kernel "
270                 << "with \"rbd feature disable";
271       unsupported_features = 0;
272     }
273     for (auto it : at::ImageFeatures::FEATURE_MAPPING) {
274       if (it.first & unsupported_features) {
275         std::cout << " " << it.second;
276       }
277     }
278     if (need_terminate)
279       std::cout << "\"";
280     std::cout << "." << std::endl;
281   }
282
283  done:
284   std::cout << "In some cases useful info is found in syslog - try \"dmesg | tail\"." << std::endl;
285 }
286
287 static int do_kernel_map(const char *poolname, const char *imgname,
288                          const char *snapname)
289 {
290 #if defined(WITH_KRBD)
291   struct krbd_ctx *krbd;
292   std::ostringstream oss;
293   char *devnode;
294   int r;
295
296   r = krbd_create_from_context(g_ceph_context, &krbd);
297   if (r < 0)
298     return r;
299
300   for (std::map<std::string, std::string>::iterator it = map_options.begin();
301        it != map_options.end(); ) {
302     // for compatibility with < 3.7 kernels, assume that rw is on by
303     // default and omit it even if it was specified by the user
304     // (see ceph.git commit fb0f1986449b)
305     if (it->first == "rw" && it->second == "rw") {
306       map_options.erase(it);
307     } else {
308       if (it != map_options.begin())
309         oss << ",";
310       oss << it->second;
311       ++it;
312     }
313   }
314
315   r = krbd_map(krbd, poolname, imgname, snapname, oss.str().c_str(), &devnode);
316   if (r < 0) {
317     print_error_description(poolname, imgname, snapname, r);
318     goto out;
319   }
320
321   std::cout << devnode << std::endl;
322
323   free(devnode);
324 out:
325   krbd_destroy(krbd);
326   return r;
327 #else
328   return -1;
329 #endif
330 }
331
332 static int do_kernel_unmap(const char *dev, const char *poolname,
333                            const char *imgname, const char *snapname)
334 {
335 #if defined(WITH_KRBD)
336   struct krbd_ctx *krbd;
337   std::ostringstream oss;
338   int r;
339
340   r = krbd_create_from_context(g_ceph_context, &krbd);
341   if (r < 0)
342     return r;
343
344   for (auto it = map_options.cbegin(); it != map_options.cend(); ++it) {
345     if (it != map_options.cbegin())
346       oss << ",";
347     oss << it->second;
348   }
349
350   if (dev)
351     r = krbd_unmap(krbd, dev, oss.str().c_str());
352   else
353     r = krbd_unmap_by_spec(krbd, poolname, imgname, snapname,
354                            oss.str().c_str());
355
356   krbd_destroy(krbd);
357   return r;
358 #else
359   return -1;
360 #endif
361
362 }
363
364 void get_show_arguments(po::options_description *positional,
365                         po::options_description *options) {
366   at::add_format_options(options);
367 }
368
369 int execute_show(const po::variables_map &vm) {
370   at::Format::Formatter formatter;
371   int r = utils::get_formatter(vm, &formatter);
372   if (r < 0) {
373     return r;
374   }
375
376   utils::init_context();
377
378   r = do_kernel_showmapped(formatter.get());
379   if (r < 0) {
380     std::cerr << "rbd: showmapped failed: " << cpp_strerror(r) << std::endl;
381     return r;
382   }
383   return 0;
384 }
385
386 void get_map_arguments(po::options_description *positional,
387                        po::options_description *options) {
388   at::add_image_or_snap_spec_options(positional, options,
389                                      at::ARGUMENT_MODIFIER_NONE);
390   options->add_options()
391     ("options,o", po::value<std::string>(), "map options")
392     ("read-only", po::bool_switch(), "map read-only")
393     ("exclusive", po::bool_switch(), "disable automatic exclusive lock transitions");
394 }
395
396 int execute_map(const po::variables_map &vm) {
397   size_t arg_index = 0;
398   std::string pool_name;
399   std::string image_name;
400   std::string snap_name;
401   int r = utils::get_pool_image_snapshot_names(
402     vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
403     &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
404     utils::SPEC_VALIDATION_NONE);
405   if (r < 0) {
406     return r;
407   }
408
409   if (vm["read-only"].as<bool>()) {
410     put_map_option("rw", "ro");
411   }
412   if (vm["exclusive"].as<bool>()) {
413     put_map_option("exclusive", "exclusive");
414   }
415
416   // parse default options first so they can be overwritten by cli options
417   char *default_map_options = strdup(g_conf->get_val<std::string>(
418     "rbd_default_map_options").c_str());
419   BOOST_SCOPE_EXIT( (default_map_options) ) {
420     free(default_map_options);
421   } BOOST_SCOPE_EXIT_END;
422
423   if (parse_map_options(default_map_options)) {
424     std::cerr << "rbd: couldn't parse default map options" << std::endl;
425     return -EINVAL;
426   }
427
428   if (vm.count("options")) {
429     char *cli_map_options = strdup(vm["options"].as<std::string>().c_str());
430     BOOST_SCOPE_EXIT( (cli_map_options) ) {
431       free(cli_map_options);
432     } BOOST_SCOPE_EXIT_END;
433
434     if (parse_map_options(cli_map_options)) {
435       std::cerr << "rbd: couldn't parse map options" << std::endl;
436       return -EINVAL;
437     }
438   }
439
440   utils::init_context();
441
442   r = do_kernel_map(pool_name.c_str(), image_name.c_str(), snap_name.c_str());
443   if (r < 0) {
444     std::cerr << "rbd: map failed: " << cpp_strerror(r) << std::endl;
445     return r;
446   }
447
448   return 0;
449 }
450
451 void get_unmap_arguments(po::options_description *positional,
452                    po::options_description *options) {
453   positional->add_options()
454     ("image-or-snap-or-device-spec",
455      "image, snapshot, or device specification\n"
456      "[<pool-name>/]<image-name>[@<snapshot-name>] or <device-path>");
457   at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
458   at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
459   at::add_snap_option(options, at::ARGUMENT_MODIFIER_NONE);
460   options->add_options()
461     ("options,o", po::value<std::string>(), "unmap options");
462 }
463
464 int execute_unmap(const po::variables_map &vm) {
465   std::string device_name = utils::get_positional_argument(vm, 0);
466   if (!boost::starts_with(device_name, "/dev/")) {
467     device_name.clear();
468   }
469
470   size_t arg_index = 0;
471   std::string pool_name;
472   std::string image_name;
473   std::string snap_name;
474   int r;
475   if (device_name.empty()) {
476     r = utils::get_pool_image_snapshot_names(
477       vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
478       &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED,
479       utils::SPEC_VALIDATION_NONE, false);
480     if (r < 0) {
481       return r;
482     }
483   }
484
485   if (device_name.empty() && image_name.empty()) {
486     std::cerr << "rbd: unmap requires either image name or device path"
487               << std::endl;
488     return -EINVAL;
489   }
490
491   if (vm.count("options")) {
492     char *cli_unmap_options = strdup(vm["options"].as<std::string>().c_str());
493     BOOST_SCOPE_EXIT( (cli_unmap_options) ) {
494       free(cli_unmap_options);
495     } BOOST_SCOPE_EXIT_END;
496
497     if (parse_unmap_options(cli_unmap_options)) {
498       std::cerr << "rbd: couldn't parse unmap options" << std::endl;
499       return -EINVAL;
500     }
501   }
502
503   utils::init_context();
504
505   r = do_kernel_unmap(device_name.empty() ? nullptr : device_name.c_str(),
506                       pool_name.c_str(), image_name.c_str(),
507                       snap_name.empty() ? nullptr : snap_name.c_str());
508   if (r < 0) {
509     std::cerr << "rbd: unmap failed: " << cpp_strerror(r) << std::endl;
510     return r;
511   }
512   return 0;
513 }
514
515 Shell::SwitchArguments switched_arguments({"read-only", "exclusive"});
516 Shell::Action action_show(
517   {"showmapped"}, {}, "Show the rbd images mapped by the kernel.", "",
518   &get_show_arguments, &execute_show);
519
520 Shell::Action action_map(
521   {"map"}, {}, "Map image to a block device using the kernel.", "",
522   &get_map_arguments, &execute_map);
523
524 Shell::Action action_unmap(
525   {"unmap"}, {}, "Unmap a rbd device that was used by the kernel.", "",
526   &get_unmap_arguments, &execute_unmap);
527
528 } // namespace kernel
529 } // namespace action
530 } // namespace rbd