]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/tools/rbd/action/Trash.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / tools / rbd / action / Trash.cc
index 136ebfacda21eecc0234ff8a01a14196020da002..327b20ba703b9a400f254c72b11c0b5a3179bfac 100644 (file)
@@ -23,6 +23,7 @@
 #include <iostream>
 #include <sstream>
 #include <boost/program_options.hpp>
+#include <boost/bind.hpp>
 
 namespace rbd {
 namespace action {
@@ -31,56 +32,82 @@ namespace trash {
 namespace at = argument_types;
 namespace po = boost::program_options;
 
+//Optional arguments used only by this set of commands (rbd trash *)
+static const std::string EXPIRES_AT("expires-at");
+static const std::string EXPIRED_BEFORE("expired-before");
+static const std::string THRESHOLD("threshold");
+
+static bool is_not_trash_user(const librbd::trash_image_info_t &trash_info) {
+  return trash_info.source != RBD_TRASH_IMAGE_SOURCE_USER;
+}
 
 void get_move_arguments(po::options_description *positional,
                         po::options_description *options) {
   at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
   options->add_options()
-    (at::DELAY.c_str(), po::value<uint64_t>(),
-     "time delay in seconds until effectively remove the image");
+    (EXPIRES_AT.c_str(), po::value<std::string>()->default_value("now"),
+     "set the expiration time of an image so it can be purged when it is stale");
 }
 
-int execute_move(const po::variables_map &vm) {
+int execute_move(const po::variables_map &vm,
+                 const std::vector<std::string> &ceph_global_init_args) {
   size_t arg_index = 0;
   std::string pool_name;
+  std::string namespace_name;
   std::string image_name;
   std::string snap_name;
 
   int r = utils::get_pool_image_snapshot_names(
-    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name,
-    &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE);
+    vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
+    &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
+    utils::SPEC_VALIDATION_NONE);
   if (r < 0) {
     return r;
   }
 
   librados::Rados rados;
   librados::IoCtx io_ctx;
-  r = utils::init(pool_name, &rados, &io_ctx);
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
   if (r < 0) {
     return r;
   }
 
-  uint64_t delay = 0;
-  if (vm.find(at::DELAY) != vm.end()) {
-    delay = vm[at::DELAY].as<uint64_t>();
+  utime_t now = ceph_clock_now();
+  utime_t exp_time = now;
+  std::string expires_at;
+  if (vm.find(EXPIRES_AT) != vm.end()) {
+    expires_at = vm[EXPIRES_AT].as<std::string>();
+    r = utime_t::invoke_date(expires_at, &exp_time);
+    if (r < 0) {
+      std::cerr << "rbd: error calling /bin/date: " << cpp_strerror(r)
+                << std::endl;
+      return r;
+    }
+  }
+
+  time_t dt = (exp_time - now).sec();
+  if(dt < 0) {
+    std::cerr << "rbd: cannot use a date in the past as an expiration date"
+              << std::endl;
+    return -EINVAL;
   }
 
   librbd::RBD rbd;
-  r = rbd.trash_move(io_ctx, image_name.c_str(), delay);
+  r = rbd.trash_move(io_ctx, image_name.c_str(), dt);
   if (r < 0) {
     std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
               << std::endl;
   }
 
   return r;
-
 }
 
 void get_remove_arguments(po::options_description *positional,
                           po::options_description *options) {
   positional->add_options()
-    (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
+    (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/[<namespace>/]]<image-id>)");
   at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
   at::add_image_id_option(options);
 
   at::add_no_progress_option(options);
@@ -88,22 +115,26 @@ void get_remove_arguments(po::options_description *positional,
       ("force", po::bool_switch(), "force remove of non-expired delayed images");
 }
 
-int execute_remove(const po::variables_map &vm) {
+int execute_remove(const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
   size_t arg_index = 0;
   std::string pool_name;
+  std::string namespace_name;
   std::string image_id;
-  int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
+  int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &namespace_name,
+                                   &image_id);
   if (r < 0) {
     return r;
   }
 
   librados::Rados rados;
   librados::IoCtx io_ctx;
-  r = utils::init(pool_name, &rados, &io_ctx);
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
   if (r < 0) {
     return r;
   }
 
+  io_ctx.set_osdmap_full_try();
   librbd::RBD rbd;
 
   utils::ProgressContext pc("Removing image", vm[at::NO_PROGRESS].as<bool>());
@@ -123,8 +154,8 @@ int execute_remove(const po::variables_map &vm) {
                 << std::endl;
     } else if (r == -EMLINK) {
       std::cerr << std::endl
-               << "Remove the image from the consistency group and try again."
-               << std::endl;
+                << "Remove the image from the group and try again."
+                << std::endl;
     } else if (r == -EPERM) {
       std::cerr << std::endl
                 << "Deferment time has not expired, please use --force if you "
@@ -143,7 +174,7 @@ int execute_remove(const po::variables_map &vm) {
 }
 
 std::string delete_status(time_t deferment_end_time) {
-  time_t now = ceph_clock_gettime();
+  time_t now = time(nullptr);
 
   std::string time_str = ctime(&deferment_end_time);
   time_str = time_str.substr(0, time_str.length() - 1);
@@ -151,6 +182,8 @@ std::string delete_status(time_t deferment_end_time) {
   std::stringstream ss;
   if (now < deferment_end_time) {
     ss << "protected until " << time_str;
+  } else {
+    ss << "expired at " << time_str;
   }
 
   return ss.str();
@@ -164,18 +197,23 @@ int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
     return r;
   }
 
+  if (!all_flag) {
+    trash_entries.erase(remove_if(trash_entries.begin(),
+                                  trash_entries.end(),
+                                  boost::bind(is_not_trash_user, _1)),
+                        trash_entries.end());
+  }
+
   if (!long_flag) {
     if (f) {
       f->open_array_section("trash");
     }
     for (const auto& entry : trash_entries) {
-      if (!all_flag &&
-          entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
-        continue;
-      }
        if (f) {
+         f->open_object_section("image");
          f->dump_string("id", entry.id);
          f->dump_string("name", entry.name);
+         f->close_section();
        } else {
          std::cout << entry.id << " " << entry.name << std::endl;
        }
@@ -197,13 +235,10 @@ int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
     tbl.define_column("SOURCE", TextTable::LEFT, TextTable::LEFT);
     tbl.define_column("DELETED_AT", TextTable::LEFT, TextTable::LEFT);
     tbl.define_column("STATUS", TextTable::LEFT, TextTable::LEFT);
+    tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT);
   }
 
   for (const auto& entry : trash_entries) {
-    if (!all_flag &&
-        entry.source == RBD_TRASH_IMAGE_SOURCE_MIRRORING) {
-      continue;
-    }
     librbd::Image im;
 
     r = rbd.open_by_id_read_only(io_ctx, im, entry.id.c_str(), NULL);
@@ -228,11 +263,35 @@ int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
       case RBD_TRASH_IMAGE_SOURCE_MIRRORING:
         del_source = "MIRRORING";
         break;
+      case RBD_TRASH_IMAGE_SOURCE_MIGRATION:
+        del_source = "MIGRATION";
+        break;
+      case RBD_TRASH_IMAGE_SOURCE_REMOVING:
+        del_source = "REMOVING";
+        break;
     }
 
     std::string time_str = ctime(&entry.deletion_time);
     time_str = time_str.substr(0, time_str.length() - 1);
 
+    bool has_parent = false;
+    std::string parent;
+    librbd::linked_image_spec_t parent_image;
+    librbd::snap_spec_t parent_snap;
+    r = im.get_parent(&parent_image, &parent_snap);
+    if (r == -ENOENT) {
+      r = 0;
+    } else if (r < 0) {
+      return r;
+    } else {
+      parent = parent_image.pool_name + "/";
+      if (!parent_image.pool_namespace.empty()) {
+        parent += parent_image.pool_namespace + "/";
+      }
+      parent += parent_image.image_name + "@" + parent_snap.name;
+      has_parent = true;
+    }
+
     if (f) {
       f->open_object_section("image");
       f->dump_string("id", entry.id);
@@ -241,14 +300,24 @@ int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
       f->dump_string("deleted_at", time_str);
       f->dump_string("status",
                      delete_status(entry.deferment_end_time));
+      if (has_parent) {
+        f->open_object_section("parent");
+        f->dump_string("pool", parent_image.pool_name);
+        f->dump_string("pool_namespace", parent_image.pool_namespace);
+        f->dump_string("image", parent_image.image_name);
+        f->dump_string("snapshot", parent_snap.name);
+        f->close_section();
+      }
       f->close_section();
     } else {
       tbl << entry.id
           << entry.name
           << del_source
           << time_str
-          << delete_status(entry.deferment_end_time)
-          << TextTable::endrow;
+          << delete_status(entry.deferment_end_time);
+      if (has_parent)
+        tbl << parent;
+      tbl << TextTable::endrow;
     }
   }
 
@@ -264,7 +333,7 @@ int do_list(librbd::RBD &rbd, librados::IoCtx& io_ctx, bool long_flag,
 
 void get_list_arguments(po::options_description *positional,
                         po::options_description *options) {
-  at::add_pool_options(positional, options);
+  at::add_pool_options(positional, options, true);
   options->add_options()
     ("all,a", po::bool_switch(), "list images from all sources");
   options->add_options()
@@ -272,23 +341,32 @@ void get_list_arguments(po::options_description *positional,
   at::add_format_options(options);
 }
 
-int execute_list(const po::variables_map &vm) {
+int execute_list(const po::variables_map &vm,
+                 const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  std::string namespace_name;
   size_t arg_index = 0;
-  std::string pool_name = utils::get_pool_name(vm, &arg_index);
+  int r = utils::get_pool_and_namespace_names(vm, true, false, &pool_name,
+                                              &namespace_name, &arg_index);
+  if (r < 0) {
+    return r;
+  }
 
   at::Format::Formatter formatter;
-  int r = utils::get_formatter(vm, &formatter);
+  r = utils::get_formatter(vm, &formatter);
   if (r < 0) {
     return r;
   }
 
   librados::Rados rados;
   librados::IoCtx io_ctx;
-  r = utils::init(pool_name, &rados, &io_ctx);
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
   if (r < 0) {
     return r;
   }
 
+  utils::disable_cache();
+
   librbd::RBD rbd;
   r = do_list(rbd, io_ctx, vm["long"].as<bool>(), vm["all"].as<bool>(),
               formatter.get());
@@ -300,27 +378,98 @@ int execute_list(const po::variables_map &vm) {
   return 0;
 }
 
+void get_purge_arguments(po::options_description *positional,
+                            po::options_description *options) {
+  at::add_pool_options(positional, options, true);
+  at::add_no_progress_option(options);
+
+  options->add_options()
+      (EXPIRED_BEFORE.c_str(), po::value<std::string>()->value_name("date"),
+       "purges images that expired before the given date");
+  options->add_options()
+      (THRESHOLD.c_str(), po::value<float>(),
+       "purges images until the current pool data usage is reduced to X%, "
+       "value range: 0.0-1.0");
+}
+
+int execute_purge (const po::variables_map &vm,
+                   const std::vector<std::string> &ceph_global_init_args) {
+  std::string pool_name;
+  std::string namespace_name;
+  size_t arg_index = 0;
+  int r = utils::get_pool_and_namespace_names(vm, true, false, &pool_name,
+                                              &namespace_name, &arg_index);
+  if (r < 0) {
+    return r;
+  }
+
+  utils::disable_cache();
+
+  librbd::RBD rbd;
+
+  librados::Rados rados;
+  librados::IoCtx io_ctx;
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+  if (r < 0) {
+    return r;
+  }
+
+  io_ctx.set_osdmap_full_try();
+
+  float threshold = -1;
+  time_t expire_ts = 0;
+
+  if (vm.find(THRESHOLD) != vm.end()) {
+    threshold = vm[THRESHOLD].as<float>();
+  } else {
+    if (vm.find(EXPIRED_BEFORE) != vm.end()) {
+      utime_t new_time;
+      r = utime_t::invoke_date(vm[EXPIRED_BEFORE].as<std::string>(), &new_time);
+      if (r < 0) {
+        std::cerr << "rbd: error calling /bin/date: " << cpp_strerror(r)
+                  << std::endl;
+        return r;
+      }
+      expire_ts = new_time.sec();
+    }
+  }
+
+  utils::ProgressContext pc("Removing images", vm[at::NO_PROGRESS].as<bool>());
+  r = rbd.trash_purge_with_progress(io_ctx, expire_ts, threshold, pc);
+  if (r < 0) {
+    pc.fail();
+  } else {
+    pc.finish();
+  }
+
+  return 0;
+}
+
 void get_restore_arguments(po::options_description *positional,
                             po::options_description *options) {
   positional->add_options()
     (at::IMAGE_ID.c_str(), "image id\n(example: [<pool-name>/]<image-id>)");
   at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
+  at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
   at::add_image_id_option(options);
   at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE, "");
 }
 
-int execute_restore(const po::variables_map &vm) {
+int execute_restore(const po::variables_map &vm,
+                    const std::vector<std::string> &ceph_global_init_args) {
   size_t arg_index = 0;
   std::string pool_name;
+  std::string namespace_name;
   std::string image_id;
-  int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &image_id);
+  int r = utils::get_pool_image_id(vm, &arg_index, &pool_name, &namespace_name,
+                                   &image_id);
   if (r < 0) {
     return r;
   }
 
   librados::Rados rados;
   librados::IoCtx io_ctx;
-  r = utils::init(pool_name, &rados, &io_ctx);
+  r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
   if (r < 0) {
     return r;
   }
@@ -351,21 +500,25 @@ int execute_restore(const po::variables_map &vm) {
 
 
 Shell::Action action_move(
-    {"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
-    &get_move_arguments, &execute_move);
+  {"trash", "move"}, {"trash", "mv"}, "Move an image to the trash.", "",
+  &get_move_arguments, &execute_move);
 
 Shell::Action action_remove(
   {"trash", "remove"}, {"trash", "rm"}, "Remove an image from trash.", "",
   &get_remove_arguments, &execute_remove);
 
+Shell::Action action_purge(
+  {"trash", "purge"}, {}, "Remove all expired images from trash.", "",
+  &get_purge_arguments, &execute_purge);
+
 Shell::SwitchArguments switched_arguments({"long", "l"});
 Shell::Action action_list(
   {"trash", "list"}, {"trash", "ls"}, "List trash images.", "",
   &get_list_arguments, &execute_list);
 
 Shell::Action action_restore(
-    {"trash", "restore"}, {}, "Restore an image from trash.", "",
-    &get_restore_arguments, &execute_restore);
+  {"trash", "restore"}, {}, "Restore an image from trash.", "",
+  &get_restore_arguments, &execute_restore);
 
 } // namespace trash
 } // namespace action