Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / action / Trash.cc
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
27 namespace rbd {
28 namespace action {
29 namespace trash {
30
31 namespace at = argument_types;
32 namespace po = boost::program_options;
33
34
35 void 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
43 int 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
79 void 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
91 int 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
145 std::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
159 int 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);
163   if (r < 0) {
164     return r;
165   }
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
265 void 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
275 int 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
303 void 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
312 int 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
353 Shell::Action action_move(
354     {"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
355     &get_move_arguments, &execute_move);
356
357 Shell::Action action_remove(
358   {"trash", "remove"}, {"trash", "rm"}, "Remove an image from trash.", "",
359   &get_remove_arguments, &execute_remove);
360
361 Shell::SwitchArguments switched_arguments({"long", "l"});
362 Shell::Action action_list(
363   {"trash", "list"}, {"trash", "ls"}, "List trash images.", "",
364   &get_list_arguments, &execute_list);
365
366 Shell::Action action_restore(
367     {"trash", "restore"}, {}, "Restore an image from trash.", "",
368     &get_restore_arguments, &execute_restore);
369
370 } // namespace trash
371 } // namespace action
372 } // namespace rbd