mirror of
https://github.com/ceph/ceph
synced 2026-08-03 07:22:20 +00:00
librbd: tweaked trash purge API
Removed the internal usage of the public librbd API, avoided double-opening image to determine data pool, and fixed issues with the unit test case. Signed-off-by: Jason Dillaman <dillaman@redhat.com>
This commit is contained in:
parent
a3155c3fc2
commit
504f4e78ef
@ -14,6 +14,7 @@
|
||||
#include "librbd/Operations.h"
|
||||
#include "librbd/TrashWatcher.h"
|
||||
#include "librbd/Utils.h"
|
||||
#include "librbd/api/DiffIterate.h"
|
||||
#include "librbd/api/Image.h"
|
||||
#include "librbd/mirror/DisableRequest.h"
|
||||
#include "librbd/mirror/EnableRequest.h"
|
||||
@ -275,8 +276,7 @@ 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;
|
||||
ldout(cct, 20) << &io_ctx << dendl;
|
||||
|
||||
std::vector<librbd::trash_image_info_t> trash_entries;
|
||||
int r = librbd::api::Trash<>::list(io_ctx, trash_entries);
|
||||
@ -290,7 +290,7 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
}
|
||||
);
|
||||
|
||||
std::vector<const char *> to_be_removed;
|
||||
std::set<std::string> to_be_removed;
|
||||
if (threshold != -1) {
|
||||
if (threshold < 0 || threshold > 1) {
|
||||
lderr(cct) << "argument 'threshold' is out of valid range"
|
||||
@ -318,7 +318,7 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
double pool_percent_used = 0;
|
||||
uint64_t pool_total_bytes = 0;
|
||||
|
||||
std::map<std::string, std::vector<const char *>> datapools;
|
||||
std::map<std::string, std::vector<std::string>> datapools;
|
||||
|
||||
std::sort(trash_entries.begin(), trash_entries.end(),
|
||||
[](librbd::trash_image_info_t a, librbd::trash_image_info_t b) {
|
||||
@ -327,12 +327,16 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
);
|
||||
|
||||
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 = -1;
|
||||
r = cls_client::get_data_pool(&io_ctx, util::header_name(entry.id),
|
||||
&data_pool_id);
|
||||
if (r < 0 && r != -ENOENT && r != -EOPNOTSUPP) {
|
||||
lderr(cct) << "failed to query data pool: " << cpp_strerror(r) << dendl;
|
||||
return r;
|
||||
} else if (data_pool_id == -1) {
|
||||
data_pool_id = io_ctx.get_id();
|
||||
}
|
||||
|
||||
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,
|
||||
@ -341,10 +345,10 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
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());
|
||||
auto data_pool = data_io_ctx.get_pool_name();
|
||||
datapools[data_pool].push_back(entry.id);
|
||||
} else {
|
||||
datapools[pool_name].push_back(entry.id.c_str());
|
||||
datapools[pool_name].push_back(entry.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,27 +371,41 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
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;
|
||||
auto ictx = new ImageCtx("", it, nullptr, io_ctx, false);
|
||||
r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
|
||||
if (r == -ENOENT) {
|
||||
continue;
|
||||
} else if (r < 0) {
|
||||
lderr(cct) << "failed to open image " << it << ": "
|
||||
<< cpp_strerror(r) << dendl;
|
||||
}
|
||||
|
||||
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) {
|
||||
r = librbd::api::DiffIterate<I>::diff_iterate(
|
||||
ictx, cls::rbd::UserSnapshotNamespace(), nullptr, 0, ictx->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;
|
||||
}, &bytes_to_free);
|
||||
|
||||
ictx->state->close();
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to calculate disk usage for image " << it
|
||||
<< ": " << cpp_strerror(r) << dendl;
|
||||
continue;
|
||||
}
|
||||
|
||||
to_be_removed.insert(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)
|
||||
@ -404,7 +422,7 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
|
||||
for (const auto &entry : trash_entries) {
|
||||
if (expire_ts >= entry.deferment_end_time) {
|
||||
to_be_removed.push_back(entry.id.c_str());
|
||||
to_be_removed.insert(entry.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,14 +433,13 @@ int Trash<I>::purge(IoCtx& io_ctx, time_t expire_ts,
|
||||
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;
|
||||
<< "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."
|
||||
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."
|
||||
|
||||
@ -29,6 +29,7 @@ except ImportError:
|
||||
from collections import Iterable
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
import time
|
||||
|
||||
cimport rados
|
||||
|
||||
@ -1247,7 +1248,7 @@ class RBD(object):
|
||||
if ret != 0:
|
||||
raise make_ex(ret, 'error moving image to trash')
|
||||
|
||||
def trash_purge(self, ioctx, expire_ts=datetime.now(), threshold=-1):
|
||||
def trash_purge(self, ioctx, expire_ts=None, threshold=-1):
|
||||
"""
|
||||
Delete RBD images from trash in bulk.
|
||||
|
||||
@ -1265,11 +1266,15 @@ class RBD(object):
|
||||
:param threshold: percentage of pool usage to be met (0 to 1)
|
||||
:type threshold: float
|
||||
"""
|
||||
as_time_t = int((expire_ts - datetime.utcfromtimestamp(0)).total_seconds())
|
||||
if expire_ts:
|
||||
expire_epoch_ts = time.mktime(expire_ts.timetuple())
|
||||
else:
|
||||
expire_epoch_ts = 0
|
||||
|
||||
cdef:
|
||||
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
|
||||
time_t _expire_ts = expire_epoch_ts
|
||||
float _threshold = threshold
|
||||
time_t _expire_ts = as_time_t
|
||||
with nogil:
|
||||
ret = rbd_trash_purge(_ioctx, _expire_ts, _threshold)
|
||||
if ret != 0:
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
|
||||
static int get_concurrency() {
|
||||
int concurrency = 0;
|
||||
@ -168,6 +169,23 @@ int TestRadosClient::mon_command(const std::vector<std::string>& cmd,
|
||||
return 0;
|
||||
} else if ((*j_it)->get_data() == "config-key rm") {
|
||||
return 0;
|
||||
} else if ((*j_it)->get_data() == "df") {
|
||||
std::stringstream str;
|
||||
str << R"({"pools": [)";
|
||||
|
||||
std::list<std::pair<int64_t, std::string>> pools;
|
||||
pool_list(pools);
|
||||
for (auto& pool : pools) {
|
||||
if (pools.begin()->first != pool.first) {
|
||||
str << ",";
|
||||
}
|
||||
str << R"({"name": ")" << pool.second << R"(", "stats": )"
|
||||
<< R"({"percent_used": 1.0, "bytes_used": 0, "max_avail": 0}})";
|
||||
}
|
||||
|
||||
str << "]}";
|
||||
outbl->append(str.str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOSYS;
|
||||
|
||||
@ -6594,6 +6594,8 @@ TEST_F(TestLibRBD, DefaultFeatures) {
|
||||
}
|
||||
|
||||
TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
|
||||
REQUIRE_FORMAT_V2();
|
||||
|
||||
librados::IoCtx ioctx;
|
||||
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
|
||||
|
||||
@ -6606,14 +6608,7 @@ TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
|
||||
|
||||
librbd::Image image;
|
||||
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
|
||||
uint8_t old_format;
|
||||
ASSERT_EQ(0, image.old_format(&old_format));
|
||||
|
||||
if (old_format) {
|
||||
ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
|
||||
image.close();
|
||||
return;
|
||||
}
|
||||
std::string image_id;
|
||||
ASSERT_EQ(0, image.get_id(&image_id));
|
||||
image.close();
|
||||
@ -6645,6 +6640,8 @@ TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
|
||||
}
|
||||
|
||||
TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
|
||||
REQUIRE_FORMAT_V2();
|
||||
|
||||
librados::IoCtx ioctx;
|
||||
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
|
||||
|
||||
@ -6657,14 +6654,7 @@ TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
|
||||
|
||||
librbd::Image image;
|
||||
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
|
||||
uint8_t old_format;
|
||||
ASSERT_EQ(0, image.old_format(&old_format));
|
||||
|
||||
if (old_format) {
|
||||
ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
|
||||
image.close();
|
||||
return;
|
||||
}
|
||||
std::string image_id;
|
||||
ASSERT_EQ(0, image.get_id(&image_id));
|
||||
image.close();
|
||||
@ -6681,6 +6671,8 @@ TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
|
||||
}
|
||||
|
||||
TEST_F(TestLibRBD, TestTrashPurge) {
|
||||
REQUIRE_FORMAT_V2();
|
||||
|
||||
librados::IoCtx ioctx;
|
||||
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
|
||||
|
||||
@ -6695,14 +6687,7 @@ TEST_F(TestLibRBD, TestTrashPurge) {
|
||||
|
||||
librbd::Image image1;
|
||||
ASSERT_EQ(0, rbd.open(ioctx, image1, name1.c_str(), nullptr));
|
||||
uint8_t old_format;
|
||||
ASSERT_EQ(0, image1.old_format(&old_format));
|
||||
|
||||
if (old_format) {
|
||||
ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name1.c_str(), 0));
|
||||
image1.close();
|
||||
return;
|
||||
}
|
||||
std::string image_id1;
|
||||
ASSERT_EQ(0, image1.get_id(&image_id1));
|
||||
image1.close();
|
||||
@ -6711,13 +6696,10 @@ TEST_F(TestLibRBD, TestTrashPurge) {
|
||||
|
||||
librbd::Image image2;
|
||||
ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), nullptr));
|
||||
ASSERT_EQ(0, image2.old_format(&old_format));
|
||||
ceph::bufferlist bl;
|
||||
bl.append(std::string(1024, '0'));
|
||||
ASSERT_EQ(1024, image2.write(0, 1024, bl));
|
||||
|
||||
if (old_format) {
|
||||
ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name2.c_str(), 0));
|
||||
image2.close();
|
||||
return;
|
||||
}
|
||||
std::string image_id2;
|
||||
ASSERT_EQ(0, image2.get_id(&image_id2));
|
||||
image2.close();
|
||||
@ -6727,29 +6709,27 @@ TEST_F(TestLibRBD, TestTrashPurge) {
|
||||
|
||||
std::vector<librbd::trash_image_info_t> entries;
|
||||
ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
|
||||
ASSERT_FALSE(entries.empty());
|
||||
bool found = false;
|
||||
for(auto& entry : entries) {
|
||||
if (entry.id == image_id1 && entry.name == name1)
|
||||
found = true;
|
||||
}
|
||||
ASSERT_FALSE(found);
|
||||
ASSERT_EQ(1U, entries.size());
|
||||
ASSERT_EQ(image_id2, entries[0].id);
|
||||
ASSERT_EQ(name2, entries[0].name);
|
||||
entries.clear();
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
ASSERT_EQ(0, rbd.trash_purge(ioctx, now.tv_sec+1000, 0));
|
||||
ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
|
||||
|
||||
found = false;
|
||||
for(auto& entry : entries) {
|
||||
if (entry.id == image_id2 && entry.name == name2)
|
||||
found = true;
|
||||
float threshold = 0.0;
|
||||
if (!is_librados_test_stub(_rados)) {
|
||||
// real cluster usage reports have a long latency to update
|
||||
threshold = -1.0;
|
||||
}
|
||||
ASSERT_FALSE(found);
|
||||
|
||||
ASSERT_EQ(0, rbd.trash_purge(ioctx, now.tv_sec+1000, threshold));
|
||||
ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
|
||||
ASSERT_EQ(0U, entries.size());
|
||||
}
|
||||
|
||||
TEST_F(TestLibRBD, TestTrashMoveAndRestore) {
|
||||
REQUIRE_FORMAT_V2();
|
||||
|
||||
librados::IoCtx ioctx;
|
||||
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
|
||||
|
||||
@ -6762,14 +6742,7 @@ TEST_F(TestLibRBD, TestTrashMoveAndRestore) {
|
||||
|
||||
librbd::Image image;
|
||||
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
|
||||
uint8_t old_format;
|
||||
ASSERT_EQ(0, image.old_format(&old_format));
|
||||
|
||||
if (old_format) {
|
||||
ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
|
||||
image.close();
|
||||
return;
|
||||
}
|
||||
std::string image_id;
|
||||
ASSERT_EQ(0, image.get_id(&image_id));
|
||||
image.close();
|
||||
|
||||
@ -1874,24 +1874,13 @@ class TestTrash(object):
|
||||
image_name2 = image_name
|
||||
image_id2 = image.id()
|
||||
|
||||
create_image()
|
||||
with Image(ioctx, image_name) as image:
|
||||
image_name3 = image_name
|
||||
image_id3 = image.id()
|
||||
|
||||
RBD().trash_move(ioctx, image_name1, 0)
|
||||
RBD().trash_move(ioctx, image_name2, 1000)
|
||||
RBD().trash_move(ioctx, image_name3, 1000)
|
||||
|
||||
RBD().trash_purge(ioctx, datetime.now())
|
||||
entries = list(RBD().trash_list(ioctx))
|
||||
for e in entries:
|
||||
assert(e['id'] != image_id1)
|
||||
|
||||
RBD.trash_purge(ioctx, datetime.now() + timedelta(seconds=5000), 0)
|
||||
entries = list(RBD().trash_list(ioctx))
|
||||
for e in entries:
|
||||
assert(e['id'] not in [image_id2, image_id3])
|
||||
eq([image_id2], [x['id'] for x in entries])
|
||||
RBD().trash_remove(ioctx, image_id2, True)
|
||||
|
||||
def test_remove_denied(self):
|
||||
create_image()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user