]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/Nbd.cc
68345f43dd6c5228ea635928aae2002a701d489a
[ceph.git] / ceph / src / tools / rbd / action / Nbd.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 "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "include/stringify.h"
8 #include "common/SubProcess.h"
9 #include <iostream>
10 #include <boost/algorithm/string.hpp>
11 #include <boost/algorithm/string/predicate.hpp>
12 #include <boost/program_options.hpp>
13
14 namespace rbd {
15 namespace action {
16 namespace nbd {
17
18 namespace at = argument_types;
19 namespace po = boost::program_options;
20
21 static int call_nbd_cmd(const po::variables_map &vm,
22 const std::vector<std::string> &args,
23 const std::vector<std::string> &ceph_global_init_args) {
24 #if defined(__FreeBSD__) || defined(_WIN32)
25 std::cerr << "rbd: nbd device is not supported" << std::endl;
26 return -EOPNOTSUPP;
27 #else
28 char exe_path[PATH_MAX];
29 ssize_t exe_path_bytes = readlink("/proc/self/exe", exe_path,
30 sizeof(exe_path) - 1);
31 if (exe_path_bytes < 0) {
32 strcpy(exe_path, "rbd-nbd");
33 } else {
34 if (snprintf(exe_path + exe_path_bytes,
35 sizeof(exe_path) - exe_path_bytes,
36 "-nbd") < 0) {
37 return -EOVERFLOW;
38 }
39 }
40
41 SubProcess process(exe_path, SubProcess::KEEP, SubProcess::KEEP, SubProcess::KEEP);
42
43 for (auto &arg : ceph_global_init_args) {
44 process.add_cmd_arg(arg.c_str());
45 }
46
47 for (auto &arg : args) {
48 process.add_cmd_arg(arg.c_str());
49 }
50
51 if (process.spawn()) {
52 std::cerr << "rbd: failed to run rbd-nbd: " << process.err() << std::endl;
53 return -EINVAL;
54 } else if (process.join()) {
55 std::cerr << "rbd: rbd-nbd failed with error: " << process.err() << std::endl;
56 return -EINVAL;
57 }
58
59 return 0;
60 #endif
61 }
62
63 #if !defined(__FreeBSD__) && !defined(_WIN32)
64 int get_image_or_snap_spec(const po::variables_map &vm, std::string *spec) {
65 size_t arg_index = 0;
66 std::string pool_name;
67 std::string nspace_name;
68 std::string image_name;
69 std::string snap_name;
70 int r = utils::get_pool_image_snapshot_names(
71 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &nspace_name,
72 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_PERMITTED,
73 utils::SPEC_VALIDATION_NONE);
74 if (r < 0) {
75 return r;
76 }
77
78 if (pool_name.empty()) {
79 // connect to the cluster to get the default pool
80 librados::Rados rados;
81 r = utils::init_rados(&rados);
82 if (r < 0) {
83 return r;
84 }
85
86 utils::normalize_pool_name(&pool_name);
87 }
88
89 spec->append(pool_name);
90 spec->append("/");
91 if (!nspace_name.empty()) {
92 spec->append(nspace_name);
93 spec->append("/");
94 }
95 spec->append(image_name);
96 if (!snap_name.empty()) {
97 spec->append("@");
98 spec->append(snap_name);
99 }
100
101 return 0;
102 }
103
104 int parse_options(const std::vector<std::string> &options,
105 std::vector<std::string> *args) {
106 for (auto &opts : options) {
107 std::vector<std::string> args_;
108 boost::split(args_, opts, boost::is_any_of(","));
109 for (auto &o : args_) {
110 args->push_back("--" + o);
111 }
112 }
113
114 return 0;
115 }
116 #endif
117
118 int execute_list(const po::variables_map &vm,
119 const std::vector<std::string> &ceph_global_init_args) {
120 #if defined(__FreeBSD__) || defined(_WIN32)
121 std::cerr << "rbd: nbd device is not supported" << std::endl;
122 return -EOPNOTSUPP;
123 #else
124 std::vector<std::string> args;
125
126 args.push_back("list-mapped");
127
128 if (vm.count("format")) {
129 args.push_back("--format");
130 args.push_back(vm["format"].as<at::Format>().value);
131 }
132 if (vm["pretty-format"].as<bool>()) {
133 args.push_back("--pretty-format");
134 }
135
136 return call_nbd_cmd(vm, args, ceph_global_init_args);
137 #endif
138 }
139
140 int execute_attach(const po::variables_map &vm,
141 const std::vector<std::string> &ceph_global_init_args) {
142 #if defined(__FreeBSD__) || defined(_WIN32)
143 std::cerr << "rbd: nbd device is not supported" << std::endl;
144 return -EOPNOTSUPP;
145 #else
146 std::vector<std::string> args;
147 std::string device_path;
148
149 args.push_back("attach");
150 std::string img;
151 int r = get_image_or_snap_spec(vm, &img);
152 if (r < 0) {
153 return r;
154 }
155 args.push_back(img);
156
157 if (vm.count("device")) {
158 device_path = vm["device"].as<std::string>();
159 args.push_back("--device");
160 args.push_back(device_path);
161 } else {
162 std::cerr << "rbd: device was not specified" << std::endl;
163 return -EINVAL;
164 }
165
166 if (vm["show-cookie"].as<bool>()) {
167 args.push_back("--show-cookie");
168 }
169
170 if (vm.count("cookie")) {
171 args.push_back("--cookie");
172 args.push_back(vm["cookie"].as<std::string>());
173 } else if (!vm["force"].as<bool>()) {
174 std::cerr << "rbd: could not validate attach request\n";
175 std::cerr << "rbd: mismatching the image and the device may lead to data corruption\n";
176 std::cerr << "rbd: must specify --cookie <arg> or --force to proceed" << std::endl;
177 return -EINVAL;
178 }
179
180 if (vm["quiesce"].as<bool>()) {
181 args.push_back("--quiesce");
182 }
183
184 if (vm["read-only"].as<bool>()) {
185 args.push_back("--read-only");
186 }
187
188 if (vm["exclusive"].as<bool>()) {
189 args.push_back("--exclusive");
190 }
191
192 if (vm.count("quiesce-hook")) {
193 args.push_back("--quiesce-hook");
194 args.push_back(vm["quiesce-hook"].as<std::string>());
195 }
196
197 if (vm.count("options")) {
198 r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
199 if (r < 0) {
200 return r;
201 }
202 }
203
204 return call_nbd_cmd(vm, args, ceph_global_init_args);
205 #endif
206 }
207
208 int execute_detach(const po::variables_map &vm,
209 const std::vector<std::string> &ceph_global_init_args) {
210 #if defined(__FreeBSD__) || defined(_WIN32)
211 std::cerr << "rbd: nbd device is not supported" << std::endl;
212 return -EOPNOTSUPP;
213 #else
214 std::string device_name = utils::get_positional_argument(vm, 0);
215 if (!boost::starts_with(device_name, "/dev/")) {
216 device_name.clear();
217 }
218
219 std::string image_name;
220 if (device_name.empty()) {
221 int r = get_image_or_snap_spec(vm, &image_name);
222 if (r < 0) {
223 return r;
224 }
225 }
226
227 if (device_name.empty() && image_name.empty()) {
228 std::cerr << "rbd: detach requires either image name or device path"
229 << std::endl;
230 return -EINVAL;
231 }
232
233 std::vector<std::string> args;
234
235 args.push_back("detach");
236 args.push_back(device_name.empty() ? image_name : device_name);
237
238 if (vm.count("options")) {
239 int r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
240 if (r < 0) {
241 return r;
242 }
243 }
244
245 return call_nbd_cmd(vm, args, ceph_global_init_args);
246 #endif
247 }
248
249 int execute_map(const po::variables_map &vm,
250 const std::vector<std::string> &ceph_global_init_args) {
251 #if defined(__FreeBSD__) || defined(_WIN32)
252 std::cerr << "rbd: nbd device is not supported" << std::endl;
253 return -EOPNOTSUPP;
254 #else
255 std::vector<std::string> args;
256
257 args.push_back("map");
258 std::string img;
259 int r = get_image_or_snap_spec(vm, &img);
260 if (r < 0) {
261 return r;
262 }
263 args.push_back(img);
264
265 if (vm["quiesce"].as<bool>()) {
266 args.push_back("--quiesce");
267 }
268
269 if (vm["show-cookie"].as<bool>()) {
270 args.push_back("--show-cookie");
271 }
272
273 if (vm.count("cookie")) {
274 args.push_back("--cookie");
275 args.push_back(vm["cookie"].as<std::string>());
276 }
277
278 if (vm["read-only"].as<bool>()) {
279 args.push_back("--read-only");
280 }
281
282 if (vm["exclusive"].as<bool>()) {
283 args.push_back("--exclusive");
284 }
285
286 if (vm.count("quiesce-hook")) {
287 args.push_back("--quiesce-hook");
288 args.push_back(vm["quiesce-hook"].as<std::string>());
289 }
290
291 if (vm.count("options")) {
292 r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
293 if (r < 0) {
294 return r;
295 }
296 }
297
298 return call_nbd_cmd(vm, args, ceph_global_init_args);
299 #endif
300 }
301
302 int execute_unmap(const po::variables_map &vm,
303 const std::vector<std::string> &ceph_global_init_args) {
304 #if defined(__FreeBSD__) || defined(_WIN32)
305 std::cerr << "rbd: nbd device is not supported" << std::endl;
306 return -EOPNOTSUPP;
307 #else
308 std::string device_name = utils::get_positional_argument(vm, 0);
309 if (!boost::starts_with(device_name, "/dev/")) {
310 device_name.clear();
311 }
312
313 std::string image_name;
314 if (device_name.empty()) {
315 int r = get_image_or_snap_spec(vm, &image_name);
316 if (r < 0) {
317 return r;
318 }
319 }
320
321 if (device_name.empty() && image_name.empty()) {
322 std::cerr << "rbd: unmap requires either image name or device path"
323 << std::endl;
324 return -EINVAL;
325 }
326
327 std::vector<std::string> args;
328
329 args.push_back("unmap");
330 args.push_back(device_name.empty() ? image_name : device_name);
331
332 if (vm.count("options")) {
333 int r = parse_options(vm["options"].as<std::vector<std::string>>(), &args);
334 if (r < 0) {
335 return r;
336 }
337 }
338
339 return call_nbd_cmd(vm, args, ceph_global_init_args);
340 #endif
341 }
342
343 void get_list_arguments_deprecated(po::options_description *positional,
344 po::options_description *options) {
345 at::add_format_options(options);
346 }
347
348 int execute_list_deprecated(const po::variables_map &vm,
349 const std::vector<std::string> &ceph_global_args) {
350 std::cerr << "rbd: 'nbd list' command is deprecated, "
351 << "use 'device list -t nbd' instead" << std::endl;
352 return execute_list(vm, ceph_global_args);
353 }
354
355 void get_map_arguments_deprecated(po::options_description *positional,
356 po::options_description *options) {
357 at::add_image_or_snap_spec_options(positional, options,
358 at::ARGUMENT_MODIFIER_NONE);
359 options->add_options()
360 ("read-only", po::bool_switch(), "map read-only")
361 ("exclusive", po::bool_switch(), "forbid writes by other clients")
362 ("device", po::value<std::string>(), "specify nbd device")
363 ("nbds_max", po::value<std::string>(), "override module param nbds_max")
364 ("max_part", po::value<std::string>(), "override module param max_part")
365 ("timeout", po::value<std::string>(), "set nbd request timeout (seconds)");
366 }
367
368 int execute_map_deprecated(const po::variables_map &vm_deprecated,
369 const std::vector<std::string> &ceph_global_args) {
370 std::cerr << "rbd: 'nbd map' command is deprecated, "
371 << "use 'device map -t nbd' instead" << std::endl;
372
373 po::options_description options;
374 options.add_options()
375 ("options,o", po::value<std::vector<std::string>>()
376 ->default_value(std::vector<std::string>(), ""), "");
377
378 po::variables_map vm = vm_deprecated;
379 po::store(po::command_line_parser({}).options(options).run(), vm);
380
381 std::vector<std::string> opts;
382 if (vm_deprecated.count("device")) {
383 opts.push_back("device=" + vm_deprecated["device"].as<std::string>());
384 }
385 if (vm_deprecated.count("nbds_max")) {
386 opts.push_back("nbds_max=" + vm_deprecated["nbds_max"].as<std::string>());
387 }
388 if (vm_deprecated.count("max_part")) {
389 opts.push_back("max_part=" + vm_deprecated["max_part"].as<std::string>());
390 }
391 if (vm_deprecated.count("timeout")) {
392 opts.push_back("timeout=" + vm_deprecated["timeout"].as<std::string>());
393 }
394
395 vm.at("options").value() = boost::any(opts);
396
397 return execute_map(vm, ceph_global_args);
398 }
399
400 void get_unmap_arguments_deprecated(po::options_description *positional,
401 po::options_description *options) {
402 positional->add_options()
403 ("image-or-snap-or-device-spec",
404 "image, snapshot, or device specification\n"
405 "[<pool-name>/]<image-name>[@<snap-name>] or <device-path>");
406 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
407 at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
408 at::add_snap_option(options, at::ARGUMENT_MODIFIER_NONE);
409 }
410
411 int execute_unmap_deprecated(const po::variables_map &vm,
412 const std::vector<std::string> &ceph_global_args) {
413 std::cerr << "rbd: 'nbd unmap' command is deprecated, "
414 << "use 'device unmap -t nbd' instead" << std::endl;
415 return execute_unmap(vm, ceph_global_args);
416 }
417
418 Shell::Action action_show_deprecated(
419 {"nbd", "list"}, {"nbd", "ls"}, "List the nbd devices already used.", "",
420 &get_list_arguments_deprecated, &execute_list_deprecated, false);
421
422 Shell::Action action_map_deprecated(
423 {"nbd", "map"}, {}, "Map image to a nbd device.", "",
424 &get_map_arguments_deprecated, &execute_map_deprecated, false);
425
426 Shell::Action action_unmap_deprecated(
427 {"nbd", "unmap"}, {}, "Unmap a nbd device.", "",
428 &get_unmap_arguments_deprecated, &execute_unmap_deprecated, false);
429
430 } // namespace nbd
431 } // namespace action
432 } // namespace rbd