summaryrefslogtreecommitdiffstats
path: root/src/librbd
diff options
context:
space:
mode:
authorTheofilos Mouratidis <t.mour@cern.ch>2018-10-03 13:42:26 +0200
committerJason Dillaman <dillaman@redhat.com>2019-01-11 15:18:45 +0100
commitb6f9aab03713410dd1b50edc7fc1d71242ac3e74 (patch)
treeac1c468c3ba58298805eb9c39b4fd2ce62560274 /src/librbd
parentMerge PR #25898 into master (diff)
downloadceph-b6f9aab03713410dd1b50edc7fc1d71242ac3e74.tar.xz
ceph-b6f9aab03713410dd1b50edc7fc1d71242ac3e74.zip
librbd: add trash purge api calls
Add trash purge api calls and fix the docs of some other rbd trash functions Signed-off-by: Theofilos Mouratidis <mtheofilos@gmail.com>
Diffstat (limited to 'src/librbd')
-rw-r--r--src/librbd/api/Trash.cc168
-rw-r--r--src/librbd/api/Trash.h2
-rw-r--r--src/librbd/internal.cc8
-rw-r--r--src/librbd/librbd.cc46
4 files changed, 219 insertions, 5 deletions
diff --git a/src/librbd/api/Trash.cc b/src/librbd/api/Trash.cc
index 319be136b58..fdc2d03a643 100644
--- a/src/librbd/api/Trash.cc
+++ b/src/librbd/api/Trash.cc
@@ -18,6 +18,7 @@
#include "librbd/mirror/DisableRequest.h"
#include "librbd/mirror/EnableRequest.h"
#include "librbd/trash/MoveRequest.h"
+#include <json_spirit/json_spirit.h>
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
@@ -271,6 +272,173 @@ int Trash<I>::list(IoCtx &io_ctx, vector<trash_image_info_t> &entries) {
}
template <typename I>
+int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
+ float threshold, ProgressContext& pctx) {
+ auto *cct((CephContext *) io_ctx.cct());
+
+ librbd::RBD rbd;
+
+ std::vector<librbd::trash_image_info_t> trash_entries;
+ int r = librbd::api::Trash<>::list(io_ctx, trash_entries);
+ if (r < 0) {
+ return r;
+ }
+
+ std::remove_if(trash_entries.begin(), trash_entries.end(),
+ [](librbd::trash_image_info_t info) {
+ return info.source != RBD_TRASH_IMAGE_SOURCE_USER;
+ }
+ );
+
+ std::vector<const char *> to_be_removed;
+ if (threshold != -1) {
+ if (threshold < 0 || threshold > 1) {
+ lderr(cct) << "argument 'threshold' is out of valid range"
+ << dendl;
+ return -EINVAL;
+ }
+
+ librados::bufferlist inbl;
+ librados::bufferlist outbl;
+ std::string pool_name = io_ctx.get_pool_name();
+
+ librados::Rados rados(io_ctx);
+ rados.mon_command(R"({"prefix": "df", "format": "json"})", inbl,
+ &outbl, nullptr);
+
+ json_spirit::mValue json;
+ if (!json_spirit::read(outbl.to_str(), json)) {
+ lderr(cct) << "ceph df json output could not be parsed"
+ << dendl;
+ return -EBADMSG;
+ }
+
+ json_spirit::mArray arr = json.get_obj()["pools"].get_array();
+
+ double pool_percent_used = 0;
+ uint64_t pool_total_bytes = 0;
+
+ std::map<std::string, std::vector<const char *>> datapools;
+
+ std::sort(trash_entries.begin(), trash_entries.end(),
+ [](librbd::trash_image_info_t a, librbd::trash_image_info_t b) {
+ return a.deferment_end_time < b.deferment_end_time;
+ }
+ );
+
+ for (const auto &entry : trash_entries) {
+ librbd::Image image;
+ std::string data_pool;
+ r = rbd.open_by_id_read_only(io_ctx, image, entry.id.c_str(), NULL);
+ if (r < 0) continue;
+
+ int64_t data_pool_id = image.get_data_pool_id();
+ if (data_pool_id != io_ctx.get_id()) {
+ librados::IoCtx data_io_ctx;
+ r = util::create_ioctx(io_ctx, "image", data_pool_id,
+ {}, &data_io_ctx);
+ if (r < 0) {
+ lderr(cct) << "error accessing data pool" << dendl;
+ continue;
+ }
+ data_pool = data_io_ctx.get_pool_name();
+ datapools[data_pool].push_back(entry.id.c_str());
+ } else {
+ datapools[pool_name].push_back(entry.id.c_str());
+ }
+ }
+
+ uint64_t bytes_to_free = 0;
+
+ for (uint8_t i = 0; i < arr.size(); ++i) {
+ json_spirit::mObject obj = arr[i].get_obj();
+ std::string name = obj.find("name")->second.get_str();
+ auto img = datapools.find(name);
+ if (img != datapools.end()) {
+ json_spirit::mObject stats = arr[i].get_obj()["stats"].get_obj();
+ pool_percent_used = stats["percent_used"].get_real();
+ if (pool_percent_used <= threshold) continue;
+
+ bytes_to_free = 0;
+
+ pool_total_bytes = stats["max_avail"].get_uint64() +
+ stats["bytes_used"].get_uint64();
+
+ auto bytes_threshold = (uint64_t) (pool_total_bytes *
+ (pool_percent_used - threshold));
+
+ librbd::Image curr_img;
+ for (const auto &it : img->second) {
+ r = rbd.open_by_id_read_only(io_ctx, curr_img, it, NULL);
+ if (r < 0) continue;
+
+ uint64_t img_size;
+ curr_img.size(&img_size);
+ r = curr_img.diff_iterate2(nullptr, 0, img_size, false, true,
+ [](uint64_t offset, size_t len, int exists, void *arg) {
+ auto *to_free = reinterpret_cast<uint64_t *>(arg);
+ if (exists)
+ (*to_free) += len;
+ return 0;
+ }, &bytes_to_free
+ );
+ if (r < 0) continue;
+ to_be_removed.push_back(it);
+ if (bytes_to_free >= bytes_threshold) break;
+ }
+ }
+ }
+ if (bytes_to_free == 0) {
+ ldout(cct, 10) << "pool usage is lower than or equal to "
+ << (threshold * 100)
+ << "%" << dendl;
+ return 0;
+ }
+ }
+
+ if (expire_ts == 0) {
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ expire_ts = now.tv_sec;
+ }
+
+ for (const auto &entry : trash_entries) {
+ if (expire_ts >= entry.deferment_end_time) {
+ to_be_removed.push_back(entry.id.c_str());
+ }
+ }
+
+ NoOpProgressContext remove_pctx;
+ uint64_t list_size = to_be_removed.size(), i = 0;
+ for (const auto &entry_id : to_be_removed) {
+ r = librbd::api::Trash<>::remove(io_ctx, entry_id, true, remove_pctx);
+ if (r < 0) {
+ if (r == -ENOTEMPTY) {
+ ldout(cct, 5) << "image has snapshots - these must be deleted "
+ << "with 'rbd snap purge' before the image can be removed."
+ << dendl;
+ } else if (r == -EBUSY) {
+ ldout(cct, 5) << "error: image still has watchers"
+ << std::endl
+ << "This means the image is still open or the client using "
+ << "it crashed. Try again after closing/unmapping it or "
+ << "waiting 30s for the crashed client to timeout."
+ << dendl;
+ } else if (r == -EMLINK) {
+ ldout(cct, 5) << "Remove the image from the group and try again."
+ << dendl;
+ } else {
+ lderr(cct) << "remove error: " << cpp_strerror(r) << dendl;
+ }
+ return r;
+ }
+ pctx.update_progress(++i, list_size);
+ }
+
+ return 0;
+}
+
+template <typename I>
int Trash<I>::remove(IoCtx &io_ctx, const std::string &image_id, bool force,
ProgressContext& prog_ctx) {
CephContext *cct((CephContext *)io_ctx.cct());
diff --git a/src/librbd/api/Trash.h b/src/librbd/api/Trash.h
index cf7217de809..f2e52f6cbeb 100644
--- a/src/librbd/api/Trash.h
+++ b/src/librbd/api/Trash.h
@@ -27,6 +27,8 @@ struct Trash {
trash_image_info_t *info);
static int list(librados::IoCtx &io_ctx,
std::vector<trash_image_info_t> &entries);
+ static int purge(IoCtx& io_ctx, time_t expire_ts,
+ float threshold, ProgressContext& pctx);
static int remove(librados::IoCtx &io_ctx, const std::string &image_id,
bool force, ProgressContext& prog_ctx);
static int restore(librados::IoCtx &io_ctx, const std::string &image_id,
diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc
index ccb38aed5e4..8b499c283da 100644
--- a/src/librbd/internal.cc
+++ b/src/librbd/internal.cc
@@ -1135,8 +1135,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
}
int lock_break(ImageCtx *ictx, rbd_lock_mode_t lock_mode,
- const std::string &lock_owner)
- {
+ const std::string &lock_owner) {
CephContext *cct = ictx->cct;
ldout(cct, 20) << __func__ << ": ictx=" << ictx << ", "
<< "lock_mode=" << lock_mode << ", "
@@ -1527,7 +1526,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
if (throttle.pending_error()) {
return throttle.wait_for_ret();
}
-
+
{
RWLock::RLocker snap_locker(src->snap_lock);
if (src->object_map != nullptr) {
@@ -1545,7 +1544,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
} else {
object_id += src->stripe_count;
}
- }
+ }
uint64_t len = min(period, src_size - offset);
bufferlist *bl = new bufferlist();
@@ -2006,4 +2005,3 @@ std::ostream &operator<<(std::ostream &os, const librbd::ImageOptions &opts) {
return os;
}
-
diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc
index 30c608737c3..142cdec6ea0 100644
--- a/src/librbd/librbd.cc
+++ b/src/librbd/librbd.cc
@@ -648,6 +648,26 @@ namespace librbd {
return r;
}
+ int RBD::trash_purge(IoCtx &io_ctx, time_t expire_ts, float threshold) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_purge_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), expire_ts, threshold);
+ NoOpProgressContext nop_pctx;
+ int r = librbd::api::Trash<>::purge(io_ctx, expire_ts, threshold, nop_pctx);
+ tracepoint(librbd, trash_purge_exit, r);
+ return r;
+ }
+
+ int RBD::trash_purge_with_progress(IoCtx &io_ctx, time_t expire_ts,
+ float threshold, ProgressContext &pctx) {
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_purge_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), expire_ts, threshold);
+ int r = librbd::api::Trash<>::purge(io_ctx, expire_ts, threshold, pctx);
+ tracepoint(librbd, trash_purge_exit, r);
+ return r;
+ }
+
int RBD::namespace_create(IoCtx& io_ctx, const char *namespace_name) {
return librbd::api::Namespace<>::create(io_ctx, namespace_name);
}
@@ -3182,6 +3202,32 @@ extern "C" void rbd_trash_list_cleanup(rbd_trash_image_info_t *entries,
}
}
+extern "C" int rbd_trash_purge(rados_ioctx_t io, time_t expire_ts,
+ float threshold) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_purge_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), expire_ts, threshold);
+ librbd::NoOpProgressContext nop_pctx;
+ int r = librbd::api::Trash<>::purge(io_ctx, expire_ts, threshold, nop_pctx);
+ tracepoint(librbd, trash_purge_exit, r);
+ return r;
+}
+
+extern "C" int rbd_trash_purge_with_progress(rados_ioctx_t io, time_t expire_ts,
+ float threshold, librbd_progress_fn_t cb, void* cbdata) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(io, io_ctx);
+ TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
+ tracepoint(librbd, trash_purge_enter, io_ctx.get_pool_name().c_str(),
+ io_ctx.get_id(), expire_ts, threshold);
+ librbd::CProgressContext pctx(cb, cbdata);
+ int r = librbd::api::Trash<>::purge(io_ctx, expire_ts, threshold, pctx);
+ tracepoint(librbd, trash_purge_exit, r);
+ return r;
+}
+
extern "C" int rbd_trash_remove(rados_ioctx_t p, const char *image_id,
bool force) {
librados::IoCtx io_ctx;