]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/Nbd.cc
bump version to 18.2.2-pve1
[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 int execute_list(const po::variables_map &vm,
64 const std::vector<std::string> &ceph_global_init_args) {
65 #if defined(__FreeBSD__) || defined(_WIN32)
66 std::cerr << "rbd: nbd device is not supported" << std::endl;
67 return -EOPNOTSUPP;
68 #else
69 std::vector<std::string> args;
70
71 args.push_back("list-mapped");
72
73 if (vm.count("format")) {
74 args.push_back("--format");
75 args.push_back(vm["format"].as<at::Format>().value);
76 }
77 if (vm["pretty-format"].as<bool>()) {
78 args.push_back("--pretty-format");
79 }
80
81 return call_nbd_cmd(vm, args, ceph_global_init_args);
82 #endif
83 }
84
85 int execute_attach(const po::variables_map &vm,
86 const std::vector<std::string> &ceph_global_init_args) {
87 #if defined(__FreeBSD__) || defined(_WIN32)
88 std::cerr << "rbd: nbd device is not supported" << std::endl;
89 return -EOPNOTSUPP;
90 #else
91 std::vector<std::string> args;
92 std::string device_path;
93
94 args.push_back("attach");
95 std::string img;
96 int r = utils::get_image_or_snap_spec(vm, &img);
97 if (r < 0) {
98 return r;
99 }
100 args.push_back(img);
101
102 if (vm.count("device")) {
103 device_path = vm["device"].as<std::string>();
104 args.push_back("--device");
105 args.push_back(device_path);
106 } else {
107 std::cerr << "rbd: device was not specified" << std::endl;
108 return -EINVAL;
109 }
110
111 if (vm["show-cookie"].as<bool>()) {
112 args.push_back("--show-cookie");
113 }
114
115 if (vm.count("cookie")) {
116 args.push_back("--cookie");
117 args.push_back(vm["cookie"].as<std::string>());
118 } else if (!vm["force"].as<bool>()) {
119 std::cerr << "rbd: could not validate attach request\n";
120 std::cerr << "rbd: mismatching the image and the device may lead to data corruption\n";
121 std::cerr << "rbd: must specify --cookie <arg> or --force to proceed" << std::endl;
122 return -EINVAL;
123 }
124
125 if (vm.count(at::SNAPSHOT_ID)) {
126 args.push_back("--snap-id");
127 args.push_back(std::to_string(vm[at::SNAPSHOT_ID].as<uint64_t>()));
128 }
129
130 if (vm["quiesce"].as<bool>()) {
131 args.push_back("--quiesce");
132 }
133
134 if (vm["read-only"].as<bool>()) {
135 args.push_back("--read-only");
136 }
137
138 if (vm["exclusive"].as<bool>()) {
139 args.push_back("--exclusive");
140 }
141
142 if (vm.count("quiesce-hook")) {
143 args.push_back("--quiesce-hook");
144 args.push_back(vm["quiesce-hook"].as<std::string>());
145 }
146
147 if (vm.count("options")) {
148 utils::append_options_as_args(vm["options"].as<std::vector<std::string>>(),
149 &args);
150 }
151
152 return call_nbd_cmd(vm, args, ceph_global_init_args);
153 #endif
154 }
155
156 int execute_detach(const po::variables_map &vm,
157 const std::vector<std::string> &ceph_global_init_args) {
158 #if defined(__FreeBSD__) || defined(_WIN32)
159 std::cerr << "rbd: nbd device is not supported" << std::endl;
160 return -EOPNOTSUPP;
161 #else
162 std::string device_name = utils::get_positional_argument(vm, 0);
163 if (!boost::starts_with(device_name, "/dev/")) {
164 device_name.clear();
165 }
166
167 std::vector<std::string> args;
168
169 args.push_back("detach");
170 std::string image_name;
171 if (device_name.empty()) {
172 int r = utils::get_image_or_snap_spec(vm, &image_name);
173 if (r < 0) {
174 return r;
175 }
176
177 if (image_name.empty()) {
178 std::cerr << "rbd: detach requires either image name or device path"
179 << std::endl;
180 return -EINVAL;
181 }
182
183 if (vm.count(at::SNAPSHOT_ID)) {
184 args.push_back("--snap-id");
185 args.push_back(std::to_string(vm[at::SNAPSHOT_ID].as<uint64_t>()));
186 }
187 }
188
189 args.push_back(device_name.empty() ? image_name : device_name);
190
191 if (vm.count("options")) {
192 utils::append_options_as_args(vm["options"].as<std::vector<std::string>>(),
193 &args);
194 }
195
196 return call_nbd_cmd(vm, args, ceph_global_init_args);
197 #endif
198 }
199
200 int execute_map(const po::variables_map &vm,
201 const std::vector<std::string> &ceph_global_init_args) {
202 #if defined(__FreeBSD__) || defined(_WIN32)
203 std::cerr << "rbd: nbd device is not supported" << std::endl;
204 return -EOPNOTSUPP;
205 #else
206 std::vector<std::string> args;
207
208 args.push_back("map");
209 std::string img;
210 int r = utils::get_image_or_snap_spec(vm, &img);
211 if (r < 0) {
212 return r;
213 }
214 args.push_back(img);
215
216 if (vm["quiesce"].as<bool>()) {
217 args.push_back("--quiesce");
218 }
219
220 if (vm["show-cookie"].as<bool>()) {
221 args.push_back("--show-cookie");
222 }
223
224 if (vm.count("cookie")) {
225 args.push_back("--cookie");
226 args.push_back(vm["cookie"].as<std::string>());
227 }
228
229 if (vm.count(at::SNAPSHOT_ID)) {
230 args.push_back("--snap-id");
231 args.push_back(std::to_string(vm[at::SNAPSHOT_ID].as<uint64_t>()));
232 }
233
234 if (vm["read-only"].as<bool>()) {
235 args.push_back("--read-only");
236 }
237
238 if (vm["exclusive"].as<bool>()) {
239 args.push_back("--exclusive");
240 }
241
242 if (vm.count("quiesce-hook")) {
243 args.push_back("--quiesce-hook");
244 args.push_back(vm["quiesce-hook"].as<std::string>());
245 }
246
247 if (vm.count("options")) {
248 utils::append_options_as_args(vm["options"].as<std::vector<std::string>>(),
249 &args);
250 }
251
252 return call_nbd_cmd(vm, args, ceph_global_init_args);
253 #endif
254 }
255
256 int execute_unmap(const po::variables_map &vm,
257 const std::vector<std::string> &ceph_global_init_args) {
258 #if defined(__FreeBSD__) || defined(_WIN32)
259 std::cerr << "rbd: nbd device is not supported" << std::endl;
260 return -EOPNOTSUPP;
261 #else
262 std::string device_name = utils::get_positional_argument(vm, 0);
263 if (!boost::starts_with(device_name, "/dev/")) {
264 device_name.clear();
265 }
266
267 std::vector<std::string> args;
268
269 args.push_back("unmap");
270 std::string image_name;
271 if (device_name.empty()) {
272 int r = utils::get_image_or_snap_spec(vm, &image_name);
273 if (r < 0) {
274 return r;
275 }
276
277 if (image_name.empty()) {
278 std::cerr << "rbd: unmap requires either image name or device path"
279 << std::endl;
280 return -EINVAL;
281 }
282
283 if (vm.count(at::SNAPSHOT_ID)) {
284 args.push_back("--snap-id");
285 args.push_back(std::to_string(vm[at::SNAPSHOT_ID].as<uint64_t>()));
286 }
287 }
288
289 args.push_back(device_name.empty() ? image_name : device_name);
290
291 if (vm.count("options")) {
292 utils::append_options_as_args(vm["options"].as<std::vector<std::string>>(),
293 &args);
294 }
295
296 return call_nbd_cmd(vm, args, ceph_global_init_args);
297 #endif
298 }
299
300 void get_list_arguments_deprecated(po::options_description *positional,
301 po::options_description *options) {
302 at::add_format_options(options);
303 }
304
305 int execute_list_deprecated(const po::variables_map &vm,
306 const std::vector<std::string> &ceph_global_args) {
307 std::cerr << "rbd: 'nbd list' command is deprecated, "
308 << "use 'device list -t nbd' instead" << std::endl;
309 return execute_list(vm, ceph_global_args);
310 }
311
312 void get_map_arguments_deprecated(po::options_description *positional,
313 po::options_description *options) {
314 at::add_image_or_snap_spec_options(positional, options,
315 at::ARGUMENT_MODIFIER_NONE);
316 options->add_options()
317 ("read-only", po::bool_switch(), "map read-only")
318 ("exclusive", po::bool_switch(), "forbid writes by other clients")
319 ("device", po::value<std::string>(), "specify nbd device")
320 ("nbds_max", po::value<std::string>(), "override module param nbds_max")
321 ("max_part", po::value<std::string>(), "override module param max_part")
322 ("timeout", po::value<std::string>(), "set nbd request timeout (seconds)");
323 }
324
325 int execute_map_deprecated(const po::variables_map &vm_deprecated,
326 const std::vector<std::string> &ceph_global_args) {
327 std::cerr << "rbd: 'nbd map' command is deprecated, "
328 << "use 'device map -t nbd' instead" << std::endl;
329
330 po::options_description options;
331 options.add_options()
332 ("options,o", po::value<std::vector<std::string>>()
333 ->default_value(std::vector<std::string>(), ""), "");
334
335 po::variables_map vm = vm_deprecated;
336 po::store(po::command_line_parser({}).options(options).run(), vm);
337
338 std::vector<std::string> opts;
339 if (vm_deprecated.count("device")) {
340 opts.push_back("device=" + vm_deprecated["device"].as<std::string>());
341 }
342 if (vm_deprecated.count("nbds_max")) {
343 opts.push_back("nbds_max=" + vm_deprecated["nbds_max"].as<std::string>());
344 }
345 if (vm_deprecated.count("max_part")) {
346 opts.push_back("max_part=" + vm_deprecated["max_part"].as<std::string>());
347 }
348 if (vm_deprecated.count("timeout")) {
349 opts.push_back("timeout=" + vm_deprecated["timeout"].as<std::string>());
350 }
351
352 vm.at("options").value() = boost::any(opts);
353
354 return execute_map(vm, ceph_global_args);
355 }
356
357 void get_unmap_arguments_deprecated(po::options_description *positional,
358 po::options_description *options) {
359 positional->add_options()
360 ("image-or-snap-or-device-spec",
361 "image, snapshot, or device specification\n"
362 "[<pool-name>/]<image-name>[@<snap-name>] or <device-path>");
363 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
364 at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
365 at::add_snap_option(options, at::ARGUMENT_MODIFIER_NONE);
366 }
367
368 int execute_unmap_deprecated(const po::variables_map &vm,
369 const std::vector<std::string> &ceph_global_args) {
370 std::cerr << "rbd: 'nbd unmap' command is deprecated, "
371 << "use 'device unmap -t nbd' instead" << std::endl;
372 return execute_unmap(vm, ceph_global_args);
373 }
374
375 Shell::Action action_show_deprecated(
376 {"nbd", "list"}, {"nbd", "ls"}, "List the nbd devices already used.", "",
377 &get_list_arguments_deprecated, &execute_list_deprecated, false);
378
379 Shell::Action action_map_deprecated(
380 {"nbd", "map"}, {}, "Map image to a nbd device.", "",
381 &get_map_arguments_deprecated, &execute_map_deprecated, false);
382
383 Shell::Action action_unmap_deprecated(
384 {"nbd", "unmap"}, {}, "Unmap a nbd device.", "",
385 &get_unmap_arguments_deprecated, &execute_unmap_deprecated, false);
386
387 } // namespace nbd
388 } // namespace action
389 } // namespace rbd