TRASH_IMAGE_SOURCE_USER = 0,
TRASH_IMAGE_SOURCE_MIRRORING = 1,
TRASH_IMAGE_SOURCE_MIGRATION = 2,
+ TRASH_IMAGE_SOURCE_REMOVING = 3,
};
inline std::ostream& operator<<(std::ostream& os,
case TRASH_IMAGE_SOURCE_MIGRATION:
os << "migration";
break;
+ case TRASH_IMAGE_SOURCE_REMOVING:
+ os << "removing";
+ break;
default:
os << "unknown (" << static_cast<uint32_t>(source) << ")";
break;
RBD_TRASH_IMAGE_SOURCE_USER = 0,
RBD_TRASH_IMAGE_SOURCE_MIRRORING = 1,
RBD_TRASH_IMAGE_SOURCE_MIGRATION = 2,
- RBD_TRASH_IMAGE_SOURCE_REMOVE = 3
+ RBD_TRASH_IMAGE_SOURCE_REMOVING = 3
} rbd_trash_image_source_t;
typedef struct {
return r;
}
- // old format images are in a tmap
+ // V1 format images are in a tmap
if (bl.length()) {
auto p = bl.cbegin();
bufferlist header;
}
}
+ // V2 format images
std::map<std::string, std::string> image_names_to_ids;
r = list_images_v2(io_ctx, &image_names_to_ids);
if (r < 0) {
.name = img_pair.first});
}
+ // include V2 images in a partially removed state
+ std::vector<librbd::trash_image_info_t> trash_images;
+ r = Trash<I>::list(io_ctx, trash_images, false);
+ if (r < 0 && r != -EOPNOTSUPP) {
+ lderr(cct) << "error listing trash images: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ for (const auto& trash_image : trash_images) {
+ if (trash_image.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
+ images->push_back({.id = trash_image.id,
+ .name = trash_image.name});
+
+ }
+ }
+
return 0;
}
}
std::vector<librbd::trash_image_info_t> trash_images;
- r = Trash<I>::list(child_io_ctx, trash_images);
+ r = Trash<I>::list(child_io_ctx, trash_images, false);
if (r < 0 && r != -EOPNOTSUPP) {
lderr(cct) << "error listing trash images: " << cpp_strerror(r)
<< dendl;
}
for (auto& it : trash_images) {
- child_image_id_to_info[it.id] = {it.name, true};
+ child_image_id_to_info.insert({
+ it.id,
+ {it.name,
+ it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING ? false : true}});
}
}
if (r == -ENOENT) {
// check if it already exists in trash from an aborted trash remove attempt
std::vector<trash_image_info_t> trash_entries;
- r = Trash<I>::list(io_ctx, trash_entries);
+ r = Trash<I>::list(io_ctx, trash_entries, false);
if (r < 0) {
return r;
} else if (r >= 0) {
for (auto& entry : trash_entries) {
if (entry.name == image_name &&
- entry.source == RBD_TRASH_IMAGE_SOURCE_REMOVE) {
+ entry.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
return Trash<I>::remove(io_ctx, entry.id, true, prog_ctx);
}
}
ConfigProxy config(cct->_conf);
Config<I>::apply_pool_overrides(io_ctx, &config);
- rbd_trash_image_source_t trash_image_source = RBD_TRASH_IMAGE_SOURCE_REMOVE;
+ rbd_trash_image_source_t trash_image_source =
+ RBD_TRASH_IMAGE_SOURCE_REMOVING;
uint64_t expire_seconds = 0;
bool check_watchers = true;
if (config.get_val<bool>("rbd_move_to_trash_on_remove")) {
r = Trash<I>::move(io_ctx, trash_image_source, image_name, image_id,
expire_seconds, check_watchers);
if (r >= 0) {
- if (trash_image_source == RBD_TRASH_IMAGE_SOURCE_REMOVE) {
+ if (trash_image_source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
// proceed with attempting to immediately remove the image
r = Trash<I>::remove(io_ctx, image_id, true, prog_ctx);
}
const std::string &image_name, std::string *image_id) {
std::vector<trash_image_info_t> entries;
- int r = Trash<>::list(io_ctx, entries);
+ int r = Trash<>::list(io_ctx, entries, false);
if (r < 0) {
return r;
}
uint64_t* max_provisioned_bytes;
uint64_t* snapshot_count;
+ std::vector<trash_image_info_t> trash_entries;
+ int r = Trash<I>::list(io_ctx, trash_entries, false);
+ if (r < 0 && r != -EOPNOTSUPP) {
+ return r;
+ }
+
get_pool_stat_option_value<I>(
stat_options, RBD_POOL_STAT_OPTION_IMAGES, &image_count);
get_pool_stat_option_value<I>(
}
std::vector<std::string> image_ids;
- image_ids.reserve(images.size());
+ image_ids.reserve(images.size() + trash_entries.size());
for (auto& it : images) {
image_ids.push_back(std::move(it.second));
}
+ for (auto& it : trash_entries) {
+ if (it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
+ image_ids.push_back(std::move(it.id));
+ }
+ }
r = get_pool_stats<I>(io_ctx, config, image_ids, image_count,
provisioned_bytes, max_provisioned_bytes,
stat_options, RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &snapshot_count);
if (image_count != nullptr || provisioned_bytes != nullptr ||
max_provisioned_bytes != nullptr || snapshot_count != nullptr) {
- std::vector<trash_image_info_t> trash_entries;
- int r = Trash<I>::list(io_ctx, trash_entries);
- if (r < 0 && r != -EOPNOTSUPP) {
- return r;
- }
std::vector<std::string> image_ids;
image_ids.reserve(trash_entries.size());
for (auto& it : trash_entries) {
+ if (it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
+ continue;
+ }
image_ids.push_back(std::move(it.id));
}
}
template <typename I>
-int Trash<I>::list(IoCtx &io_ctx, vector<trash_image_info_t> &entries) {
+int Trash<I>::list(IoCtx &io_ctx, vector<trash_image_info_t> &entries,
+ bool exclude_user_remove_source) {
CephContext *cct((CephContext *)io_ctx.cct());
ldout(cct, 20) << "trash_list " << &io_ctx << dendl;
for (const auto &entry : trash_entries) {
rbd_trash_image_source_t source =
static_cast<rbd_trash_image_source_t>(entry.second.source);
+ if (exclude_user_remove_source &&
+ source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
+ continue;
+ }
entries.push_back({entry.first, entry.second.name, source,
entry.second.deletion_time.sec(),
entry.second.deferment_end_time.sec()});
ldout(cct, 20) << &io_ctx << dendl;
std::vector<librbd::trash_image_info_t> trash_entries;
- int r = librbd::api::Trash<I>::list(io_ctx, trash_entries);
+ int r = librbd::api::Trash<I>::list(io_ctx, trash_entries, true);
if (r < 0) {
return r;
}
static int get(librados::IoCtx &io_ctx, const std::string &id,
trash_image_info_t *info);
static int list(librados::IoCtx &io_ctx,
- std::vector<trash_image_info_t> &entries);
+ std::vector<trash_image_info_t> &entries,
+ bool exclude_user_remove_source);
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,
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, trash_list_enter,
io_ctx.get_pool_name().c_str(), io_ctx.get_id());
- int r = librbd::api::Trash<>::list(io_ctx, entries);
+ int r = librbd::api::Trash<>::list(io_ctx, entries, true);
#ifdef WITH_LTTNG
if (r >= 0) {
for (const auto& entry : entries) {
memset(entries, 0, sizeof(*entries) * *num_entries);
vector<librbd::trash_image_info_t> cpp_entries;
- int r = librbd::api::Trash<>::list(io_ctx, cpp_entries);
+ int r = librbd::api::Trash<>::list(io_ctx, cpp_entries, true);
if (r < 0) {
tracepoint(librbd, trash_list_exit, r, *num_entries);
return r;
_RBD_TRASH_IMAGE_SOURCE_USER "RBD_TRASH_IMAGE_SOURCE_USER",
_RBD_TRASH_IMAGE_SOURCE_MIRRORING "RBD_TRASH_IMAGE_SOURCE_MIRRORING",
_RBD_TRASH_IMAGE_SOURCE_MIGRATION "RBD_TRASH_IMAGE_SOURCE_MIGRATION"
- _RBD_TRASH_IMAGE_SOURCE_REMOVE "RBD_TRASH_IMAGE_SOURCE_REMOVE"
+ _RBD_TRASH_IMAGE_SOURCE_REMOVING "RBD_TRASH_IMAGE_SOURCE_REMOVING"
ctypedef struct rbd_trash_image_info_t:
char *id
if ret != 0:
raise make_ex(ret, 'error retrieving image from trash')
- __source_string = ['USER', 'MIRRORING', 'MIGRATION']
+ __source_string = ['USER', 'MIRRORING', 'MIGRATION', 'REMOVING']
info = {
'id' : decode_cstr(c_info.id),
'name' : decode_cstr(c_info.name),
test_MirroringWatcher.cc
test_ObjectMap.cc
test_Operations.cc
+ test_Trash.cc
journal/test_Entries.cc
journal/test_Replay.cc)
add_library(rbd_test STATIC ${librbd_test})
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls/rbd/cls_rbd_client.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "test/librbd/test_fixture.h"
+#include "test/librbd/test_support.h"
+#include "librbd/api/Trash.h"
+#include <set>
+#include <vector>
+
+void register_test_trash() {
+}
+
+namespace librbd {
+
+static bool operator==(const trash_image_info_t& lhs,
+ const trash_image_info_t& rhs) {
+ return (lhs.id == rhs.id &&
+ lhs.name == rhs.name &&
+ lhs.source == rhs.source);
+}
+
+static bool operator==(const image_spec_t& lhs,
+ const image_spec_t& rhs) {
+ return (lhs.id == rhs.id && lhs.name == rhs.name);
+}
+
+class TestTrash : public TestFixture {
+public:
+
+ TestTrash() {}
+};
+
+TEST_F(TestTrash, UserRemovingSource) {
+ REQUIRE_FORMAT_V2();
+
+ auto compare_lambda = [](const trash_image_info_t& lhs,
+ const trash_image_info_t& rhs) {
+ if (lhs.id != rhs.id) {
+ return lhs.id < rhs.id;
+ } else if (lhs.name != rhs.name) {
+ return lhs.name < rhs.name;
+ }
+ return lhs.source < rhs.source;
+ };
+ typedef std::set<trash_image_info_t, decltype(compare_lambda)> TrashEntries;
+
+ librbd::RBD rbd;
+ librbd::Image image;
+ auto image_name1 = m_image_name;
+ std::string image_id1;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, image_name1.c_str()));
+ ASSERT_EQ(0, image.get_id(&image_id1));
+ ASSERT_EQ(0, image.close());
+
+ auto image_name2 = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, image_name2, m_image_size));
+
+ std::string image_id2;
+ ASSERT_EQ(0, rbd.open(m_ioctx, image, image_name2.c_str()));
+ ASSERT_EQ(0, image.get_id(&image_id2));
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, api::Trash<>::move(m_ioctx, RBD_TRASH_IMAGE_SOURCE_USER,
+ image_name1, image_id1, 0, false));
+ ASSERT_EQ(0, api::Trash<>::move(m_ioctx, RBD_TRASH_IMAGE_SOURCE_REMOVING,
+ image_name2, image_id2, 0, true));
+
+ TrashEntries trash_entries{compare_lambda};
+ TrashEntries expected_trash_entries{compare_lambda};
+
+ std::vector<trash_image_info_t> entries;
+ ASSERT_EQ(0, api::Trash<>::list(m_ioctx, entries, true));
+ trash_entries.insert(entries.begin(), entries.end());
+
+ expected_trash_entries = {
+ {.id = image_id1,
+ .name = image_name1,
+ .source = RBD_TRASH_IMAGE_SOURCE_USER},
+ };
+ ASSERT_EQ(expected_trash_entries, trash_entries);
+
+ std::vector<image_spec_t> expected_images = {
+ {.id = image_id2, .name = image_name2}
+ };
+ std::vector<image_spec_t> images;
+ ASSERT_EQ(0, rbd.list2(m_ioctx, &images));
+ ASSERT_EQ(expected_images, images);
+}
+
+} // namespace librbd
extern void register_test_mirroring_watcher();
extern void register_test_object_map();
extern void register_test_operations();
+extern void register_test_trash();
#endif // TEST_LIBRBD_INTERNALS
int main(int argc, char **argv)
register_test_mirroring_watcher();
register_test_object_map();
register_test_operations();
+ register_test_trash();
#endif // TEST_LIBRBD_INTERNALS
::testing::InitGoogleTest(&argc, argv);
case RBD_TRASH_IMAGE_SOURCE_MIGRATION:
del_source = "MIGRATION";
break;
- case RBD_TRASH_IMAGE_SOURCE_REMOVE:
- del_source = "REMOVE";
+ case RBD_TRASH_IMAGE_SOURCE_REMOVING:
+ del_source = "REMOVING";
break;
}