]>
Commit | Line | Data |
---|---|---|
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/types.h" | |
8 | #include "include/stringify.h" | |
9 | #include "common/errno.h" | |
10 | #include "common/Formatter.h" | |
11 | #include "common/TextTable.h" | |
12 | #include <iostream> | |
13 | #include <boost/program_options.hpp> | |
14 | #include <boost/bind/bind.hpp> | |
15 | ||
16 | namespace rbd { | |
17 | namespace action { | |
18 | namespace snap { | |
19 | ||
20 | using namespace boost::placeholders; | |
21 | ||
22 | static const std::string ALL_NAME("all"); | |
23 | ||
24 | namespace at = argument_types; | |
25 | namespace po = boost::program_options; | |
26 | ||
27 | int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados::Rados& rados) | |
28 | { | |
29 | std::vector<librbd::snap_info_t> snaps; | |
30 | TextTable t; | |
31 | int r; | |
32 | ||
33 | r = image.snap_list(snaps); | |
34 | if (r < 0) { | |
35 | std::cerr << "rbd: unable to list snapshots" << std::endl; | |
36 | return r; | |
37 | } | |
38 | ||
39 | librbd::image_info_t info; | |
40 | if (!all_snaps) { | |
41 | snaps.erase(remove_if(snaps.begin(), | |
42 | snaps.end(), | |
43 | boost::bind(utils::is_not_user_snap_namespace, &image, _1)), | |
44 | snaps.end()); | |
45 | } else if (!f) { | |
46 | r = image.stat(info, sizeof(info)); | |
47 | if (r < 0) { | |
48 | std::cerr << "rbd: unable to get image info" << std::endl; | |
49 | return r; | |
50 | } | |
51 | } | |
52 | ||
53 | if (f) { | |
54 | f->open_array_section("snapshots"); | |
55 | } else { | |
56 | t.define_column("SNAPID", TextTable::LEFT, TextTable::RIGHT); | |
57 | t.define_column("NAME", TextTable::LEFT, TextTable::LEFT); | |
58 | t.define_column("SIZE", TextTable::LEFT, TextTable::RIGHT); | |
59 | t.define_column("PROTECTED", TextTable::LEFT, TextTable::LEFT); | |
60 | t.define_column("TIMESTAMP", TextTable::LEFT, TextTable::RIGHT); | |
61 | if (all_snaps) { | |
62 | t.define_column("NAMESPACE", TextTable::LEFT, TextTable::LEFT); | |
63 | } | |
64 | } | |
65 | ||
66 | std::list<std::pair<int64_t, std::string>> pool_list; | |
67 | rados.pool_list2(pool_list); | |
68 | std::map<int64_t, std::string> pool_map(pool_list.begin(), pool_list.end()); | |
69 | ||
70 | for (std::vector<librbd::snap_info_t>::iterator s = snaps.begin(); | |
71 | s != snaps.end(); ++s) { | |
72 | struct timespec timestamp; | |
73 | bool snap_protected = false; | |
74 | image.snap_get_timestamp(s->id, ×tamp); | |
75 | std::string tt_str = ""; | |
76 | if(timestamp.tv_sec != 0) { | |
77 | time_t tt = timestamp.tv_sec; | |
78 | tt_str = ctime(&tt); | |
79 | tt_str = tt_str.substr(0, tt_str.length() - 1); | |
80 | } | |
81 | ||
82 | librbd::snap_namespace_type_t snap_namespace; | |
83 | r = image.snap_get_namespace_type(s->id, &snap_namespace); | |
84 | if (r < 0) { | |
85 | std::cerr << "rbd: unable to retrieve snap namespace" << std::endl; | |
86 | return r; | |
87 | } | |
88 | ||
89 | std::string snap_namespace_name = "Unknown"; | |
90 | switch (snap_namespace) { | |
91 | case RBD_SNAP_NAMESPACE_TYPE_USER: | |
92 | snap_namespace_name = "user"; | |
93 | break; | |
94 | case RBD_SNAP_NAMESPACE_TYPE_GROUP: | |
95 | snap_namespace_name = "group"; | |
96 | break; | |
97 | case RBD_SNAP_NAMESPACE_TYPE_TRASH: | |
98 | snap_namespace_name = "trash"; | |
99 | break; | |
100 | case RBD_SNAP_NAMESPACE_TYPE_MIRROR: | |
101 | snap_namespace_name = "mirror"; | |
102 | break; | |
103 | } | |
104 | ||
105 | int get_trash_res = -ENOENT; | |
106 | std::string trash_original_name; | |
107 | int get_group_res = -ENOENT; | |
108 | librbd::snap_group_namespace_t group_snap; | |
109 | int get_mirror_res = -ENOENT; | |
110 | librbd::snap_mirror_namespace_t mirror_snap; | |
111 | std::string mirror_snap_state = "unknown"; | |
112 | if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_GROUP) { | |
113 | get_group_res = image.snap_get_group_namespace(s->id, &group_snap, | |
114 | sizeof(group_snap)); | |
115 | } else if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_TRASH) { | |
116 | get_trash_res = image.snap_get_trash_namespace( | |
117 | s->id, &trash_original_name); | |
118 | } else if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_MIRROR) { | |
119 | get_mirror_res = image.snap_get_mirror_namespace( | |
120 | s->id, &mirror_snap, sizeof(mirror_snap)); | |
121 | ||
122 | switch (mirror_snap.state) { | |
123 | case RBD_SNAP_MIRROR_STATE_PRIMARY: | |
124 | mirror_snap_state = "primary"; | |
125 | break; | |
126 | case RBD_SNAP_MIRROR_STATE_NON_PRIMARY: | |
127 | mirror_snap_state = "non-primary"; | |
128 | break; | |
129 | case RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED: | |
130 | case RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED: | |
131 | mirror_snap_state = "demoted"; | |
132 | break; | |
133 | } | |
134 | } | |
135 | ||
136 | std::string protected_str = ""; | |
137 | if (snap_namespace == RBD_SNAP_NAMESPACE_TYPE_USER) { | |
138 | r = image.snap_is_protected(s->name.c_str(), &snap_protected); | |
139 | if (r < 0) { | |
140 | std::cerr << "rbd: unable to retrieve snap protection" << std::endl; | |
141 | return r; | |
142 | } | |
143 | } | |
144 | ||
145 | if (f) { | |
146 | protected_str = snap_protected ? "true" : "false"; | |
147 | f->open_object_section("snapshot"); | |
148 | f->dump_unsigned("id", s->id); | |
149 | f->dump_string("name", s->name); | |
150 | f->dump_unsigned("size", s->size); | |
151 | f->dump_string("protected", protected_str); | |
152 | f->dump_string("timestamp", tt_str); | |
153 | if (all_snaps) { | |
154 | f->open_object_section("namespace"); | |
155 | f->dump_string("type", snap_namespace_name); | |
156 | if (get_group_res == 0) { | |
157 | std::string pool_name = pool_map[group_snap.group_pool]; | |
158 | f->dump_string("pool", pool_name); | |
159 | f->dump_string("group", group_snap.group_name); | |
160 | f->dump_string("group snap", group_snap.group_snap_name); | |
161 | } else if (get_trash_res == 0) { | |
162 | f->dump_string("original_name", trash_original_name); | |
163 | } else if (get_mirror_res == 0) { | |
164 | f->dump_string("state", mirror_snap_state); | |
165 | f->open_array_section("mirror_peer_uuids"); | |
166 | for (auto &uuid : mirror_snap.mirror_peer_uuids) { | |
167 | f->dump_string("peer_uuid", uuid); | |
168 | } | |
169 | f->close_section(); | |
170 | f->dump_bool("complete", mirror_snap.complete); | |
171 | if (mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY || | |
172 | mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED) { | |
173 | f->dump_string("primary_mirror_uuid", | |
174 | mirror_snap.primary_mirror_uuid); | |
175 | f->dump_unsigned("primary_snap_id", | |
176 | mirror_snap.primary_snap_id); | |
177 | f->dump_unsigned("last_copied_object_number", | |
178 | mirror_snap.last_copied_object_number); | |
179 | } | |
180 | } | |
181 | f->close_section(); | |
182 | } | |
183 | f->close_section(); | |
184 | } else { | |
185 | protected_str = snap_protected ? "yes" : ""; | |
186 | t << s->id << s->name << stringify(byte_u_t(s->size)) << protected_str << tt_str; | |
187 | ||
188 | if (all_snaps) { | |
189 | std::ostringstream oss; | |
190 | oss << snap_namespace_name; | |
191 | ||
192 | if (get_group_res == 0) { | |
193 | std::string pool_name = pool_map[group_snap.group_pool]; | |
194 | oss << " (" << pool_name << "/" | |
195 | << group_snap.group_name << "@" | |
196 | << group_snap.group_snap_name << ")"; | |
197 | } else if (get_trash_res == 0) { | |
198 | oss << " (" << trash_original_name << ")"; | |
199 | } else if (get_mirror_res == 0) { | |
200 | oss << " (" << mirror_snap_state << " " | |
201 | << "peer_uuids:[" << mirror_snap.mirror_peer_uuids << "]"; | |
202 | if (mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY || | |
203 | mirror_snap.state == RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED) { | |
204 | oss << " " << mirror_snap.primary_mirror_uuid << ":" | |
205 | << mirror_snap.primary_snap_id << " "; | |
206 | if (!mirror_snap.complete) { | |
207 | if (info.num_objs > 0) { | |
208 | auto progress = std::min<uint64_t>( | |
209 | 100, 100 * mirror_snap.last_copied_object_number / | |
210 | info.num_objs); | |
211 | oss << progress << "% "; | |
212 | } else { | |
213 | oss << "not "; | |
214 | } | |
215 | } | |
216 | oss << "copied"; | |
217 | } | |
218 | oss << ")"; | |
219 | } | |
220 | ||
221 | t << oss.str(); | |
222 | } | |
223 | t << TextTable::endrow; | |
224 | } | |
225 | } | |
226 | ||
227 | if (f) { | |
228 | f->close_section(); | |
229 | f->flush(std::cout); | |
230 | } else if (snaps.size()) { | |
231 | std::cout << t; | |
232 | } | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | int do_add_snap(librbd::Image& image, const char *snapname, | |
238 | uint32_t flags, bool no_progress) | |
239 | { | |
240 | utils::ProgressContext pc("Creating snap", no_progress); | |
241 | ||
242 | int r = image.snap_create2(snapname, flags, pc); | |
243 | if (r < 0) { | |
244 | pc.fail(); | |
245 | return r; | |
246 | } | |
247 | ||
248 | pc.finish(); | |
249 | return 0; | |
250 | } | |
251 | ||
252 | int do_remove_snap(librbd::Image& image, const char *snapname, bool force, | |
253 | bool no_progress) | |
254 | { | |
255 | uint32_t flags = force? RBD_SNAP_REMOVE_FORCE : 0; | |
256 | int r = 0; | |
257 | utils::ProgressContext pc("Removing snap", no_progress); | |
258 | ||
259 | r = image.snap_remove2(snapname, flags, pc); | |
260 | if (r < 0) { | |
261 | pc.fail(); | |
262 | return r; | |
263 | } | |
264 | ||
265 | pc.finish(); | |
266 | return 0; | |
267 | } | |
268 | ||
269 | int do_rollback_snap(librbd::Image& image, const char *snapname, | |
270 | bool no_progress) | |
271 | { | |
272 | utils::ProgressContext pc("Rolling back to snapshot", no_progress); | |
273 | int r = image.snap_rollback_with_progress(snapname, pc); | |
274 | if (r < 0) { | |
275 | pc.fail(); | |
276 | return r; | |
277 | } | |
278 | pc.finish(); | |
279 | return 0; | |
280 | } | |
281 | ||
282 | int do_purge_snaps(librbd::Image& image, bool no_progress) | |
283 | { | |
284 | utils::ProgressContext pc("Removing all snapshots", no_progress); | |
285 | std::vector<librbd::snap_info_t> snaps; | |
286 | bool is_protected = false; | |
287 | int r = image.snap_list(snaps); | |
288 | if (r < 0) { | |
289 | pc.fail(); | |
290 | return r; | |
291 | } else if (0 == snaps.size()) { | |
292 | return 0; | |
293 | } else { | |
294 | std::list<std::string> protect; | |
295 | snaps.erase(remove_if(snaps.begin(), | |
296 | snaps.end(), | |
297 | boost::bind(utils::is_not_user_snap_namespace, &image, _1)), | |
298 | snaps.end()); | |
299 | for (auto it = snaps.begin(); it != snaps.end();) { | |
300 | r = image.snap_is_protected(it->name.c_str(), &is_protected); | |
301 | if (r < 0) { | |
302 | pc.fail(); | |
303 | return r; | |
304 | } else if (is_protected == true) { | |
305 | protect.push_back(it->name.c_str()); | |
306 | snaps.erase(it); | |
307 | } else { | |
308 | ++it; | |
309 | } | |
310 | } | |
311 | ||
312 | if (!protect.empty()) { | |
313 | std::cout << "rbd: error removing snapshot(s) '" << protect << "', which " | |
314 | << (1 == protect.size() ? "is" : "are") | |
315 | << " protected - these must be unprotected with " | |
316 | << "`rbd snap unprotect`." | |
317 | << std::endl; | |
318 | } | |
319 | for (size_t i = 0; i < snaps.size(); ++i) { | |
320 | r = image.snap_remove(snaps[i].name.c_str()); | |
321 | if (r < 0) { | |
322 | pc.fail(); | |
323 | return r; | |
324 | } | |
325 | pc.update_progress(i + 1, snaps.size() + protect.size()); | |
326 | } | |
327 | ||
328 | if (!protect.empty()) { | |
329 | pc.fail(); | |
330 | } else if (snaps.size() > 0) { | |
331 | pc.finish(); | |
332 | } | |
333 | ||
334 | return 0; | |
335 | } | |
336 | } | |
337 | ||
338 | int do_protect_snap(librbd::Image& image, const char *snapname) | |
339 | { | |
340 | int r = image.snap_protect(snapname); | |
341 | if (r < 0) | |
342 | return r; | |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | int do_unprotect_snap(librbd::Image& image, const char *snapname) | |
348 | { | |
349 | int r = image.snap_unprotect(snapname); | |
350 | if (r < 0) | |
351 | return r; | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
356 | int do_set_limit(librbd::Image& image, uint64_t limit) | |
357 | { | |
358 | return image.snap_set_limit(limit); | |
359 | } | |
360 | ||
361 | void get_list_arguments(po::options_description *positional, | |
362 | po::options_description *options) { | |
363 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
364 | at::add_image_id_option(options); | |
365 | at::add_format_options(options); | |
366 | ||
367 | std::string name = ALL_NAME + ",a"; | |
368 | ||
369 | options->add_options() | |
370 | (name.c_str(), po::bool_switch(), "list snapshots from all namespaces"); | |
371 | } | |
372 | ||
373 | int execute_list(const po::variables_map &vm, | |
374 | const std::vector<std::string> &ceph_global_init_args) { | |
375 | size_t arg_index = 0; | |
376 | std::string pool_name; | |
377 | std::string namespace_name; | |
378 | std::string image_name; | |
379 | std::string snap_name; | |
380 | std::string image_id; | |
381 | ||
382 | if (vm.count(at::IMAGE_ID)) { | |
383 | image_id = vm[at::IMAGE_ID].as<std::string>(); | |
384 | } | |
385 | ||
386 | int r = utils::get_pool_image_snapshot_names( | |
387 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
388 | &image_name, &snap_name, image_id.empty(), | |
389 | utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE); | |
390 | if (r < 0) { | |
391 | return r; | |
392 | } | |
393 | ||
394 | if (!image_id.empty() && !image_name.empty()) { | |
395 | std::cerr << "rbd: trying to access image using both name and id. " | |
396 | << std::endl; | |
397 | return -EINVAL; | |
398 | } | |
399 | ||
400 | at::Format::Formatter formatter; | |
401 | r = utils::get_formatter(vm, &formatter); | |
402 | if (r < 0) { | |
403 | return r; | |
404 | } | |
405 | ||
406 | librados::Rados rados; | |
407 | librados::IoCtx io_ctx; | |
408 | librbd::Image image; | |
409 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, | |
410 | image_id, "", true, &rados, &io_ctx, &image); | |
411 | if (r < 0) { | |
412 | return r; | |
413 | } | |
414 | ||
415 | bool all_snaps = vm[ALL_NAME].as<bool>(); | |
416 | r = do_list_snaps(image, formatter.get(), all_snaps, rados); | |
417 | if (r < 0) { | |
418 | std::cerr << "rbd: failed to list snapshots: " << cpp_strerror(r) | |
419 | << std::endl; | |
420 | return r; | |
421 | } | |
422 | return 0; | |
423 | } | |
424 | ||
425 | void get_create_arguments(po::options_description *positional, | |
426 | po::options_description *options) { | |
427 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
428 | at::add_snap_create_options(options); | |
429 | at::add_no_progress_option(options); | |
430 | } | |
431 | ||
432 | int execute_create(const po::variables_map &vm, | |
433 | const std::vector<std::string> &ceph_global_init_args) { | |
434 | size_t arg_index = 0; | |
435 | std::string pool_name; | |
436 | std::string namespace_name; | |
437 | std::string image_name; | |
438 | std::string snap_name; | |
439 | int r = utils::get_pool_image_snapshot_names( | |
440 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
441 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_REQUIRED, | |
442 | utils::SPEC_VALIDATION_SNAP); | |
443 | if (r < 0) { | |
444 | return r; | |
445 | } | |
446 | ||
447 | uint32_t flags; | |
448 | r = utils::get_snap_create_flags(vm, &flags); | |
449 | if (r < 0) { | |
450 | return r; | |
451 | } | |
452 | ||
453 | librados::Rados rados; | |
454 | librados::IoCtx io_ctx; | |
455 | librbd::Image image; | |
456 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
457 | false, &rados, &io_ctx, &image); | |
458 | if (r < 0) { | |
459 | return r; | |
460 | } | |
461 | ||
462 | r = do_add_snap(image, snap_name.c_str(), flags, | |
463 | vm[at::NO_PROGRESS].as<bool>()); | |
464 | if (r < 0) { | |
465 | std::cerr << "rbd: failed to create snapshot: " << cpp_strerror(r) | |
466 | << std::endl; | |
467 | return r; | |
468 | } | |
469 | return 0; | |
470 | } | |
471 | ||
472 | void get_remove_arguments(po::options_description *positional, | |
473 | po::options_description *options) { | |
474 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
475 | at::add_image_id_option(options); | |
476 | at::add_snap_id_option(options); | |
477 | at::add_no_progress_option(options); | |
478 | ||
479 | options->add_options() | |
480 | ("force", po::bool_switch(), "flatten children and unprotect snapshot if needed."); | |
481 | } | |
482 | ||
483 | int execute_remove(const po::variables_map &vm, | |
484 | const std::vector<std::string> &ceph_global_init_args) { | |
485 | size_t arg_index = 0; | |
486 | std::string pool_name; | |
487 | std::string namespace_name; | |
488 | std::string image_name; | |
489 | std::string snap_name; | |
490 | std::string image_id; | |
491 | uint64_t snap_id = CEPH_NOSNAP; | |
492 | bool force = vm["force"].as<bool>(); | |
493 | bool no_progress = vm[at::NO_PROGRESS].as<bool>(); | |
494 | ||
495 | if (vm.count(at::IMAGE_ID)) { | |
496 | image_id = vm[at::IMAGE_ID].as<std::string>(); | |
497 | } | |
498 | if (vm.count(at::SNAPSHOT_ID)) { | |
499 | snap_id = vm[at::SNAPSHOT_ID].as<uint64_t>(); | |
500 | } | |
501 | ||
502 | int r = utils::get_pool_image_snapshot_names( | |
503 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
504 | &image_name, &snap_name, image_id.empty(), | |
505 | (snap_id == CEPH_NOSNAP ? utils::SNAPSHOT_PRESENCE_REQUIRED : | |
506 | utils::SNAPSHOT_PRESENCE_PERMITTED), | |
507 | utils::SPEC_VALIDATION_NONE); | |
508 | if (r < 0) { | |
509 | return r; | |
510 | } | |
511 | ||
512 | if (!image_id.empty() && !image_name.empty()) { | |
513 | std::cerr << "rbd: trying to access image using both name and id." | |
514 | << std::endl; | |
515 | return -EINVAL; | |
516 | } else if (!snap_name.empty() && snap_id != CEPH_NOSNAP) { | |
517 | std::cerr << "rbd: trying to access snapshot using both name and id." | |
518 | << std::endl; | |
519 | return -EINVAL; | |
520 | } else if ((force || no_progress) && snap_id != CEPH_NOSNAP) { | |
521 | std::cerr << "rbd: force and no-progress options not permitted when " | |
522 | << "removing by id." << std::endl; | |
523 | return -EINVAL; | |
524 | } | |
525 | ||
526 | librados::Rados rados; | |
527 | librados::IoCtx io_ctx; | |
528 | librbd::Image image; | |
529 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
530 | if (r < 0) { | |
531 | return r; | |
532 | } | |
533 | ||
534 | io_ctx.set_pool_full_try(); | |
535 | if (image_id.empty()) { | |
536 | r = utils::open_image(io_ctx, image_name, false, &image); | |
537 | } else { | |
538 | r = utils::open_image_by_id(io_ctx, image_id, false, &image); | |
539 | } | |
540 | if (r < 0) { | |
541 | return r; | |
542 | } | |
543 | ||
544 | if (!snap_name.empty()) { | |
545 | r = do_remove_snap(image, snap_name.c_str(), force, no_progress); | |
546 | } else { | |
547 | r = image.snap_remove_by_id(snap_id); | |
548 | } | |
549 | ||
550 | if (r < 0) { | |
551 | if (r == -EBUSY) { | |
552 | std::cerr << "rbd: snapshot " | |
553 | << (snap_name.empty() ? std::string("id ") + stringify(snap_id) : | |
554 | std::string("'") + snap_name + "'") | |
555 | << " is protected from removal." << std::endl; | |
556 | } else { | |
557 | std::cerr << "rbd: failed to remove snapshot: " << cpp_strerror(r) | |
558 | << std::endl; | |
559 | } | |
560 | return r; | |
561 | } | |
562 | return 0; | |
563 | } | |
564 | ||
565 | void get_purge_arguments(po::options_description *positional, | |
566 | po::options_description *options) { | |
567 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
568 | at::add_image_id_option(options); | |
569 | at::add_no_progress_option(options); | |
570 | } | |
571 | ||
572 | int execute_purge(const po::variables_map &vm, | |
573 | const std::vector<std::string> &ceph_global_init_args) { | |
574 | size_t arg_index = 0; | |
575 | std::string pool_name; | |
576 | std::string namespace_name; | |
577 | std::string image_name; | |
578 | std::string snap_name; | |
579 | std::string image_id; | |
580 | ||
581 | if (vm.count(at::IMAGE_ID)) { | |
582 | image_id = vm[at::IMAGE_ID].as<std::string>(); | |
583 | } | |
584 | ||
585 | int r = utils::get_pool_image_snapshot_names( | |
586 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
587 | &image_name, &snap_name, image_id.empty(), | |
588 | utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE); | |
589 | if (r < 0) { | |
590 | return r; | |
591 | } | |
592 | ||
593 | if (!image_id.empty() && !image_name.empty()) { | |
594 | std::cerr << "rbd: trying to access image using both name and id. " | |
595 | << std::endl; | |
596 | return -EINVAL; | |
597 | } | |
598 | ||
599 | librados::Rados rados; | |
600 | librados::IoCtx io_ctx; | |
601 | librbd::Image image; | |
602 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
603 | if (r < 0) { | |
604 | return r; | |
605 | } | |
606 | ||
607 | io_ctx.set_pool_full_try(); | |
608 | if (image_id.empty()) { | |
609 | r = utils::open_image(io_ctx, image_name, false, &image); | |
610 | } else { | |
611 | r = utils::open_image_by_id(io_ctx, image_id, false, &image); | |
612 | } | |
613 | if (r < 0) { | |
614 | return r; | |
615 | } | |
616 | ||
617 | r = do_purge_snaps(image, vm[at::NO_PROGRESS].as<bool>()); | |
618 | if (r < 0) { | |
619 | if (r != -EBUSY) { | |
620 | std::cerr << "rbd: removing snaps failed: " << cpp_strerror(r) | |
621 | << std::endl; | |
622 | } | |
623 | return r; | |
624 | } | |
625 | return 0; | |
626 | } | |
627 | ||
628 | void get_rollback_arguments(po::options_description *positional, | |
629 | po::options_description *options) { | |
630 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
631 | at::add_no_progress_option(options); | |
632 | } | |
633 | ||
634 | int execute_rollback(const po::variables_map &vm, | |
635 | const std::vector<std::string> &ceph_global_init_args) { | |
636 | size_t arg_index = 0; | |
637 | std::string pool_name; | |
638 | std::string namespace_name; | |
639 | std::string image_name; | |
640 | std::string snap_name; | |
641 | int r = utils::get_pool_image_snapshot_names( | |
642 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
643 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_REQUIRED, | |
644 | utils::SPEC_VALIDATION_NONE); | |
645 | if (r < 0) { | |
646 | return r; | |
647 | } | |
648 | ||
649 | librados::Rados rados; | |
650 | librados::IoCtx io_ctx; | |
651 | librbd::Image image; | |
652 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
653 | false, &rados, &io_ctx, &image); | |
654 | if (r < 0) { | |
655 | return r; | |
656 | } | |
657 | ||
658 | r = do_rollback_snap(image, snap_name.c_str(), | |
659 | vm[at::NO_PROGRESS].as<bool>()); | |
660 | if (r < 0) { | |
661 | std::cerr << "rbd: rollback failed: " << cpp_strerror(r) << std::endl; | |
662 | return r; | |
663 | } | |
664 | return 0; | |
665 | } | |
666 | ||
667 | void get_protect_arguments(po::options_description *positional, | |
668 | po::options_description *options) { | |
669 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
670 | } | |
671 | ||
672 | int execute_protect(const po::variables_map &vm, | |
673 | const std::vector<std::string> &ceph_global_init_args) { | |
674 | size_t arg_index = 0; | |
675 | std::string pool_name; | |
676 | std::string namespace_name; | |
677 | std::string image_name; | |
678 | std::string snap_name; | |
679 | int r = utils::get_pool_image_snapshot_names( | |
680 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
681 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_REQUIRED, | |
682 | utils::SPEC_VALIDATION_NONE); | |
683 | if (r < 0) { | |
684 | return r; | |
685 | } | |
686 | ||
687 | librados::Rados rados; | |
688 | librados::IoCtx io_ctx; | |
689 | librbd::Image image; | |
690 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
691 | false, &rados, &io_ctx, &image); | |
692 | if (r < 0) { | |
693 | return r; | |
694 | } | |
695 | ||
696 | bool is_protected = false; | |
697 | r = image.snap_is_protected(snap_name.c_str(), &is_protected); | |
698 | if (r < 0) { | |
699 | std::cerr << "rbd: protecting snap failed: " << cpp_strerror(r) | |
700 | << std::endl; | |
701 | return r; | |
702 | } else if (is_protected) { | |
703 | std::cerr << "rbd: snap is already protected" << std::endl; | |
704 | return -EBUSY; | |
705 | } | |
706 | ||
707 | r = do_protect_snap(image, snap_name.c_str()); | |
708 | if (r < 0) { | |
709 | std::cerr << "rbd: protecting snap failed: " << cpp_strerror(r) | |
710 | << std::endl; | |
711 | return r; | |
712 | } | |
713 | return 0; | |
714 | } | |
715 | ||
716 | void get_unprotect_arguments(po::options_description *positional, | |
717 | po::options_description *options) { | |
718 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
719 | at::add_image_id_option(options); | |
720 | } | |
721 | ||
722 | int execute_unprotect(const po::variables_map &vm, | |
723 | const std::vector<std::string> &ceph_global_init_args) { | |
724 | size_t arg_index = 0; | |
725 | std::string pool_name; | |
726 | std::string namespace_name; | |
727 | std::string image_name; | |
728 | std::string snap_name; | |
729 | std::string image_id; | |
730 | ||
731 | if (vm.count(at::IMAGE_ID)) { | |
732 | image_id = vm[at::IMAGE_ID].as<std::string>(); | |
733 | } | |
734 | ||
735 | int r = utils::get_pool_image_snapshot_names( | |
736 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
737 | &image_name, &snap_name, image_id.empty(), | |
738 | utils::SNAPSHOT_PRESENCE_REQUIRED, utils::SPEC_VALIDATION_NONE); | |
739 | if (r < 0) { | |
740 | return r; | |
741 | } | |
742 | ||
743 | if (!image_id.empty() && !image_name.empty()) { | |
744 | std::cerr << "rbd: trying to access image using both name and id. " | |
745 | << std::endl; | |
746 | return -EINVAL; | |
747 | } | |
748 | ||
749 | librados::Rados rados; | |
750 | librados::IoCtx io_ctx; | |
751 | librbd::Image image; | |
752 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
753 | if (r < 0) { | |
754 | return r; | |
755 | } | |
756 | ||
757 | io_ctx.set_pool_full_try(); | |
758 | if (image_id.empty()) { | |
759 | r = utils::open_image(io_ctx, image_name, false, &image); | |
760 | } else { | |
761 | r = utils::open_image_by_id(io_ctx, image_id, false, &image); | |
762 | } | |
763 | if (r < 0) { | |
764 | return r; | |
765 | } | |
766 | ||
767 | bool is_protected = false; | |
768 | r = image.snap_is_protected(snap_name.c_str(), &is_protected); | |
769 | if (r < 0) { | |
770 | std::cerr << "rbd: unprotecting snap failed: " << cpp_strerror(r) | |
771 | << std::endl; | |
772 | return r; | |
773 | } else if (!is_protected) { | |
774 | std::cerr << "rbd: snap is already unprotected" << std::endl; | |
775 | return -EINVAL; | |
776 | } | |
777 | ||
778 | r = do_unprotect_snap(image, snap_name.c_str()); | |
779 | if (r < 0) { | |
780 | std::cerr << "rbd: unprotecting snap failed: " << cpp_strerror(r) | |
781 | << std::endl; | |
782 | return r; | |
783 | } | |
784 | return 0; | |
785 | } | |
786 | ||
787 | void get_set_limit_arguments(po::options_description *pos, | |
788 | po::options_description *opt) { | |
789 | at::add_image_spec_options(pos, opt, at::ARGUMENT_MODIFIER_NONE); | |
790 | at::add_limit_option(opt); | |
791 | } | |
792 | ||
793 | int execute_set_limit(const po::variables_map &vm, | |
794 | const std::vector<std::string> &ceph_global_init_args) { | |
795 | size_t arg_index = 0; | |
796 | std::string pool_name; | |
797 | std::string namespace_name; | |
798 | std::string image_name; | |
799 | std::string snap_name; | |
800 | uint64_t limit; | |
801 | ||
802 | int r = utils::get_pool_image_snapshot_names( | |
803 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
804 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE, | |
805 | utils::SPEC_VALIDATION_NONE); | |
806 | if (r < 0) { | |
807 | return r; | |
808 | } | |
809 | ||
810 | if (vm.count(at::LIMIT)) { | |
811 | limit = vm[at::LIMIT].as<uint64_t>(); | |
812 | } else { | |
813 | std::cerr << "rbd: must specify --limit <num>" << std::endl; | |
814 | return -ERANGE; | |
815 | } | |
816 | ||
817 | librados::Rados rados; | |
818 | librados::IoCtx io_ctx; | |
819 | librbd::Image image; | |
820 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
821 | false, &rados, &io_ctx, &image); | |
822 | if (r < 0) { | |
823 | return r; | |
824 | } | |
825 | ||
826 | r = do_set_limit(image, limit); | |
827 | if (r < 0) { | |
828 | std::cerr << "rbd: setting snapshot limit failed: " << cpp_strerror(r) | |
829 | << std::endl; | |
830 | return r; | |
831 | } | |
832 | return 0; | |
833 | } | |
834 | ||
835 | void get_clear_limit_arguments(po::options_description *pos, | |
836 | po::options_description *opt) { | |
837 | at::add_image_spec_options(pos, opt, at::ARGUMENT_MODIFIER_NONE); | |
838 | } | |
839 | ||
840 | int execute_clear_limit(const po::variables_map &vm, | |
841 | const std::vector<std::string> &ceph_global_init_args) { | |
842 | size_t arg_index = 0; | |
843 | std::string pool_name; | |
844 | std::string namespace_name; | |
845 | std::string image_name; | |
846 | std::string snap_name; | |
847 | ||
848 | int r = utils::get_pool_image_snapshot_names( | |
849 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
850 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE, | |
851 | utils::SPEC_VALIDATION_NONE); | |
852 | if (r < 0) { | |
853 | return r; | |
854 | } | |
855 | ||
856 | librados::Rados rados; | |
857 | librados::IoCtx io_ctx; | |
858 | librbd::Image image; | |
859 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
860 | false, &rados, &io_ctx, &image); | |
861 | if (r < 0) { | |
862 | return r; | |
863 | } | |
864 | ||
865 | r = do_set_limit(image, UINT64_MAX); | |
866 | if (r < 0) { | |
867 | std::cerr << "rbd: clearing snapshot limit failed: " << cpp_strerror(r) | |
868 | << std::endl; | |
869 | return r; | |
870 | } | |
871 | return 0; | |
872 | } | |
873 | ||
874 | void get_rename_arguments(po::options_description *positional, | |
875 | po::options_description *options) { | |
876 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE); | |
877 | at::add_snap_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST); | |
878 | } | |
879 | ||
880 | int execute_rename(const po::variables_map &vm, | |
881 | const std::vector<std::string> &ceph_global_init_args) { | |
882 | size_t arg_index = 0; | |
883 | std::string pool_name; | |
884 | std::string namespace_name; | |
885 | std::string image_name; | |
886 | std::string src_snap_name; | |
887 | int r = utils::get_pool_image_snapshot_names( | |
888 | vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, | |
889 | &image_name, &src_snap_name, true, utils::SNAPSHOT_PRESENCE_REQUIRED, | |
890 | utils::SPEC_VALIDATION_NONE); | |
891 | if (r < 0) { | |
892 | return -r; | |
893 | } | |
894 | ||
895 | std::string dest_pool_name(pool_name); | |
896 | std::string dest_namespace_name(namespace_name); | |
897 | std::string dest_image_name; | |
898 | std::string dest_snap_name; | |
899 | r = utils::get_pool_image_snapshot_names( | |
900 | vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dest_pool_name, | |
901 | &dest_namespace_name, &dest_image_name, &dest_snap_name, true, | |
902 | utils::SNAPSHOT_PRESENCE_REQUIRED, utils::SPEC_VALIDATION_SNAP); | |
903 | if (r < 0) { | |
904 | return -r; | |
905 | } | |
906 | ||
907 | if (pool_name != dest_pool_name) { | |
908 | std::cerr << "rbd: source and destination pool must be the same" | |
909 | << std::endl; | |
910 | return -EINVAL; | |
911 | } else if (namespace_name != dest_namespace_name) { | |
912 | std::cerr << "rbd: source and destination namespace must be the same" | |
913 | << std::endl; | |
914 | return -EINVAL; | |
915 | } else if (image_name != dest_image_name) { | |
916 | std::cerr << "rbd: source and destination image name must be the same" | |
917 | << std::endl; | |
918 | return -EINVAL; | |
919 | } | |
920 | ||
921 | librados::Rados rados; | |
922 | librados::IoCtx io_ctx; | |
923 | librbd::Image image; | |
924 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", | |
925 | false, &rados, &io_ctx, &image); | |
926 | if (r < 0) { | |
927 | return r; | |
928 | } | |
929 | ||
930 | r = image.snap_rename(src_snap_name.c_str(), dest_snap_name.c_str()); | |
931 | if (r < 0) { | |
932 | std::cerr << "rbd: renaming snap failed: " << cpp_strerror(r) | |
933 | << std::endl; | |
934 | return r; | |
935 | } | |
936 | return 0; | |
937 | } | |
938 | ||
939 | Shell::Action action_list( | |
940 | {"snap", "list"}, {"snap", "ls"}, "Dump list of image snapshots.", "", | |
941 | &get_list_arguments, &execute_list); | |
942 | Shell::Action action_create( | |
943 | {"snap", "create"}, {"snap", "add"}, "Create a snapshot.", "", | |
944 | &get_create_arguments, &execute_create); | |
945 | Shell::Action action_remove( | |
946 | {"snap", "remove"}, {"snap", "rm"}, "Delete a snapshot.", "", | |
947 | &get_remove_arguments, &execute_remove); | |
948 | Shell::Action action_purge( | |
949 | {"snap", "purge"}, {}, "Delete all unprotected snapshots.", "", | |
950 | &get_purge_arguments, &execute_purge); | |
951 | Shell::Action action_rollback( | |
952 | {"snap", "rollback"}, {"snap", "revert"}, "Rollback image to snapshot.", "", | |
953 | &get_rollback_arguments, &execute_rollback); | |
954 | Shell::Action action_protect( | |
955 | {"snap", "protect"}, {}, "Prevent a snapshot from being deleted.", "", | |
956 | &get_protect_arguments, &execute_protect); | |
957 | Shell::Action action_unprotect( | |
958 | {"snap", "unprotect"}, {}, "Allow a snapshot to be deleted.", "", | |
959 | &get_unprotect_arguments, &execute_unprotect); | |
960 | Shell::Action action_set_limit( | |
961 | {"snap", "limit", "set"}, {}, "Limit the number of snapshots.", "", | |
962 | &get_set_limit_arguments, &execute_set_limit); | |
963 | Shell::Action action_clear_limit( | |
964 | {"snap", "limit", "clear"}, {}, "Remove snapshot limit.", "", | |
965 | &get_clear_limit_arguments, &execute_clear_limit); | |
966 | Shell::Action action_rename( | |
967 | {"snap", "rename"}, {}, "Rename a snapshot.", "", | |
968 | &get_rename_arguments, &execute_rename); | |
969 | ||
970 | } // namespace snap | |
971 | } // namespace action | |
972 | } // namespace rbd |