]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
f6b5b4d7 TL |
67 | static std::string map_option_string_cb(const char *value_char) |
68 | { | |
69 | return value_char; | |
70 | } | |
71 | ||
72 | static 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 | ||
81 | static 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 | 90 | static void put_map_option(const std::string &key, const std::string &val) |
7c673cae FG |
91 | { |
92 | map_options[key] = val; | |
93 | } | |
94 | ||
95 | static 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 | 114 | static 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 | 202 | static 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 | 230 | static 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 | ||
249 | static 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 |
281 | static 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 |
353 | static 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); | |
406 | out: | |
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 | ||
415 | static 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 |
457 | int 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 |
475 | int 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 |
530 | int 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 |