From 08532ea166dfd97c548d9f1fb478e163021cdda3 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Fri, 5 Aug 2016 14:27:43 +0800 Subject: [PATCH] mon/PGMonitor: calc the %USED of pool using used/(used+avail) we were using "the raw space used by this pool" / "the raw space of the whole cluster" as the %USED. but it's wrong: - there is chance that not all OSDs are assigned to the pool in question - we can not utilize all the raw space for the pool: there is overhead. and the size of available space for a pool is capped by the assigned OSD with minimal free space. so we should use USED / (USED + AVAIL) as the %USED. so once we have no AVAIL space left, %USED will be 100%. Fixes: http://tracker.ceph.com/issues/16933 Signed-off-by: Kefu Chai --- src/common/TextTable.h | 4 +- src/mon/PGMonitor.cc | 9 ++- src/mon/PGMonitor.h | 5 ++ src/test/mon/CMakeLists.txt | 5 ++ src/test/mon/PGMonitor.cc | 152 ++++++++++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 src/test/mon/PGMonitor.cc diff --git a/src/common/TextTable.h b/src/common/TextTable.h index 20d3f0acf13..102e16fe482 100644 --- a/src/common/TextTable.h +++ b/src/common/TextTable.h @@ -49,10 +49,12 @@ private: }; std::vector col; // column definitions - std::vector > row; // row data array unsigned int curcol, currow; // col, row being inserted into unsigned int indent; // indent width when rendering +protected: + std::vector > row; // row data array + public: TextTable(): curcol(0), currow(0), indent(0) {} ~TextTable() {} diff --git a/src/mon/PGMonitor.cc b/src/mon/PGMonitor.cc index f9b0aeaa4c3..263faa9f161 100644 --- a/src/mon/PGMonitor.cc +++ b/src/mon/PGMonitor.cc @@ -1373,10 +1373,13 @@ void PGMonitor::dump_object_stat_sum(TextTable &tbl, Formatter *f, } } else { tbl << stringify(si_t(sum.num_bytes)); - int64_t kb_used = SHIFT_ROUND_UP(sum.num_bytes, 10); float used = 0.0; - if (pg_map.osd_sum.kb > 0) - used = (float)kb_used * raw_used_rate * curr_object_copies_rate / pg_map.osd_sum.kb; + if (avail) { + used = sum.num_bytes * curr_object_copies_rate; + used /= used + avail; + } else if (sum.num_bytes) { + used = 1.0; + } tbl << percentify(used*100); tbl << si_t(avail); tbl << sum.num_objects; diff --git a/src/mon/PGMonitor.h b/src/mon/PGMonitor.h index 098fcbcbeb1..be6eb0b4517 100644 --- a/src/mon/PGMonitor.h +++ b/src/mon/PGMonitor.h @@ -214,6 +214,11 @@ private: // no copying allowed PGMonitor(const PGMonitor &rhs); PGMonitor &operator=(const PGMonitor &rhs); + + // we don't want to include gtest.h just for FRIEND_TEST + friend class pgmonitor_dump_object_stat_sum_0_Test; + friend class pgmonitor_dump_object_stat_sum_1_Test; + friend class pgmonitor_dump_object_stat_sum_2_Test; }; #endif diff --git a/src/test/mon/CMakeLists.txt b/src/test/mon/CMakeLists.txt index 70270f86fe7..c89bcdc29c0 100644 --- a/src/test/mon/CMakeLists.txt +++ b/src/test/mon/CMakeLists.txt @@ -46,3 +46,8 @@ add_executable(unittest_mon_pgmap add_ceph_unittest(unittest_mon_pgmap ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_mon_pgmap) target_link_libraries(unittest_mon_pgmap mon global) +add_executable(unittest_mon_pgmonitor + PGMonitor.cc + ) +add_ceph_unittest(unittest_mon_pgmonitor ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_mon_pgmonitor) +target_link_libraries(unittest_mon_pgmonitor mon global) diff --git a/src/test/mon/PGMonitor.cc b/src/test/mon/PGMonitor.cc new file mode 100644 index 00000000000..84703228e01 --- /dev/null +++ b/src/test/mon/PGMonitor.cc @@ -0,0 +1,152 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "gtest/gtest.h" + +#include "common/ceph_argparse.h" +#include "common/TextTable.h" +#include "global/global_init.h" +#include "global/global_context.h" +#include "include/stringify.h" +#include "mon/PGMonitor.h" + +namespace { + class CheckTextTable : public TextTable { + public: + CheckTextTable(bool verbose) { + for (int i = 0; i < 4; i++) { + define_column("", TextTable::LEFT, TextTable::LEFT); + } + if (verbose) { + for (int i = 0; i < 4; i++) { + define_column("", TextTable::LEFT, TextTable::LEFT); + } + } + } + const string& get(unsigned r, unsigned c) const { + assert(r < row.size()); + assert(c < row[r].size()); + return row[r][c]; + } + }; + + // copied from PGMonitor.cc + string percentify(float a) { + stringstream ss; + if (a < 0.01) + ss << "0"; + else + ss << std::fixed << std::setprecision(2) << a; + return ss.str(); + } +} + +// dump_object_stat_sum() is called by "ceph df" command +// with table, without formatter, verbose = true, not empty, avail > 0 +TEST(pgmonitor, dump_object_stat_sum_0) +{ + bool verbose = true; + CheckTextTable tbl(verbose); + object_stat_sum_t sum; + sum.num_bytes = 42 * 1024 * 1024; + sum.num_objects = 42; + sum.num_objects_degraded = 13; // there are 13 missings + not_yet_backfilled + sum.num_objects_dirty = 2; + sum.num_rd = 100; + sum.num_rd_kb = 123; + sum.num_wr = 101; + sum.num_wr_kb = 321; + + sum.calc_copies(3); // assuming we have 3 copies for each obj + // nominal amount of space available for new objects in this pool + uint64_t avail = 2016 * 1024 * 1024; + pg_pool_t pool; + pool.quota_max_objects = 2000; + pool.quota_max_bytes = 2000 * 1024 * 1024; + pool.size = 2; + pool.type = pg_pool_t::TYPE_REPLICATED; + PGMonitor::dump_object_stat_sum(tbl, nullptr, sum, avail, + pool.get_size(), verbose, &pool); + ASSERT_EQ(stringify(si_t(sum.num_bytes)), tbl.get(0, 0)); + float copies_rate = + (static_cast(sum.num_object_copies - sum.num_objects_degraded) / + sum.num_object_copies); + float used_bytes = sum.num_bytes * copies_rate; + float used_percent = used_bytes / (used_bytes + avail) * 100; + unsigned col = 0; + ASSERT_EQ(stringify(si_t(sum.num_bytes)), tbl.get(0, col++)); + ASSERT_EQ(percentify(used_percent), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(avail)), tbl.get(0, col++)); + ASSERT_EQ(stringify(sum.num_objects), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(sum.num_objects_dirty)), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(sum.num_rd)), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(sum.num_wr)), tbl.get(0, col++)); + // we can use pool.size for raw_used_rate if it is a replica pool + uint64_t raw_bytes_used = sum.num_bytes * pool.get_size() * copies_rate; + ASSERT_EQ(stringify(si_t(raw_bytes_used)), tbl.get(0, col++)); +} + +// with table, without formatter, verbose = true, empty, avail > 0 +TEST(pgmonitor, dump_object_stat_sum_1) +{ + bool verbose = true; + CheckTextTable tbl(verbose); + object_stat_sum_t sum; // zero by default + ASSERT_TRUE(sum.is_zero()); + // nominal amount of space available for new objects in this pool + uint64_t avail = 2016 * 1024 * 1024; + pg_pool_t pool; + pool.quota_max_objects = 2000; + pool.quota_max_bytes = 2000 * 1024 * 1024; + pool.size = 2; + pool.type = pg_pool_t::TYPE_REPLICATED; + PGMonitor::dump_object_stat_sum(tbl, nullptr, sum, avail, + pool.get_size(), verbose, &pool); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, 0)); + unsigned col = 0; + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); + ASSERT_EQ(percentify(0), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(avail)), tbl.get(0, col++)); + ASSERT_EQ(stringify(0), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); +} + +// with table, without formatter, verbose = false, empty, avail = 0 +TEST(pgmonitor, dump_object_stat_sum_2) +{ + bool verbose = false; + CheckTextTable tbl(verbose); + object_stat_sum_t sum; // zero by default + ASSERT_TRUE(sum.is_zero()); + // nominal amount of space available for new objects in this pool + uint64_t avail = 0; + pg_pool_t pool; + pool.quota_max_objects = 2000; + pool.quota_max_bytes = 2000 * 1024 * 1024; + pool.size = 2; + pool.type = pg_pool_t::TYPE_REPLICATED; + + PGMonitor::dump_object_stat_sum(tbl, nullptr, sum, avail, + pool.get_size(), verbose, &pool); + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, 0)); + unsigned col = 0; + ASSERT_EQ(stringify(si_t(0)), tbl.get(0, col++)); + ASSERT_EQ(percentify(0), tbl.get(0, col++)); + ASSERT_EQ(stringify(si_t(avail)), tbl.get(0, col++)); + ASSERT_EQ(stringify(0), tbl.get(0, col++)); +} + +int main(int argc, char **argv) { + vector args; + argv_to_vec(argc, (const char **)argv, args); + env_to_vec(args); + + vector def_args; + global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); + common_init_finish(g_ceph_context); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} -- 2.39.5