// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #include "gtest/gtest.h" #include "include/cephfs/libcephfs.h" #include "include/stat.h" #include "include/ceph_assert.h" #include "include/object.h" #include "include/stringify.h" #include #include #include #include #include #include #include #include #include #include using namespace std; class TestMount { ceph_mount_info* cmount = nullptr; char dir_path[64]; public: TestMount( const char* root_dir_name = "dir0") { ceph_create(&cmount, NULL); ceph_conf_read_file(cmount, NULL); ceph_conf_parse_env(cmount, NULL); ceph_assert(0 == ceph_mount(cmount, NULL)); sprintf(dir_path, "/%s_%d", root_dir_name, getpid()); ceph_assert(0 == ceph_mkdir(cmount, dir_path, 0777)); } ~TestMount() { if (cmount) { ceph_assert(0 == purge_dir("")); } ceph_rmdir(cmount, dir_path); ceph_shutdown(cmount); } int conf_get(const char *option, char *buf, size_t len) { return ceph_conf_get(cmount, option, buf, len); } string make_file_path(const char* relpath) { char path[PATH_MAX]; sprintf(path, "%s/%s", dir_path, relpath); return path; } string make_snap_name(const char* name) { char snap_name[64]; if (name && *name) { sprintf(snap_name, "%s_%d", name, getpid()); } else { // just simulate empty snapname snap_name[0] = 0; } return snap_name; } string make_snap_path(const char* sname, const char* subdir = nullptr) { char snap_path[PATH_MAX]; string snap_name = subdir ? concat_path(make_snap_name(sname), subdir) : make_snap_name(sname); sprintf(snap_path, ".snap/%s", snap_name.c_str()); return snap_path; } int mksnap(const char* name) { string snap_name = make_snap_name(name); return ceph_mksnap(cmount, dir_path, snap_name.c_str(), 0755, nullptr, 0); } int rmsnap(const char* name) { string snap_name = make_snap_name(name); return ceph_rmsnap(cmount, dir_path, snap_name.c_str()); } int get_snapid(const char* name, uint64_t* res) { ceph_assert(res); snap_info snap_info; char snap_path[PATH_MAX]; string snap_name = make_snap_name(name); sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name.c_str()); int r = ceph_get_snap_info(cmount, snap_path, &snap_info); if (r >= 0) { *res = snap_info.id; r = 0; } return r; } int write_full(const char* relpath, const string& data) { auto file_path = make_file_path(relpath); int fd = ceph_open(cmount, file_path.c_str(), O_WRONLY | O_CREAT, 0666); if (fd < 0) { return -EACCES; } int r = ceph_write(cmount, fd, data.c_str(), data.size(), 0); if (r >= 0) { ceph_truncate(cmount, file_path.c_str(), data.size()); ceph_fsync(cmount, fd, 0); } ceph_close(cmount, fd); return r; } string concat_path(string_view path, string_view name) { string s(path); if (s.empty() || s.back() != '/') { s += '/'; } s += name; return s; } int unlink(const char* relpath) { auto file_path = make_file_path(relpath); return ceph_unlink(cmount, file_path.c_str()); } int test_open(const char* relpath) { auto subdir_path = make_file_path(relpath); struct ceph_dir_result* ls_dir; int r = ceph_opendir(cmount, subdir_path.c_str(), &ls_dir); if (r != 0) { return r; } ceph_assert(0 == ceph_closedir(cmount, ls_dir)); return r; } int for_each_readdir(const char* relpath, std::function fn) { auto subdir_path = make_file_path(relpath); struct ceph_dir_result* ls_dir; int r = ceph_opendir(cmount, subdir_path.c_str(), &ls_dir); if (r != 0) { return r; } while (1) { struct dirent result; struct ceph_statx stx; r = ceph_readdirplus_r( cmount, ls_dir, &result, &stx, CEPH_STATX_BASIC_STATS, 0, NULL); if (!r) break; if (r < 0) { std::cerr << "ceph_readdirplus_r failed, error: " << r << std::endl; return r; } if (strcmp(result.d_name, ".") == 0 || strcmp(result.d_name, "..") == 0) { continue; } if (!fn(&result, &stx)) { r = -EINTR; break; } } ceph_assert(0 == ceph_closedir(cmount, ls_dir)); return r; } int readdir_and_compare(const char* relpath, const vector& expected0) { vector expected(expected0); auto end = expected.end(); int r = for_each_readdir(relpath, [&](const dirent* dire, const struct ceph_statx* stx) { std::string name(dire->d_name); auto it = std::find(expected.begin(), end, name); if (it == end) { std::cerr << "readdir_and_compare error: unexpected name:" << name << std::endl; return false; } expected.erase(it); return true; }); if (r == 0 && !expected.empty()) { std::cerr << __func__ << " error: left entries:" << std::endl; for (auto& e : expected) { std::cerr << e << std::endl; } std::cerr << __func__ << " ************" << std::endl; r = -ENOTEMPTY; } return r; } int for_each_readdir_snapdiff(const char* relpath, const char* snap1, const char* snap2, std::function fn) { auto s1 = make_snap_name(snap1); auto s2 = make_snap_name(snap2); ceph_snapdiff_info info; ceph_snapdiff_entry_t res_entry; int r = ceph_open_snapdiff(cmount, dir_path, relpath, s1.c_str(), s2.c_str(), &info); if (r != 0) { std::cerr << " Failed to open snapdiff, ret:" << r << std::endl; return r; } while (0 < (r = ceph_readdir_snapdiff(&info, &res_entry))) { if (strcmp(res_entry.dir_entry.d_name, ".") == 0 || strcmp(res_entry.dir_entry.d_name, "..") == 0) { continue; } if (!fn(&res_entry.dir_entry, res_entry.snapid)) { r = -EINTR; break; } } ceph_assert(0 == ceph_close_snapdiff(&info)); if (r != 0) { std::cerr << " Failed to readdir snapdiff, ret:" << r << " " << relpath << ", " << snap1 << " vs. " << snap2 << std::endl; } return r; } int readdir_snapdiff_and_compare(const char* relpath, const char* snap1, const char* snap2, const vector>& expected0) { vector> expected(expected0); auto end = expected.end(); int r = for_each_readdir_snapdiff(relpath, snap1, snap2, [&](const dirent* dire, uint64_t snapid) { pair p = std::make_pair(dire->d_name, snapid); auto it = std::find(expected.begin(), end, p); if (it == end) { std::cerr << "readdir_snapdiff_and_compare error: unexpected name:" << dire->d_name << "/" << snapid << std::endl; return false; } expected.erase(it); return true; }); if (r == 0 && !expected.empty()) { std::cerr << __func__ << " error: left entries:" << std::endl; for (auto& e : expected) { std::cerr << e.first << "/" << e.second << std::endl; } std::cerr << __func__ << " ************" << std::endl; r = -ENOTEMPTY; } return r; } int mkdir(const char* relpath) { auto path = make_file_path(relpath); return ceph_mkdir(cmount, path.c_str(), 0777); } int rmdir(const char* relpath) { auto path = make_file_path(relpath); return ceph_rmdir(cmount, path.c_str()); } int purge_dir(const char* relpath0, bool inclusive = true) { int r = for_each_readdir(relpath0, [&](const dirent* dire, const struct ceph_statx* stx) { string relpath = concat_path(relpath0, dire->d_name); if (S_ISDIR(stx->stx_mode)) { purge_dir(relpath.c_str()); rmdir(relpath.c_str()); } else { unlink(relpath.c_str()); } return true; }); if (r != 0) { return r; } if (*relpath0 != 0) { r = rmdir(relpath0); } return r; } void remove_all() { purge_dir("/", false); } ceph_mount_info* get_cmount() { return cmount; } void verify_snap_diff(vector>& expected, const char* relpath, const char* snap1, const char* snap2); void print_snap_diff(const char* relpath, const char* snap1, const char* snap2); void prepareSnapDiffLib1Cases(); void prepareSnapDiffLib2Cases(); void prepareSnapDiffLib3Cases(); void prepareHugeSnapDiff(const std::string& name_prefix_start, const std::string& name_prefix_bulk, const std::string& name_prefix_end, size_t file_count, bool bulk_diff); }; // Helper function to verify readdir_snapdiff returns expected results void TestMount::verify_snap_diff(vector>& expected, const char* relpath, const char* snap1, const char* snap2) { std::cout << "---------" << snap1 << " vs. " << snap2 << " diff listing verification for /" << (relpath ? relpath : "") << std::endl; ASSERT_EQ(0, readdir_snapdiff_and_compare(relpath, snap1, snap2, expected)); }; // Helper function to print readdir_snapdiff results void TestMount::print_snap_diff(const char* relpath, const char* snap1, const char* snap2) { std::cout << "---------" << snap1 << " vs. " << snap2 << " diff listing for /" << (relpath ? relpath : "") << std::endl; ASSERT_EQ(0, for_each_readdir_snapdiff(relpath, snap1, snap2, [&](const dirent* dire, uint64_t snapid) { std::cout << dire->d_name << " snap " << snapid << std::endl; return true; })); }; /* The following method creates some files/folders/snapshots layout, described in the sheet below. We're to test SnapDiff readdir API against that structure. * where: - xN denotes file 'x' version N. - X denotes folder name - * denotes no/removed file/folder # snap1 snap2 # fileA1 | fileA2 | # * | fileB2 | # fileC1 | * | # fileD1 | fileD1 | # dirA | dirA | # dirA/fileA1 | dirA/fileA2 | # * | dirB | # * | dirB/fileb2 | # dirC | * | # dirC/filec1 | * | # dirD | dirD | # dirD/fileD1 | dirD/fileD1 | */ void TestMount::prepareSnapDiffLib1Cases() { //************ snap1 ************* ASSERT_LE(0, write_full("fileA", "hello world")); ASSERT_LE(0, write_full("fileC", "hello world to be removed")); ASSERT_LE(0, write_full("fileD", "hello world unmodified")); ASSERT_EQ(0, mkdir("dirA")); ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v1")); ASSERT_EQ(0, mkdir("dirC")); ASSERT_LE(0, write_full("dirC/filec", "file 'C/c' v1")); ASSERT_EQ(0, mkdir("dirD")); ASSERT_LE(0, write_full("dirD/filed", "file 'D/d' v1")); ASSERT_EQ(0, mksnap("snap1")); //************ snap2 ************* ASSERT_LE(0, write_full("fileA", "hello world again in A")); ASSERT_LE(0, write_full("fileB", "hello world in B")); ASSERT_EQ(0, unlink("fileC")); ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v2")); ASSERT_EQ(0, purge_dir("dirC")); ASSERT_EQ(0, mkdir("dirB")); ASSERT_LE(0, write_full("dirB/fileb", "file 'B/b' v2")); ASSERT_EQ(0, mksnap("snap2")); } /* * Basic functionality testing for the SnapDiff readdir API */ TEST(LibCephFS, SnapDiffLib) { TestMount test_mount; // Create simple directory tree with a couple of snapshots // to test against test_mount.prepareSnapDiffLib1Cases(); uint64_t snapid1; uint64_t snapid2; // learn snapshot ids and do basic verification ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); ASSERT_GT(snapid1, 0); ASSERT_GT(snapid2, 0); ASSERT_GT(snapid2, snapid1); std::cout << snapid1 << " vs. " << snapid2 << std::endl; // // Make sure root listing for snapshot snap1 is as expected // { std::cout << "---------snap1 listing verification---------" << std::endl; string snap_path = test_mount.make_snap_path("snap1"); vector expected; expected.push_back("fileA"); expected.push_back("fileC"); expected.push_back("fileD"); expected.push_back("dirA"); expected.push_back("dirC"); expected.push_back("dirD"); ASSERT_EQ(0, test_mount.readdir_and_compare(snap_path.c_str(), expected)); } // // Make sure root listing for snapshot snap2 is as expected // { std::cout << "---------snap2 listing verification---------" << std::endl; string snap_path = test_mount.make_snap_path("snap2"); vector expected; expected.push_back("fileA"); expected.push_back("fileB"); expected.push_back("fileD"); expected.push_back("dirA"); expected.push_back("dirB"); expected.push_back("dirD"); ASSERT_EQ(0, test_mount.readdir_and_compare(snap_path.c_str(), expected)); } // // Print snap1 vs. snap2 delta for the root // test_mount.print_snap_diff("", "snap1", "snap2"); // // Make sure snap1 vs. snap2 delta for the root is as expected // { vector> expected; expected.emplace_back("fileA", snapid2); expected.emplace_back("fileB", snapid2); expected.emplace_back("fileC", snapid1); expected.emplace_back("dirA", snapid2); expected.emplace_back("dirB", snapid2); expected.emplace_back("dirC", snapid1); expected.emplace_back("dirD", snapid2); test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); } // // Make sure snap1 vs. snap2 delta for /dirA is as expected // { vector> expected; expected.emplace_back("fileA", snapid2); test_mount.verify_snap_diff(expected, "dirA", "snap1", "snap2"); } // // Make sure snap1 vs. snap2 delta for /dirB is as expected // { vector> expected; expected.emplace_back("fileb", snapid2); test_mount.verify_snap_diff(expected, "dirB", "snap1", "snap2"); } // // Make sure snap1 vs. snap2 delta for /dirC is as expected // { vector> expected; expected.emplace_back("filec", snapid1); test_mount.verify_snap_diff(expected, "dirC", "snap2", "snap1"); } // // Make sure snap1 vs. snap2 delta for /dirD is as expected // { vector> expected; test_mount.verify_snap_diff(expected, "dirD", "snap1", "snap2"); } // Make sure SnapDiff returns an error when provided with the same // snapshot name for both parties A and B. { string snap_path = test_mount.make_snap_path("snap2"); string snap_other_path = snap_path; std::cout << "---------invalid snapdiff params, the same snaps---------" << std::endl; ASSERT_EQ(-EINVAL, test_mount.for_each_readdir_snapdiff( "", "snap2", "snap2", [&](const dirent* dire, uint64_t snapid) { return true; })); } // Make sure SnapDiff returns an error when provided with an empty // snapshot name for one of the parties { std::cout << "---------invalid snapdiff params, no snap_other ---------" << std::endl; string snap_path = test_mount.make_snap_path("snap2"); string snap_other_path; ASSERT_EQ(-EINVAL, test_mount.for_each_readdir_snapdiff( "", "snap2", "", [&](const dirent* dire, uint64_t snapid) { return true; })); } std::cout << "------------- closing -------------" << std::endl; ASSERT_EQ(0, test_mount.purge_dir("")); ASSERT_EQ(0, test_mount.rmsnap("snap1")); ASSERT_EQ(0, test_mount.rmsnap("snap2")); } /* The following method creates some files/folders/snapshots layout, described in the sheet below. We're to test SnapDiff readdir API against that structure. * where: - xN denotes file 'x' version N. - X denotes folder name - * denotes no/removed file/folder # snap1 snap2 snap3 head # fileA1 | fileA2 | fileA2 # * | fileB2 | fileB2 # fileC1 | * | fileC3 # fileD1 | fileD1 | fileD3 # * | * | fileE3 # fileF1 | * | * # fileG1 | fileG2 | * # dirA | dirA | * # dirA/fileA1 | dirA/fileA2 | * # * | dirB | * # * | dirB/fileb2 | * # dirC | * | * # dirC/filec1 | * | * # dirD | dirD | dirD # dirD/filed1 | dirD/filed1 | dirD/filed1 */ void TestMount::prepareSnapDiffLib2Cases() { //************ snap1 ************* ASSERT_LE(0, write_full("fileA", "hello world")); ASSERT_LE(0, write_full("fileC", "hello world to be removed temporarily")); ASSERT_LE(0, write_full("fileD", "hello world unmodified")); ASSERT_LE(0, write_full("fileF", "hello world to be removed completely")); ASSERT_LE(0, write_full("fileG", "hello world to be overwritten at snap2")); ASSERT_EQ(0, mkdir("dirA")); ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v1")); ASSERT_EQ(0, mkdir("dirC")); ASSERT_LE(0, write_full("dirC/filec", "file 'C/c' v1")); ASSERT_EQ(0, mkdir("dirD")); ASSERT_LE(0, write_full("dirD/filed", "file 'D/d' v1")); ASSERT_EQ(0, mksnap("snap1")); //************ snap2 ************* ASSERT_LE(0, write_full("fileA", "hello world again in A")); ASSERT_LE(0, write_full("fileB", "hello world in B")); ASSERT_LE(0, write_full("fileG", "hello world to be removed at snap3")); ASSERT_EQ(0, unlink("fileC")); ASSERT_EQ(0, unlink("fileF")); ASSERT_LE(0, write_full("dirA/fileA", "file 'A/a' v2")); ASSERT_EQ(0, mkdir("dirB")); ASSERT_LE(0, write_full("dirB/fileb", "file 'B/b' v2")); ASSERT_EQ(0, purge_dir("dirC")); ASSERT_EQ(0, mksnap("snap2")); //************ snap3 ************* ASSERT_LE(0, write_full("fileC", "hello world in C recovered")); ASSERT_LE(0, write_full("fileD", "hello world in D now modified")); ASSERT_LE(0, write_full("fileE", "file 'E' created at snap3")); ASSERT_EQ(0, unlink("fileG")); ASSERT_EQ(0, purge_dir("dirA")); ASSERT_EQ(0, purge_dir("dirB")); ASSERT_EQ(0, mksnap("snap3")); } /* The following method creates a folder with tons of file updated between two snapshots We're to test SnapDiff readdir API against that structure. * where: - xN denotes file 'x' version N. - X denotes folder name - * denotes no/removed file/folder # snap1 snap2 * aaaaA1 | aaaaA1 | * aaaaB1 | * | * * | aaaaC2 | * aaaaD1 | aaaaD2 | # file1 | file2| * fileZ1 | fileA1 | * zzzzA1 | zzzzA1 | * zzzzB1 | * | * * | zzzzC2 | * zzzzD1 | zzzzD2 | */ void TestMount::prepareHugeSnapDiff(const std::string& name_prefix_start, const std::string& name_prefix_bulk, const std::string& name_prefix_end, size_t file_count, bool bulk_diff) { //************ snap1 ************* std::string startA = name_prefix_start + "A"; std::string startB = name_prefix_start + "B"; std::string startC = name_prefix_start + "C"; std::string startD = name_prefix_start + "D"; std::string endA = name_prefix_end + "A"; std::string endB = name_prefix_end + "B"; std::string endC = name_prefix_end + "C"; std::string endD = name_prefix_end + "D"; ASSERT_LE(0, write_full(startA.c_str(), "hello world")); ASSERT_LE(0, write_full(startB.c_str(), "hello world")); ASSERT_LE(0, write_full(startD.c_str(), "hello world")); for(size_t i = 0; i < file_count; i++) { auto s = name_prefix_bulk + stringify(i); ASSERT_LE(0, write_full(s.c_str(), "hello world")); } ASSERT_LE(0, write_full(endA.c_str(), "hello world")); ASSERT_LE(0, write_full(endB.c_str(), "hello world")); ASSERT_LE(0, write_full(endD.c_str(), "hello world")); ASSERT_EQ(0, mksnap("snap1")); ASSERT_LE(0, unlink(startB.c_str())); ASSERT_LE(0, write_full(startC.c_str(), "hello world2")); ASSERT_LE(0, write_full(startD.c_str(), "hello world2")); if (bulk_diff) { for(size_t i = 0; i < file_count; i++) { auto s = std::string(name_prefix_bulk) + stringify(i); ASSERT_LE(0, write_full(s.c_str(), "hello world2")); } } ASSERT_LE(0, unlink(endB.c_str())); ASSERT_LE(0, write_full(endC.c_str(), "hello world2")); ASSERT_LE(0, write_full(endD.c_str(), "hello world2")); ASSERT_EQ(0, mksnap("snap2")); } /* * More versatile SnapDiff readdir API verification, * includes 3 different snapshots and interleaving/repetitive calls to make sure * the results aren't spoiled due to caching. */ TEST(LibCephFS, SnapDiffLib2) { TestMount test_mount; test_mount.prepareSnapDiffLib2Cases(); // Create simple directory tree with a couple of snapshots to test against uint64_t snapid1; uint64_t snapid2; uint64_t snapid3; ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3)); std::cout << snapid1 << " vs. " << snapid2 << " vs. " << snapid3 << std::endl; ASSERT_GT(snapid1, 0); ASSERT_GT(snapid2, 0); ASSERT_GT(snapid3, 0); ASSERT_GT(snapid2, snapid1); ASSERT_GT(snapid3, snapid2); // define a labda which verifies snap1/snap2/snap3 listings auto verify_snap_listing = [&]() { { string snap_path = test_mount.make_snap_path("snap1"); std::cout << "---------snap1 listing verification---------" << std::endl; vector expected; expected.push_back("fileA"); expected.push_back("fileC"); expected.push_back("fileD"); expected.push_back("fileF"); expected.push_back("fileG"); expected.push_back("dirA"); expected.push_back("dirC"); expected.push_back("dirD"); ASSERT_EQ(0, test_mount.readdir_and_compare(snap_path.c_str(), expected)); } { std::cout << "---------snap2 listing verification---------" << std::endl; string snap_path = test_mount.make_snap_path("snap2"); vector expected; expected.push_back("fileA"); expected.push_back("fileB"); expected.push_back("fileD"); expected.push_back("fileG"); expected.push_back("dirA"); expected.push_back("dirB"); expected.push_back("dirD"); ASSERT_EQ(0, test_mount.readdir_and_compare(snap_path.c_str(), expected)); } { std::cout << "---------snap3 listing verification---------" << std::endl; string snap_path = test_mount.make_snap_path("snap3"); vector expected; expected.push_back("fileA"); expected.push_back("fileB"); expected.push_back("fileC"); expected.push_back("fileD"); expected.push_back("fileE"); expected.push_back("dirD"); ASSERT_EQ(0, test_mount.readdir_and_compare(snap_path.c_str(), expected)); } }; // Prepare expected delta for snap1 vs. snap2 vector> snap1_2_diff_expected; snap1_2_diff_expected.emplace_back("fileA", snapid2); snap1_2_diff_expected.emplace_back("fileB", snapid2); snap1_2_diff_expected.emplace_back("fileC", snapid1); snap1_2_diff_expected.emplace_back("fileF", snapid1); snap1_2_diff_expected.emplace_back("fileG", snapid2); snap1_2_diff_expected.emplace_back("dirA", snapid2); snap1_2_diff_expected.emplace_back("dirB", snapid2); snap1_2_diff_expected.emplace_back("dirC", snapid1); snap1_2_diff_expected.emplace_back("dirD", snapid2); // Prepare expected delta for snap1 vs. snap3 vector> snap1_3_diff_expected; snap1_3_diff_expected.emplace_back("fileA", snapid3); snap1_3_diff_expected.emplace_back("fileB", snapid3); snap1_3_diff_expected.emplace_back("fileC", snapid3); snap1_3_diff_expected.emplace_back("fileD", snapid3); snap1_3_diff_expected.emplace_back("fileE", snapid3); snap1_3_diff_expected.emplace_back("fileF", snapid1); snap1_3_diff_expected.emplace_back("fileG", snapid1); snap1_3_diff_expected.emplace_back("dirA", snapid1); snap1_3_diff_expected.emplace_back("dirC", snapid1); snap1_3_diff_expected.emplace_back("dirD", snapid3); // Prepare expected delta for snap2 vs. snap3 vector> snap2_3_diff_expected; snap2_3_diff_expected.emplace_back("fileC", snapid3); snap2_3_diff_expected.emplace_back("fileD", snapid3); snap2_3_diff_expected.emplace_back("fileE", snapid3); snap2_3_diff_expected.emplace_back("fileG", snapid2); snap2_3_diff_expected.emplace_back("dirA", snapid2); snap2_3_diff_expected.emplace_back("dirB", snapid2); snap2_3_diff_expected.emplace_back("dirD", snapid3); // Check snapshot listings on a cold cache verify_snap_listing(); // Check snapshot listings on a warm cache verify_snap_listing(); // served from cache // Print snap1 vs. snap2 delta against the root folder test_mount.print_snap_diff("", "snap1", "snap2"); // Verify snap1 vs. snap2 delta for the root test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap1", "snap2"); // Check snapshot listings on a warm cache once again // to make sure it wasn't spoiled by SnapDiff verify_snap_listing(); // served from cache // Verify snap2 vs. snap1 delta test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap2", "snap1"); // Check snapshot listings on a warm cache once again // to make sure it wasn't spoiled by SnapDiff verify_snap_listing(); // served from cache // Verify snap1 vs. snap3 delta for the root test_mount.verify_snap_diff(snap1_3_diff_expected, "", "snap1", "snap3"); // Verify snap2 vs. snap3 delta for the root test_mount.verify_snap_diff(snap2_3_diff_expected, "", "snap2", "snap3"); // Check snapshot listings on a warm cache once again // to make sure it wasn't spoiled by SnapDiff verify_snap_listing(); // served from cache // Print snap1 vs. snap2 delta against /dirA folder test_mount.print_snap_diff("dirA", "snap1", "snap2"); // Verify snap1 vs. snap2 delta for /dirA { vector> expected; expected.emplace_back("fileA", snapid2); test_mount.verify_snap_diff(expected, "dirA", "snap1", "snap2"); } // Print snap1 vs. snap2 delta against /dirB folder test_mount.print_snap_diff("dirB", "snap1", "snap2"); // Verify snap1 vs. snap2 delta for /dirB { vector> expected; expected.emplace_back("fileb", snapid2); test_mount.verify_snap_diff(expected, "dirB", "snap1", "snap2"); } // Print snap1 vs. snap2 delta against /dirD folder test_mount.print_snap_diff("dirD", "snap1", "snap2"); // Verify snap1 vs. snap2 delta for /dirD { vector> expected; test_mount.verify_snap_diff(expected, "dirD", "snap1", "snap2"); } // Check snapshot listings on a warm cache once again // to make sure it wasn't spoiled by SnapDiff verify_snap_listing(); // served from cache // Verify snap1 vs. snap2 delta for the root once again test_mount.verify_snap_diff(snap1_2_diff_expected, "", "snap1", "snap2"); // Verify snap2 vs. snap3 delta for the root once again test_mount.verify_snap_diff(snap2_3_diff_expected, "", "snap3", "snap2"); // Verify snap1 vs. snap3 delta for the root once again test_mount.verify_snap_diff(snap1_3_diff_expected, "", "snap1", "snap3"); std::cout << "------------- closing -------------" << std::endl; ASSERT_EQ(0, test_mount.purge_dir("")); ASSERT_EQ(0, test_mount.rmsnap("snap1")); ASSERT_EQ(0, test_mount.rmsnap("snap2")); ASSERT_EQ(0, test_mount.rmsnap("snap3")); } /* The following method creates some files/folders/snapshots layout, described in the sheet below. We're to test SnapDiff against that structure. * where: - xN denotes file 'x' version N. - X denotes folder name - * denotes no/removed file/folder # snap1 snap2 snap3 head # a1 | a1 | a3 | a4 # b1 | b2 | b3 | b3 # c1 | * | * | * # * | d2 | d3 | d3 # f1 | f2 | * | * # ff1 | ff1 | * | * # g1 | * | g3 | g3 # * | * | * | h4 # i1 | i1 | i1 | i1 # S | S | S | S # S/sa1 | S/sa2 | S/sa3 | S/sa3 # * | * | * | S/sh4 # * | T | T | T # * | T/td2 | T/td3 | T/td3 # C | * | * | * # C/cc1 | * | * | * # C/C1 | * | * | * # C/C1/c1| * | * | * # G | * | G | G # G/gg1 | * | G/gg3 | G/gg3 # * | k2 | * | * # * | l2 | l2 | * # * | K | * | * # * | K/kk2 | * | * # * | * | H | H # * | * | H/hh3 | H/hh3 # I | I | I | * # I/ii1 | I/ii2 | I/ii3 | * # I/iii1 | I/iii1 | I/iii3| * # * | * | I/iiii3| * # * | I/J | I/J | * # * | I/J/i2 | I/J/i3 | * # * | I/J/j2 | I/J/j2 | * # * | I/J/k2 | * | * # * | * | I/J/l3 | * # L | L | L | L # L/ll1 | L/ll1 | L/ll3 | L/ll3 # L/LL | L/LL | L/LL | L/LL # * | L/LL/ll2| L/LL/ll3| L/LL/ll4 # * | L/LM | * | * # * | L/LM/lm2| * | * # * | L/LN | L/LN | * */ void TestMount::prepareSnapDiffLib3Cases() { //************ snap1 ************* ASSERT_LE(0, write_full("a", "file 'a' v1")); ASSERT_LE(0, write_full("b", "file 'b' v1")); ASSERT_LE(0, write_full("c", "file 'c' v1")); ASSERT_LE(0, write_full("e", "file 'e' v1")); ASSERT_LE(0, write_full("~e", "file '~e' v1")); ASSERT_LE(0, write_full("f", "file 'f' v1")); ASSERT_LE(0, write_full("ff", "file 'ff' v1")); ASSERT_LE(0, write_full("g", "file 'g' v1")); ASSERT_LE(0, write_full("i", "file 'i' v1")); ASSERT_EQ(0, mkdir("S")); ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v1")); ASSERT_EQ(0, mkdir("C")); ASSERT_LE(0, write_full("C/cc", "file 'C/cc' v1")); ASSERT_EQ(0, mkdir("C/CC")); ASSERT_LE(0, write_full("C/CC/c", "file 'C/CC/c' v1")); ASSERT_EQ(0, mkdir("G")); ASSERT_LE(0, write_full("G/gg", "file 'G/gg' v1")); ASSERT_EQ(0, mkdir("I")); ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v1")); ASSERT_LE(0, write_full("I/iii", "file 'I/iii' v1")); ASSERT_EQ(0, mkdir("L")); ASSERT_LE(0, write_full("L/ll", "file 'L/ll' v1")); ASSERT_EQ(0, mkdir("L/LL")); ASSERT_EQ(0, mksnap("snap1")); //************ snap2 ************* ASSERT_LE(0, write_full("b", "file 'b' v2")); ASSERT_EQ(0, unlink("c")); ASSERT_LE(0, write_full("d", "file 'd' v2")); ASSERT_LE(0, write_full("e", "file 'e' v2")); ASSERT_LE(0, write_full("~e", "file '~e' v2")); ASSERT_LE(0, write_full("f", "file 'f' v2")); ASSERT_EQ(0, unlink("g")); ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v2")); ASSERT_EQ(0, mkdir("T")); ASSERT_LE(0, write_full("T/td", "file 'T/td' v2")); ASSERT_EQ(0, purge_dir("C")); ASSERT_EQ(0, purge_dir("G")); ASSERT_LE(0, write_full("k", "file 'k' v2")); ASSERT_LE(0, write_full("l", "file 'l' v2")); ASSERT_EQ(0, mkdir("K")); ASSERT_LE(0, write_full("K/kk", "file 'K/kk' v2")); ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v2")); ASSERT_EQ(0, mkdir("I/J")); ASSERT_LE(0, write_full("I/J/i", "file 'I/J/i' v2")); ASSERT_LE(0, write_full("I/J/j", "file 'I/J/j' v2")); ASSERT_LE(0, write_full("I/J/k", "file 'I/J/k' v2")); ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' v2")); ASSERT_EQ(0, mkdir("L/LM")); ASSERT_LE(0, write_full("L/LM/lm", "file 'L/LM/lm' v2")); ASSERT_EQ(0, mkdir("L/LN")); ASSERT_EQ(0, mksnap("snap2")); //************ snap3 ************* ASSERT_LE(0, write_full("a", "file 'a' v3")); ASSERT_LE(0, write_full("b", "file 'b' v3")); ASSERT_LE(0, write_full("d", "file 'd' v3")); ASSERT_EQ(0, unlink("e")); ASSERT_EQ(0, unlink("~e")); ASSERT_EQ(0, unlink("f")); ASSERT_EQ(0, unlink("ff")); ASSERT_LE(0, write_full("g", "file 'g' v3")); ASSERT_LE(0, write_full("S/sa", "file 'S/sa' v3")); ASSERT_LE(0, write_full("T/td", "file 'T/td' v3")); ASSERT_EQ(0, mkdir("G")); ASSERT_LE(0, write_full("G/gg", "file 'G/gg' v3")); ASSERT_EQ(0, unlink("k")); ASSERT_EQ(0, purge_dir("K")); ASSERT_EQ(0, mkdir("H")); ASSERT_LE(0, write_full("H/hh", "file 'H/hh' v3")); ASSERT_LE(0, write_full("I/ii", "file 'I/ii' v3")); ASSERT_LE(0, write_full("I/iii", "file 'I/iii' v3")); ASSERT_LE(0, write_full("I/iiii", "file 'I/iiii' v3")); ASSERT_LE(0, write_full("I/J/i", "file 'I/J/i' v3")); ASSERT_EQ(0, unlink("I/J/k")); ASSERT_LE(0, write_full("I/J/l", "file 'I/J/l' v3")); ASSERT_LE(0, write_full("L/ll", "file 'L/ll' v3")); ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' v3")); ASSERT_EQ(0, purge_dir("L/LM")); ASSERT_EQ(0, mksnap("snap3")); //************ head ************* ASSERT_LE(0, write_full("a", "file 'a' head")); ASSERT_LE(0, write_full("h", "file 'h' head")); ASSERT_LE(0, write_full("S/sh", "file 'S/sh' head")); ASSERT_EQ(0, unlink("l")); ASSERT_EQ(0, purge_dir("I")); ASSERT_LE(0, write_full("L/LL/ll", "file 'L/LL/ll' head")); ASSERT_EQ(0, purge_dir("L/LN")); } // // This case tests SnapDiff functionality for snap1/snap2 snapshot delta // It operates against FS layout created by prepareSnapDiffCases() method, // see relevant table before that function for FS state overview. // TEST(LibCephFS, SnapDiffCases1_2) { TestMount test_mount; // Create directory tree evolving through a bunch of snapshots test_mount.prepareSnapDiffLib3Cases(); uint64_t snapid1; uint64_t snapid2; ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); std::cout << snapid1 << " vs. " << snapid2 << std::endl; ASSERT_GT(snapid1, 0); ASSERT_GT(snapid2, 0); ASSERT_GT(snapid2, snapid1); // Print snapshot delta (snap1 vs. snap2) results for root in a // human-readable form. test_mount.print_snap_diff("", "snap1", "snap2"); { // Make sure the root delta is as expected // One should use columns snap1 and snap2 from // the table preceeding prepareSnapDiffCases() function // to learn which names to expect in the delta. // // - file 'a' is unchanged hence not present in delta // - file 'ff' is unchanged hence not present in delta // - file 'i' is unchanged hence not present in delta // vector> expected; expected.emplace_back("b", snapid2); // file 'b' is updated in snap2 expected.emplace_back("c", snapid1); // file 'c' is removed in snap2 expected.emplace_back("d", snapid2); // file 'd' is created in snap2 expected.emplace_back("e", snapid2); // file 'e' is updated in snap2 expected.emplace_back("~e", snapid2); // file '~e' is updated in snap2 expected.emplace_back("f", snapid2); // file 'f' is updated in snap2 expected.emplace_back("g", snapid1); // file 'g' is removed in snap2 expected.emplace_back("S", snapid2); // folder 'S' is present in snap2 hence reported expected.emplace_back("T", snapid2); // folder 'T' is created in snap2 expected.emplace_back("C", snapid1); // folder 'C' is removed in snap2 expected.emplace_back("G", snapid1); // folder 'G' is removed in snap2 expected.emplace_back("k", snapid2); // file 'k' is created in snap2 expected.emplace_back("l", snapid2); // file 'l' is created in snap2 expected.emplace_back("K", snapid2); // folder 'K' is created in snap2 expected.emplace_back("I", snapid2); // folder 'I' is created in snap2 expected.emplace_back("L", snapid2); // folder 'L' is present in snap2 but got more // subfolders test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); } { // // Make sure snapshot delta for /S (existed at both snap1 and snap2) // is as expected // vector> expected; expected.emplace_back("sa", snapid2); test_mount.verify_snap_diff(expected, "S", "snap1", "snap2"); } { // // Make sure snapshot delta for /T (created at snap2) // is as expected // vector> expected; expected.emplace_back("td", snapid2); test_mount.verify_snap_diff(expected, "T", "snap1", "snap2"); } { // // Make sure snapshot delta for /C (removed at snap2) // is as expected // vector> expected; expected.emplace_back("cc", snapid1); expected.emplace_back("CC", snapid1); test_mount.verify_snap_diff(expected, "C", "snap2", "snap1"); } { // // Make sure snapshot delta for /C/CC (removed at snap2) // is as expected // vector> expected; expected.emplace_back("c", snapid1); test_mount.verify_snap_diff(expected, "C/CC", "snap2", "snap1"); } { // // Make sure snapshot delta for /I (created at snap2) // is as expected // vector> expected; expected.emplace_back("ii", snapid2); expected.emplace_back("J", snapid2); test_mount.verify_snap_diff(expected, "I", "snap1", "snap2"); } { // // Make sure snapshot delta for /I/J (created at snap2) // is as expected // vector> expected; expected.emplace_back("i", snapid2); expected.emplace_back("j", snapid2); expected.emplace_back("k", snapid2); test_mount.verify_snap_diff(expected, "I/J", "snap1", "snap2"); } { // // Make sure snapshot delta for /L (extended at snap2) // is as expected // vector> expected; expected.emplace_back("LL", snapid2); expected.emplace_back("LM", snapid2); expected.emplace_back("LN", snapid2); test_mount.verify_snap_diff(expected, "L", "snap1", "snap2"); } { // // Make sure snapshot delta for /L/LL (updated at snap2) // is as expected // vector> expected; expected.emplace_back("ll", snapid2); test_mount.verify_snap_diff(expected, "L/LL", "snap1", "snap2"); } { // // Make sure snapshot delta for /L/LN (created empty at snap2) // is as expected // vector> expected; test_mount.verify_snap_diff(expected, "L/LN", "snap1", "snap2"); } { // Make sure snapshot delta for /L/LM (created at snap2) // is as expected // vector> expected; expected.emplace_back("lm", snapid2); test_mount.verify_snap_diff(expected, "L/LM", "snap1", "snap2"); } std::cout << "-------------" << std::endl; test_mount.remove_all(); test_mount.rmsnap("snap1"); test_mount.rmsnap("snap2"); test_mount.rmsnap("snap3"); } // // This case tests SnapDiff functionality for snap2/snap3 snapshot delta // retrieved through .snap path-based query API. // It operates against FS layout created by prepareSnapDiffCases() method, // see relevant table before that function for FS state overview. // TEST(LibCephFS, SnapDiffCases2_3) { TestMount test_mount; // Create directory tree evolving through a bunch of snapshots test_mount.prepareSnapDiffLib3Cases(); uint64_t snapid2; uint64_t snapid3; ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3)); std::cout << snapid2 << " vs. " << snapid3 << std::endl; ASSERT_GT(snapid3, 0); ASSERT_GT(snapid3, 0); ASSERT_GT(snapid3, snapid2); // Print snapshot delta (snap2 vs. snap3) results for root in a // human-readable form. test_mount.print_snap_diff("", "snap2", "snap3"); { // Make sure the root delta is as expected // One should use columns snap1 and snap2 from // the table preceeding prepareSnapDiffCases() function // to learn which names to expect in the delta. // // - file 'c' is removed since snap1 hence not present in delta // - file 'l' is unchanged hence not present in delta // - file 'i' is unchanged hence not present in delta // vector> expected; expected.emplace_back("a", snapid3); // file 'a' is updated in snap3 expected.emplace_back("b", snapid3); // file 'b' is updated in snap3 expected.emplace_back("d", snapid3); // file 'd' is updated in snap3 expected.emplace_back("~e", snapid2); // file '~e' is removed in snap3 expected.emplace_back("e", snapid2); // file 'e' is removed in snap3 expected.emplace_back("f", snapid2); // file 'f' is removed in snap3 expected.emplace_back("ff", snapid2); // file 'ff' is removed in snap3 expected.emplace_back("g", snapid3); // file 'g' re-appeared in snap3 expected.emplace_back("S", snapid3); // folder 'S' is present in snap3 hence reported expected.emplace_back("T", snapid3); // folder 'T' is present in snap3 hence reported expected.emplace_back("G", snapid3); // folder 'G' re-appeared in snap3 hence reported expected.emplace_back("k", snapid2); // file 'k' is removed in snap3 expected.emplace_back("K", snapid2); // folder 'K' is removed in snap3 expected.emplace_back("H", snapid3); // folder 'H' is created in snap3 hence reported expected.emplace_back("I", snapid3); // folder 'I' is present in snap3 hence reported expected.emplace_back("L", snapid3); // folder 'L' is present in snap3 hence reported test_mount.verify_snap_diff(expected, "", "snap2", "snap3"); } { // // Make sure snapshot delta for /S (children updated) is as expected // vector> expected; expected.emplace_back("sa", snapid3); test_mount.verify_snap_diff(expected, "S", "snap2", "snap3"); } { // // Make sure snapshot delta for /T (children updated) is as expected // vector> expected; expected.emplace_back("td", snapid3); test_mount.verify_snap_diff(expected, "T", "snap2", "snap3"); } { // // Make sure snapshot delta for /G (re-appeared) is as expected // vector> expected; expected.emplace_back("gg", snapid3); test_mount.verify_snap_diff(expected, "G", "snap2", "snap3"); } { // // Make sure snapshot delta for /K (removed) is as expected // vector> expected; expected.emplace_back("kk", snapid2); test_mount.verify_snap_diff(expected, "K", "snap3", "snap2"); } { // // Make sure snapshot delta for /H (created) is as expected // vector> expected; expected.emplace_back("hh", snapid3); test_mount.verify_snap_diff(expected, "H", "snap2", "snap3"); } { // // Make sure snapshot delta for /I (children updated) is as expected // vector> expected; expected.emplace_back("ii", snapid3); expected.emplace_back("iii", snapid3); expected.emplace_back("iiii", snapid3); expected.emplace_back("J", snapid3); test_mount.verify_snap_diff(expected, "I", "snap2", "snap3"); } { // // Make sure snapshot delta for /I/J (children updated/removed) is as expected // vector> expected; expected.emplace_back("i", snapid3); expected.emplace_back("k", snapid2); expected.emplace_back("l", snapid3); test_mount.verify_snap_diff(expected, "I/J", "snap2", "snap3"); } { // // Make sure snapshot delta for /L (children updated/removed) is as expected // vector> expected; expected.emplace_back("ll", snapid3); expected.emplace_back("LL", snapid3); expected.emplace_back("LM", snapid2); expected.emplace_back("LN", snapid3); test_mount.verify_snap_diff(expected, "L", "snap2", "snap3"); } { // // Make sure snapshot delta for /L/LL (children updated) is as expected // vector> expected; expected.emplace_back("ll", snapid3); test_mount.verify_snap_diff(expected, "L/LL", "snap2", "snap3"); } { // // Make sure snapshot delta for /L/LM (removed) is as expected // vector> expected; expected.emplace_back("lm", snapid2); test_mount.verify_snap_diff(expected, "L/LM", "snap3", "snap2"); } { // // Make sure snapshot delta for /L/LN (created empty) is as expected // vector> expected; test_mount.verify_snap_diff(expected, "L/LN", "snap2", "snap3"); } test_mount.remove_all(); test_mount.rmsnap("snap1"); test_mount.rmsnap("snap2"); test_mount.rmsnap("snap3"); } // // This case tests SnapDiff functionality for snap1/snap3 snapshot delta // retrieved through .snap path-based query API. // It operates against FS layout created by prepareSnapDiffCases() method, // see relevant table before that function for FS state overview. // TEST(LibCephFS, SnapDiffCases1_3) { TestMount test_mount; // Create directory tree evolving through a bunch of snapshots test_mount.prepareSnapDiffLib3Cases(); uint64_t snapid1; uint64_t snapid3; ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap3", &snapid3)); std::cout << snapid1 << " vs. " << snapid3 << std::endl; ASSERT_GT(snapid3, 0); ASSERT_GT(snapid3, 0); ASSERT_GT(snapid3, snapid1); // Print snapshot delta (snap2 vs. snap3) results for root in a // human-readable form. test_mount.print_snap_diff("", "snap1", "snap3"); { // Make sure the root delta is as expected // One should use columns snap1 and snap3 from // the table preceeding prepareSnapDiffCases() function // to learn which names to expect in the delta. // // - file 'i' is unchanged hence not present in delta // vector> expected; expected.emplace_back("a", snapid3); // file 'a' is updated in snap3 expected.emplace_back("b", snapid3); // file 'b' is updated in snap3 expected.emplace_back("c", snapid1); // file 'c' is removed in snap2 expected.emplace_back("d", snapid3); // file 'd' is updated in snap3 expected.emplace_back("~e", snapid1); // file '~e' is removed in snap3 expected.emplace_back("e", snapid1); // file 'e' is removed in snap3 expected.emplace_back("f", snapid1); // file 'f' is removed in snap3 expected.emplace_back("ff", snapid1); // file 'ff' is removed in snap3 expected.emplace_back("g", snapid3); // file 'g' removed in snap2 and // re-appeared in snap3 expected.emplace_back("S", snapid3); // folder 'S' is present in snap3 hence reported expected.emplace_back("T", snapid3); // folder 'T' is present in snap3 hence reported expected.emplace_back("C", snapid1); // folder 'C' is removed in snap2 // folder 'G' is removed in snap2 and re-appeared in snap3 // hence reporting it twice under different snapid expected.emplace_back("G", snapid1); expected.emplace_back("G", snapid3); expected.emplace_back("l", snapid3); // file 'l' is created in snap2 expected.emplace_back("H", snapid3); // folder 'H' is created in snap3 hence reported expected.emplace_back("I", snapid3); // folder 'I' is created in snap3 hence reported expected.emplace_back("L", snapid3); // folder 'L' is created in snap3 hence reported test_mount.verify_snap_diff(expected, "", "snap3", "snap1"); } { // // Make sure snapshot delta for /S (children updated) is as expected // vector> expected; expected.emplace_back("sa", snapid3); test_mount.verify_snap_diff(expected, "S", "snap3", "snap1"); } { // // Make sure snapshot delta for /T (created and children updated) is as expected // vector> expected; expected.emplace_back("td", snapid3); test_mount.verify_snap_diff(expected, "T", "snap3", "snap1"); } { // // Make sure snapshot delta for /C (removed) is as expected // vector> expected; expected.emplace_back("cc", snapid1); expected.emplace_back("CC", snapid1); test_mount.verify_snap_diff(expected, "C", "snap3", "snap1"); } { // // Make sure snapshot delta for /C/CC (removed) is as expected // vector> expected; expected.emplace_back("c", snapid1); test_mount.verify_snap_diff(expected, "C/CC", "snap3", "snap1"); } { // // Make sure snapshot delta for /G (removed) is as expected // For this case (G@snap1 and G@snap3 are different entries) // the order in which snapshot names are provided is crucial. // Making G@snap1 vs. snap3 delta returns everything from G@snap1 // but omits any entries from G/snap3 (since it's a different entry). // And making G@snap3 vs. snap1 delta returns everything from G@snap3 // but nothing from snap1, vector> expected; expected.emplace_back("gg", snapid1); test_mount.verify_snap_diff(expected, "G", "snap1", "snap3"); } { // // Make sure snapshot delta for /G (re-created) is as expected // The snapshot names order is important, see above. // vector> expected; expected.emplace_back("gg", snapid3); test_mount.verify_snap_diff(expected, "G", "snap3", "snap1"); } { // // Make sure snapshot delta for /H (created) is as expected // vector> expected; expected.emplace_back("hh", snapid3); test_mount.verify_snap_diff(expected, "H", "snap1", "snap3"); } { // // Make sure snapshot delta for /I (chinldren updated) is as expected // vector> expected; expected.emplace_back("ii", snapid3); expected.emplace_back("iii", snapid3); expected.emplace_back("iiii", snapid3); expected.emplace_back("J", snapid3); test_mount.verify_snap_diff(expected, "I", "snap1", "snap3"); } { // // Make sure snapshot delta for /I/J (created at snap2) is as expected // vector> expected; expected.emplace_back("i", snapid3); expected.emplace_back("j", snapid3); expected.emplace_back("l", snapid3); test_mount.verify_snap_diff(expected, "I/J", "snap1", "snap3"); } { // // Make sure snapshot delta for /L is as expected // vector> expected; expected.emplace_back("ll", snapid3); expected.emplace_back("LL", snapid3); expected.emplace_back("LN", snapid3); test_mount.verify_snap_diff(expected, "L", "snap1", "snap3"); } { // // Make sure snapshot delta for /L/LL (children updated) is as expected // vector> expected; expected.emplace_back("ll", snapid3); test_mount.verify_snap_diff(expected, "L/LL", "snap1", "snap3"); } { vector> expected; test_mount.verify_snap_diff(expected, "L/LN", "snap1", "snap3"); } std::cout << "-------------" << std::endl; test_mount.remove_all(); test_mount.rmsnap("snap1"); test_mount.rmsnap("snap2"); test_mount.rmsnap("snap3"); } /* * SnapDiff readdir API testing for huge dir * when delta is minor. */ TEST(LibCephFS, HugeSnapDiffSmallDelta) { TestMount test_mount; long int file_count = 10000; printf("Seeding %ld files...\n", file_count); // Create simple directory tree with a couple of snapshots // to test against. string name_prefix_start = "aaaa"; string name_prefix_bulk = "file"; string name_prefix_end = "zzzz"; test_mount.prepareHugeSnapDiff(name_prefix_start, name_prefix_bulk, name_prefix_end, file_count, false); uint64_t snapid1; uint64_t snapid2; // learn snapshot ids and do basic verification ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); ASSERT_GT(snapid1, 0); ASSERT_GT(snapid2, 0); ASSERT_GT(snapid2, snapid1); std::cout << snapid1 << " vs. " << snapid2 << std::endl; // // Make sure snap1 vs. snap2 delta for the root is as expected // { vector> expected; expected.emplace_back(name_prefix_start + "B", snapid1); expected.emplace_back(name_prefix_start + "C", snapid2); expected.emplace_back(name_prefix_start + "D", snapid2); expected.emplace_back(name_prefix_end + "B", snapid1); expected.emplace_back(name_prefix_end + "C", snapid2); expected.emplace_back(name_prefix_end + "D", snapid2); test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); } std::cout << "------------- closing -------------" << std::endl; ASSERT_EQ(0, test_mount.purge_dir("")); ASSERT_EQ(0, test_mount.rmsnap("snap1")); ASSERT_EQ(0, test_mount.rmsnap("snap2")); } /* * SnapDiff readdir API testing for huge dir * when delta is large */ TEST(LibCephFS, HugeSnapDiffLargeDelta) { TestMount test_mount; // Calculate amount of files required to have multiple directory fragments // using relevant config parameters. // file_count = mds_bal_spli_size * mds_bal_fragment_fast_factor + 100 char buf[256]; int r = test_mount.conf_get("mds_bal_split_size", buf, sizeof(buf)); ASSERT_TRUE(r >= 0); long int file_count = strtol(buf, nullptr, 10); r = test_mount.conf_get("mds_bal_fragment_fast_factor ", buf, sizeof(buf)); ASSERT_TRUE(r >= 0); double factor = strtod(buf, nullptr); file_count *= factor; file_count += 100; printf("Seeding %ld files...\n", file_count); // Create simple directory tree with a couple of snapshots // to test against. string name_prefix_start = "aaaa"; string name_prefix_bulk = "file"; string name_prefix_end = "zzzz"; test_mount.prepareHugeSnapDiff(name_prefix_start, name_prefix_bulk, name_prefix_end, file_count, true); uint64_t snapid1; uint64_t snapid2; // learn snapshot ids and do basic verification ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); ASSERT_GT(snapid1, 0); ASSERT_GT(snapid2, 0); ASSERT_GT(snapid2, snapid1); std::cout << snapid1 << " vs. " << snapid2 << std::endl; // // Make sure snap1 vs. snap2 delta for the root is as expected // { vector> expected; expected.emplace_back(name_prefix_start + "B", snapid1); expected.emplace_back(name_prefix_start + "C", snapid2); expected.emplace_back(name_prefix_start + "D", snapid2); for (size_t i = 0; i < (size_t)file_count; i++) { expected.emplace_back(name_prefix_bulk + stringify(i), snapid2); } expected.emplace_back(name_prefix_end + "B", snapid1); expected.emplace_back(name_prefix_end + "C", snapid2); expected.emplace_back(name_prefix_end + "D", snapid2); test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); } std::cout << "------------- closing -------------" << std::endl; ASSERT_EQ(0, test_mount.purge_dir("")); ASSERT_EQ(0, test_mount.rmsnap("snap1")); ASSERT_EQ(0, test_mount.rmsnap("snap2")); }