]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/action/Trash.cc
update sources to v12.1.2
[ceph.git] / ceph / src / tools / rbd / action / Trash.cc
CommitLineData
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 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2017 SUSE LINUX GmbH
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include "tools/rbd/ArgumentTypes.h"
16#include "tools/rbd/Shell.h"
17#include "tools/rbd/Utils.h"
18#include "common/errno.h"
19#include "include/stringify.h"
20#include "common/Formatter.h"
21#include "common/TextTable.h"
22#include "common/Clock.h"
23#include <iostream>
24#include <sstream>
25#include <boost/program_options.hpp>
26
27namespace rbd {
28namespace action {
29namespace trash {
30
31namespace at = argument_types;
32namespace po = boost::program_options;
33
34
35void get_move_arguments(po::options_description *positional,
36 po::options_description *options) {
37 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
38 options->add_options()
39 (at::DELAY.c_str(), po::value<uint64_t>(),
40 "time delay in seconds until effectively remove the image");
41}
42
43int execute_move(const po::variables_map &vm) {
44 size_t arg_index = 0;
45 std::string pool_name;
46 std::string image_name;
47 std::string snap_name;
48
49 int r = utils::get_pool_image_snapshot_names(
50 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
51 &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
52 if (r < 0) {
53 return r;
54 }
55
56 librados::Rados rados;
57 librados::IoCtx io_ctx;
58 r = utils::init(pool_name, &rados, &io_ctx);
59 if (r < 0) {
60 return r;
61 }
62
63 uint64_t delay = 0;
64 if (vm.find(at::DELAY) != vm.end()) {
65 delay = vm[at::DELAY].as<uint64_t>();
66 }
67
68 librbd::RBD rbd;
69 r = rbd.trash_move(io_ctx, image_name.c_str(), delay);
70 if (r < 0) {
71 std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
72 << std::endl;
73 }
74
75 return r;
76
77}
78
79void get_remove_arguments(po::options_description *positional,
80 po::options_description *options) {
81 positional->add_options()
82 (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
83 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
84 at::add_image_id_option(options);
85
86 at::add_no_progress_option(options);
87 options->add_options()
88 ("force", po::bool_switch(), "force remove of non-expired delayed images");
89}
90
91int execute_remove(const po::variables_map &vm) {
92 size_t arg_index = 0;
93 std::string pool_name;
94 std::string image_id;
95 int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
96 if (r < 0) {
97 return r;
98 }
99
100 librados::Rados rados;
101 librados::IoCtx io_ctx;
102 r = utils::init(pool_name, &rados, &io_ctx);
103 if (r < 0) {
104 return r;
105 }
106
107 librbd::RBD rbd;
108
109 utils::ProgressContext pc("Removing image", vm[at::NO_PROGRESS].as<bool>());
110 r = rbd.trash_remove_with_progress(io_ctx, image_id.c_str(),
111 vm["force"].as<bool>(), pc);
112 if (r < 0) {
113 if (r == -ENOTEMPTY) {
114 std::cerr << "rbd: image has snapshots - these must be deleted"
115 << " with 'rbd snap purge' before the image can be removed."
116 << std::endl;
117 } else if (r == -EBUSY) {
118 std::cerr << "rbd: error: image still has watchers"
119 << std::endl
120 << "This means the image is still open or the client using "
121 << "it crashed. Try again after closing/unmapping it or "
122 << "waiting 30s for the crashed client to timeout."
123 << std::endl;
124 } else if (r == -EMLINK) {
125 std::cerr << std::endl
126 << "Remove the image from the consistency group and try again."
127 << std::endl;
128 } else if (r == -EPERM) {
129 std::cerr << std::endl
130 << "Deferment time has not expired, please use --force if you "
131 << "really want to remove the image"
132 << std::endl;
133 } else {
134 std::cerr << "rbd: remove error: " << cpp_strerror(r) << std::endl;
135 }
136 pc.fail();
137 return r;
138 }
139
140 pc.finish();
141
142 return r;
143}
144
145std::string delete_status(time_t deferment_end_time) {
146 time_t now = ceph_clock_gettime();
147
148 std::string time_str = ctime(&deferment_end_time);
149 time_str = time_str.substr(0, time_str.length() - 1);
150
151 std::stringstream ss;
152 if (now < deferment_end_time) {
153 ss << "protected until " << time_str;
154 }
155
156 return ss.str();
157}
158
159int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
160 bool all_flag, Formatter *f) {
161 std::vector<librbd::trash_image_info_t> trash_entries;
162 int r = rbd.trash_list(io_ctx, trash_entries);
31f18b77 163 if (r < 0) {
7c673cae
FG
164 return r;
165 }
7c673cae
FG
166
167 if (!long_flag) {
168 if (f) {
169 f->open_array_section("trash");
170 }
171 for (const auto& entry : trash_entries) {
172 if (!all_flag &&
173 entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
174 continue;
175 }
176 if (f) {
177 f->dump_string("id", entry.id);
178 f->dump_string("name", entry.name);
179 } else {
180 std::cout << entry.id << " " << entry.name << std::endl;
181 }
182 }
183 if (f) {
184 f->close_section();
185 f->flush(std::cout);
186 }
187 return 0;
188 }
189
190 TextTable tbl;
191
192 if (f) {
193 f->open_array_section("trash");
194 } else {
195 tbl.define_column("ID", TextTable::LEFT, TextTable::LEFT);
196 tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
197 tbl.define_column("SOURCE", TextTable::LEFT, TextTable::LEFT);
198 tbl.define_column("DELETED_AT", TextTable::LEFT, TextTable::LEFT);
199 tbl.define_column("STATUS", TextTable::LEFT, TextTable::LEFT);
200 }
201
202 for (const auto& entry : trash_entries) {
203 if (!all_flag &&
204 entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
205 continue;
206 }
207 librbd::Image im;
208
209 r = rbd.open_by_id_read_only(io_ctx, im, entry.id.c_str(), NULL);
210 // image might disappear between rbd.list() and rbd.open(); ignore
211 // that, warn about other possible errors (EPERM, say, for opening
212 // an old-format image, because you need execute permission for the
213 // class method)
214 if (r < 0) {
215 if (r != -ENOENT) {
216 std::cerr << "rbd: error opening " << entry.id << ": "
217 << cpp_strerror(r) << std::endl;
218 }
219 // in any event, continue to next image
220 continue;
221 }
222
223 std::string del_source;
224 switch (entry.source) {
225 case RBD_TRASH_IMAGE_SOURCE_USER:
226 del_source = "USER";
227 break;
228 case RBD_TRASH_IMAGE_SOURCE_MIRRORING:
229 del_source = "MIRRORING";
230 break;
231 }
232
233 std::string time_str = ctime(&entry.deletion_time);
234 time_str = time_str.substr(0, time_str.length() - 1);
235
236 if (f) {
237 f->open_object_section("image");
238 f->dump_string("id", entry.id);
239 f->dump_string("name", entry.name);
240 f->dump_string("source", del_source);
241 f->dump_string("deleted_at", time_str);
242 f->dump_string("status",
243 delete_status(entry.deferment_end_time));
244 f->close_section();
245 } else {
246 tbl << entry.id
247 << entry.name
248 << del_source
249 << time_str
250 << delete_status(entry.deferment_end_time)
251 << TextTable::endrow;
252 }
253 }
254
255 if (f) {
256 f->close_section();
257 f->flush(std::cout);
258 } else if (!trash_entries.empty()) {
259 std::cout << tbl;
260 }
261
262 return r < 0 ? r : 0;
263}
264
265void get_list_arguments(po::options_description *positional,
266 po::options_description *options) {
267 at::add_pool_options(positional, options);
268 options->add_options()
269 ("all,a", po::bool_switch(), "list images from all sources");
270 options->add_options()
271 ("long,l", po::bool_switch(), "long listing format");
272 at::add_format_options(options);
273}
274
275int execute_list(const po::variables_map &vm) {
276 size_t arg_index = 0;
277 std::string pool_name = utils::get_pool_name(vm, &arg_index);
278
279 at::Format::Formatter formatter;
280 int r = utils::get_formatter(vm, &formatter);
281 if (r < 0) {
282 return r;
283 }
284
285 librados::Rados rados;
286 librados::IoCtx io_ctx;
287 r = utils::init(pool_name, &rados, &io_ctx);
288 if (r < 0) {
289 return r;
290 }
291
292 librbd::RBD rbd;
293 r = do_list(rbd, io_ctx, vm["long"].as<bool>(), vm["all"].as<bool>(),
294 formatter.get());
295 if (r < 0) {
296 std::cerr << "rbd: trash list: " << cpp_strerror(r) << std::endl;
297 return r;
298 }
299
300 return 0;
301}
302
303void get_restore_arguments(po::options_description *positional,
304 po::options_description *options) {
305 positional->add_options()
306 (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
307 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
308 at::add_image_id_option(options);
309 at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE, "");
310}
311
312int execute_restore(const po::variables_map &vm) {
313 size_t arg_index = 0;
314 std::string pool_name;
315 std::string image_id;
316 int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
317 if (r < 0) {
318 return r;
319 }
320
321 librados::Rados rados;
322 librados::IoCtx io_ctx;
323 r = utils::init(pool_name, &rados, &io_ctx);
324 if (r < 0) {
325 return r;
326 }
327
328 std::string name;
329 if (vm.find(at::IMAGE_NAME) != vm.end()) {
330 name = vm[at::IMAGE_NAME].as<std::string>();
331 }
332
333 librbd::RBD rbd;
334 r = rbd.trash_restore(io_ctx, image_id.c_str(), name.c_str());
335 if (r < 0) {
336 if (r == -ENOENT) {
337 std::cerr << "rbd: error: image does not exist in trash"
338 << std::endl;
339 } else if (r == -EEXIST) {
340 std::cerr << "rbd: error: an image with the same name already exists, "
341 << "try again with with a different name"
342 << std::endl;
343 } else {
344 std::cerr << "rbd: restore error: " << cpp_strerror(r) << std::endl;
345 }
346 return r;
347 }
348
349 return r;
350}
351
352
353Shell::Action action_move(
c07f9fc5 354 {"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
7c673cae
FG
355 &get_move_arguments, &execute_move);
356
357Shell::Action action_remove(
c07f9fc5 358 {"trash", "remove"}, {"trash", "rm"}, "Remove an image from trash.", "",
7c673cae
FG
359 &get_remove_arguments, &execute_remove);
360
361Shell::SwitchArguments switched_arguments({"long", "l"});
362Shell::Action action_list(
363 {"trash", "list"}, {"trash", "ls"}, "List trash images.", "",
364 &get_list_arguments, &execute_list);
365
366Shell::Action action_restore(
c07f9fc5 367 {"trash", "restore"}, {}, "Restore an image from trash.", "",
7c673cae
FG
368 &get_restore_arguments, &execute_restore);
369
370} // namespace trash
371} // namespace action
372} // namespace rbd