]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mon/PGMonitor: calc the %USED of pool using used/(used+avail)
authorKefu Chai <kchai@redhat.com>
Fri, 5 Aug 2016 06:27:43 +0000 (14:27 +0800)
committerKefu Chai <kchai@redhat.com>
Sat, 6 Aug 2016 17:37:40 +0000 (01:37 +0800)
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 <kchai@redhat.com>
src/common/TextTable.h
src/mon/PGMonitor.cc
src/mon/PGMonitor.h
src/test/mon/CMakeLists.txt
src/test/mon/PGMonitor.cc [new file with mode: 0644]

index 20d3f0acf139a8730d9f690fdc10e03be3ed26ae..102e16fe4824647d607e5ea3c90f0a06e18c85c4 100644 (file)
@@ -49,10 +49,12 @@ private:
   };
 
   std::vector<TextTableColumn> col;    // column definitions
-  std::vector<std::vector<std::string> > row;  // row data array
   unsigned int curcol, currow;         // col, row being inserted into
   unsigned int indent;                 // indent width when rendering
 
+protected:
+  std::vector<std::vector<std::string> > row;  // row data array
+
 public:
   TextTable(): curcol(0), currow(0), indent(0) {}
   ~TextTable() {}
index f9b0aeaa4c3458c9b527d55d5b621e71029f0519..263faa9f161a3a5229659cdd098e4d43cbb6e134 100644 (file)
@@ -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;
index 098fcbcbeb1ffa5cfa8a88bf1cf22a93e8c02456..be6eb0b4517ad32716c0db19e2080e082e073455 100644 (file)
@@ -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
index 70270f86fe7cc119d072d734f664a03fa396cf44..c89bcdc29c0eeac7c7e2e8dbab480032caac748c 100644 (file)
@@ -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 (file)
index 0000000..8470322
--- /dev/null
@@ -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<float>(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<const char*> args;
+  argv_to_vec(argc, (const char **)argv, args);
+  env_to_vec(args);
+
+  vector<const char*> 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();
+}