]>
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 "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> | |
f67539c2 | 14 | #include <boost/bind/bind.hpp> |
7c673cae FG |
15 | |
16 | namespace rbd { | |
17 | namespace action { | |
18 | namespace snap { | |
19 | ||
f67539c2 TL |
20 | using namespace boost::placeholders; |
21 | ||
11fdf7f2 TL |
22 | static const std::string ALL_NAME("all"); |
23 | ||
7c673cae FG |
24 | namespace at = argument_types; |
25 | namespace po = boost::program_options; | |
26 | ||
11fdf7f2 | 27 | int do_list_snaps(librbd::Image& image, Formatter *f, bool all_snaps, librados::Rados& rados) |
7c673cae FG |
28 | { |
29 | std::vector<librbd::snap_info_t> snaps; | |
30 | TextTable t; | |
31 | int r; | |
32 | ||
33 | r = image.snap_list(snaps); | |
11fdf7f2 TL |
34 | if (r < 0) { |
35 | std::cerr << "rbd: unable to list snapshots" << std::endl; | |
7c673cae | 36 | return r; |
11fdf7f2 TL |
37 | } |
38 | ||
9f95a23c | 39 | librbd::image_info_t info; |
11fdf7f2 TL |
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()); | |
9f95a23c TL |
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 | } | |
11fdf7f2 | 51 | } |
7c673cae FG |
52 | |
53 | if (f) { | |
54 | f->open_array_section("snapshots"); | |
55 | } else { | |
11fdf7f2 | 56 | t.define_column("SNAPID", TextTable::LEFT, TextTable::RIGHT); |
7c673cae | 57 | t.define_column("NAME", TextTable::LEFT, TextTable::LEFT); |
11fdf7f2 TL |
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 | } | |
7c673cae FG |
64 | } |
65 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
70 | for (std::vector<librbd::snap_info_t>::iterator s = snaps.begin(); |
71 | s != snaps.end(); ++s) { | |
72 | struct timespec timestamp; | |
11fdf7f2 | 73 | bool snap_protected = false; |
7c673cae | 74 | image.snap_get_timestamp(s->id, ×tamp); |
20effc67 | 75 | std::string tt_str = ""; |
7c673cae FG |
76 | if(timestamp.tv_sec != 0) { |
77 | time_t tt = timestamp.tv_sec; | |
78 | tt_str = ctime(&tt); | |
11fdf7f2 TL |
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; | |
9f95a23c TL |
100 | case RBD_SNAP_NAMESPACE_TYPE_MIRROR: |
101 | snap_namespace_name = "mirror"; | |
102 | break; | |
11fdf7f2 TL |
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; | |
9f95a23c TL |
109 | int get_mirror_res = -ENOENT; |
110 | librbd::snap_mirror_namespace_t mirror_snap; | |
111 | std::string mirror_snap_state = "unknown"; | |
11fdf7f2 TL |
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); | |
9f95a23c TL |
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 | } | |
11fdf7f2 TL |
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 | } | |
7c673cae FG |
143 | } |
144 | ||
145 | if (f) { | |
11fdf7f2 | 146 | protected_str = snap_protected ? "true" : "false"; |
7c673cae FG |
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); | |
11fdf7f2 | 151 | f->dump_string("protected", protected_str); |
7c673cae | 152 | f->dump_string("timestamp", tt_str); |
11fdf7f2 | 153 | if (all_snaps) { |
9f95a23c | 154 | f->open_object_section("namespace"); |
11fdf7f2 | 155 | f->dump_string("type", snap_namespace_name); |
9f95a23c TL |
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) { | |
11fdf7f2 | 162 | f->dump_string("original_name", trash_original_name); |
9f95a23c TL |
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 | } | |
11fdf7f2 | 180 | } |
9f95a23c | 181 | f->close_section(); |
11fdf7f2 | 182 | } |
7c673cae FG |
183 | f->close_section(); |
184 | } else { | |
11fdf7f2 TL |
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) { | |
20effc67 | 189 | std::ostringstream oss; |
11fdf7f2 TL |
190 | oss << snap_namespace_name; |
191 | ||
192 | if (get_group_res == 0) { | |
9f95a23c TL |
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 << ")"; | |
11fdf7f2 TL |
197 | } else if (get_trash_res == 0) { |
198 | oss << " (" << trash_original_name << ")"; | |
9f95a23c TL |
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) { | |
1911f103 TL |
204 | oss << " " << mirror_snap.primary_mirror_uuid << ":" |
205 | << mirror_snap.primary_snap_id << " "; | |
9f95a23c TL |
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 << ")"; | |
11fdf7f2 TL |
219 | } |
220 | ||
9f95a23c | 221 | t << oss.str(); |
11fdf7f2 TL |
222 | } |
223 | t << TextTable::endrow; | |
7c673cae FG |
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 | ||
f67539c2 TL |
237 | int do_add_snap(librbd::Image& image, const char *snapname, |
238 | uint32_t flags, bool no_progress) | |
7c673cae | 239 | { |
f67539c2 TL |
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(); | |
7c673cae | 245 | return r; |
f67539c2 | 246 | } |
7c673cae | 247 | |
f67539c2 | 248 | pc.finish(); |
7c673cae FG |
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); | |
11fdf7f2 | 258 | |
7c673cae FG |
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 { | |
20effc67 | 294 | std::list<std::string> protect; |
11fdf7f2 TL |
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); | |
7c673cae FG |
301 | if (r < 0) { |
302 | pc.fail(); | |
303 | return r; | |
304 | } else if (is_protected == true) { | |
11fdf7f2 TL |
305 | protect.push_back(it->name.c_str()); |
306 | snaps.erase(it); | |
307 | } else { | |
308 | ++it; | |
7c673cae FG |
309 | } |
310 | } | |
11fdf7f2 TL |
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 | } | |
7c673cae FG |
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 | } | |
11fdf7f2 TL |
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(); | |
7c673cae FG |
332 | } |
333 | ||
7c673cae FG |
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 | ||
7c673cae FG |
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); | |
11fdf7f2 TL |
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"); | |
7c673cae FG |
371 | } |
372 | ||
11fdf7f2 TL |
373 | int execute_list(const po::variables_map &vm, |
374 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
375 | size_t arg_index = 0; |
376 | std::string pool_name; | |
11fdf7f2 | 377 | std::string namespace_name; |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | } | |
7c673cae | 393 | |
11fdf7f2 | 394 | if (!image_id.empty() && !image_name.empty()) { |
7c673cae FG |
395 | std::cerr << "rbd: trying to access image using both name and id. " |
396 | << std::endl; | |
397 | return -EINVAL; | |
398 | } | |
399 | ||
7c673cae FG |
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; | |
11fdf7f2 TL |
409 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, |
410 | image_id, "", true, &rados, &io_ctx, &image); | |
7c673cae FG |
411 | if (r < 0) { |
412 | return r; | |
413 | } | |
414 | ||
11fdf7f2 TL |
415 | bool all_snaps = vm[ALL_NAME].as<bool>(); |
416 | r = do_list_snaps(image, formatter.get(), all_snaps, rados); | |
7c673cae | 417 | if (r < 0) { |
20effc67 | 418 | std::cerr << "rbd: failed to list snapshots: " << cpp_strerror(r) |
7c673cae FG |
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); | |
f67539c2 TL |
428 | at::add_snap_create_options(options); |
429 | at::add_no_progress_option(options); | |
7c673cae FG |
430 | } |
431 | ||
11fdf7f2 TL |
432 | int execute_create(const po::variables_map &vm, |
433 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
434 | size_t arg_index = 0; |
435 | std::string pool_name; | |
11fdf7f2 | 436 | std::string namespace_name; |
7c673cae FG |
437 | std::string image_name; |
438 | std::string snap_name; | |
439 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
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); | |
7c673cae FG |
443 | if (r < 0) { |
444 | return r; | |
445 | } | |
446 | ||
f67539c2 TL |
447 | uint32_t flags; |
448 | r = utils::get_snap_create_flags(vm, &flags); | |
449 | if (r < 0) { | |
450 | return r; | |
451 | } | |
452 | ||
7c673cae FG |
453 | librados::Rados rados; |
454 | librados::IoCtx io_ctx; | |
455 | librbd::Image image; | |
11fdf7f2 TL |
456 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
457 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
458 | if (r < 0) { |
459 | return r; | |
460 | } | |
461 | ||
f67539c2 TL |
462 | r = do_add_snap(image, snap_name.c_str(), flags, |
463 | vm[at::NO_PROGRESS].as<bool>()); | |
7c673cae | 464 | if (r < 0) { |
20effc67 TL |
465 | std::cerr << "rbd: failed to create snapshot: " << cpp_strerror(r) |
466 | << std::endl; | |
7c673cae FG |
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); | |
7c673cae | 475 | at::add_image_id_option(options); |
11fdf7f2 TL |
476 | at::add_snap_id_option(options); |
477 | at::add_no_progress_option(options); | |
7c673cae FG |
478 | |
479 | options->add_options() | |
480 | ("force", po::bool_switch(), "flatten children and unprotect snapshot if needed."); | |
481 | } | |
482 | ||
11fdf7f2 TL |
483 | int execute_remove(const po::variables_map &vm, |
484 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
485 | size_t arg_index = 0; |
486 | std::string pool_name; | |
11fdf7f2 | 487 | std::string namespace_name; |
7c673cae FG |
488 | std::string image_name; |
489 | std::string snap_name; | |
490 | std::string image_id; | |
11fdf7f2 | 491 | uint64_t snap_id = CEPH_NOSNAP; |
7c673cae | 492 | bool force = vm["force"].as<bool>(); |
11fdf7f2 | 493 | bool no_progress = vm[at::NO_PROGRESS].as<bool>(); |
7c673cae FG |
494 | |
495 | if (vm.count(at::IMAGE_ID)) { | |
496 | image_id = vm[at::IMAGE_ID].as<std::string>(); | |
497 | } | |
11fdf7f2 TL |
498 | if (vm.count(at::SNAPSHOT_ID)) { |
499 | snap_id = vm[at::SNAPSHOT_ID].as<uint64_t>(); | |
7c673cae FG |
500 | } |
501 | ||
11fdf7f2 TL |
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); | |
7c673cae FG |
508 | if (r < 0) { |
509 | return r; | |
510 | } | |
511 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
526 | librados::Rados rados; |
527 | librados::IoCtx io_ctx; | |
528 | librbd::Image image; | |
11fdf7f2 | 529 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); |
7c673cae FG |
530 | if (r < 0) { |
531 | return r; | |
532 | } | |
533 | ||
9f95a23c | 534 | io_ctx.set_pool_full_try(); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
550 | if (r < 0) { |
551 | if (r == -EBUSY) { | |
11fdf7f2 TL |
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; | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
572 | int execute_purge(const po::variables_map &vm, |
573 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
574 | size_t arg_index = 0; |
575 | std::string pool_name; | |
11fdf7f2 | 576 | std::string namespace_name; |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | } | |
7c673cae | 592 | |
11fdf7f2 | 593 | if (!image_id.empty() && !image_name.empty()) { |
7c673cae FG |
594 | std::cerr << "rbd: trying to access image using both name and id. " |
595 | << std::endl; | |
596 | return -EINVAL; | |
597 | } | |
598 | ||
7c673cae FG |
599 | librados::Rados rados; |
600 | librados::IoCtx io_ctx; | |
601 | librbd::Image image; | |
11fdf7f2 | 602 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); |
7c673cae FG |
603 | if (r < 0) { |
604 | return r; | |
605 | } | |
606 | ||
9f95a23c | 607 | io_ctx.set_pool_full_try(); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
634 | int execute_rollback(const po::variables_map &vm, |
635 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
636 | size_t arg_index = 0; |
637 | std::string pool_name; | |
11fdf7f2 | 638 | std::string namespace_name; |
7c673cae FG |
639 | std::string image_name; |
640 | std::string snap_name; | |
641 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
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); | |
7c673cae FG |
645 | if (r < 0) { |
646 | return r; | |
647 | } | |
648 | ||
649 | librados::Rados rados; | |
650 | librados::IoCtx io_ctx; | |
651 | librbd::Image image; | |
11fdf7f2 TL |
652 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
653 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
672 | int execute_protect(const po::variables_map &vm, |
673 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
674 | size_t arg_index = 0; |
675 | std::string pool_name; | |
11fdf7f2 | 676 | std::string namespace_name; |
7c673cae FG |
677 | std::string image_name; |
678 | std::string snap_name; | |
679 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
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); | |
7c673cae FG |
683 | if (r < 0) { |
684 | return r; | |
685 | } | |
686 | ||
687 | librados::Rados rados; | |
688 | librados::IoCtx io_ctx; | |
689 | librbd::Image image; | |
11fdf7f2 TL |
690 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
691 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
722 | int execute_unprotect(const po::variables_map &vm, |
723 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
724 | size_t arg_index = 0; |
725 | std::string pool_name; | |
11fdf7f2 | 726 | std::string namespace_name; |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | } | |
7c673cae | 742 | |
11fdf7f2 | 743 | if (!image_id.empty() && !image_name.empty()) { |
7c673cae FG |
744 | std::cerr << "rbd: trying to access image using both name and id. " |
745 | << std::endl; | |
746 | return -EINVAL; | |
747 | } | |
748 | ||
7c673cae FG |
749 | librados::Rados rados; |
750 | librados::IoCtx io_ctx; | |
751 | librbd::Image image; | |
11fdf7f2 | 752 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); |
7c673cae FG |
753 | if (r < 0) { |
754 | return r; | |
755 | } | |
756 | ||
9f95a23c | 757 | io_ctx.set_pool_full_try(); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
793 | int execute_set_limit(const po::variables_map &vm, |
794 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
795 | size_t arg_index = 0; |
796 | std::string pool_name; | |
11fdf7f2 | 797 | std::string namespace_name; |
7c673cae FG |
798 | std::string image_name; |
799 | std::string snap_name; | |
800 | uint64_t limit; | |
801 | ||
802 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
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); | |
7c673cae FG |
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; | |
11fdf7f2 TL |
820 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
821 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
840 | int execute_clear_limit(const po::variables_map &vm, |
841 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
842 | size_t arg_index = 0; |
843 | std::string pool_name; | |
11fdf7f2 | 844 | std::string namespace_name; |
7c673cae FG |
845 | std::string image_name; |
846 | std::string snap_name; | |
847 | ||
848 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
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); | |
7c673cae FG |
852 | if (r < 0) { |
853 | return r; | |
854 | } | |
855 | ||
856 | librados::Rados rados; | |
857 | librados::IoCtx io_ctx; | |
858 | librbd::Image image; | |
11fdf7f2 TL |
859 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
860 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
861 | if (r < 0) { |
862 | return r; | |
863 | } | |
864 | ||
11fdf7f2 | 865 | r = do_set_limit(image, UINT64_MAX); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
880 | int execute_rename(const po::variables_map &vm, |
881 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
882 | size_t arg_index = 0; |
883 | std::string pool_name; | |
11fdf7f2 | 884 | std::string namespace_name; |
7c673cae FG |
885 | std::string image_name; |
886 | std::string src_snap_name; | |
887 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
888 | vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, |
889 | &image_name, &src_snap_name, true, utils::SNAPSHOT_PRESENCE_REQUIRED, | |
7c673cae FG |
890 | utils::SPEC_VALIDATION_NONE); |
891 | if (r < 0) { | |
892 | return -r; | |
893 | } | |
894 | ||
895 | std::string dest_pool_name(pool_name); | |
11fdf7f2 | 896 | std::string dest_namespace_name(namespace_name); |
7c673cae FG |
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, | |
11fdf7f2 TL |
901 | &dest_namespace_name, &dest_image_name, &dest_snap_name, true, |
902 | utils::SNAPSHOT_PRESENCE_REQUIRED, utils::SPEC_VALIDATION_SNAP); | |
7c673cae FG |
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; | |
11fdf7f2 TL |
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; | |
7c673cae FG |
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 | } | |
11fdf7f2 | 920 | |
7c673cae FG |
921 | librados::Rados rados; |
922 | librados::IoCtx io_ctx; | |
923 | librbd::Image image; | |
11fdf7f2 TL |
924 | r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "", |
925 | false, &rados, &io_ctx, &image); | |
7c673cae FG |
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( | |
c07f9fc5 | 946 | {"snap", "remove"}, {"snap", "rm"}, "Delete a snapshot.", "", |
7c673cae FG |
947 | &get_remove_arguments, &execute_remove); |
948 | Shell::Action action_purge( | |
11fdf7f2 | 949 | {"snap", "purge"}, {}, "Delete all unprotected snapshots.", "", |
7c673cae FG |
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 |