]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
osd: Modify lazy omap stats to only show one copy 28070/head
authorBrad Hubbard <bhubbard@redhat.com>
Tue, 9 Jul 2019 05:03:24 +0000 (15:03 +1000)
committerBrad Hubbard <bhubbard@redhat.com>
Tue, 9 Jul 2019 23:34:18 +0000 (09:34 +1000)
Only show stats from the primary to get more in line with other pg stats
representations.

Signed-off-by: Brad Hubbard <bhubbard@redhat.com>
src/osd/PGBackend.cc
src/test/lazy-omap-stats/CMakeLists.txt
src/test/lazy-omap-stats/lazy-omap-stats-test.cc [deleted file]
src/test/lazy-omap-stats/lazy-omap-stats-test.h [deleted file]
src/test/lazy-omap-stats/lazy_omap_stats_test.cc [new file with mode: 0644]
src/test/lazy-omap-stats/lazy_omap_stats_test.h [new file with mode: 0644]
src/test/lazy-omap-stats/main.cc

index fa802d3a7c1f620ffcda6109767cb22e779287aa..afde1517b1c4b098d6b3f80c384a7f6af605f032 100644 (file)
@@ -1247,6 +1247,10 @@ void PGBackend::be_omap_checks(const map<pg_shard_t,ScrubMap*> &maps,
   // Iterate through objects and update omap stats
   for (const auto& k : master_set) {
     for (const auto& map : maps) {
+      if (map.first != get_parent()->primary_shard()) {
+        // Only set omap stats for the primary
+        continue;
+      }
       auto it = map.second->objects.find(k);
       if (it == map.second->objects.end())
         continue;
index 2d2fd0957e429f00abbd144659de3cab45b4666b..fad71f135ce0057405a9f6aea39b3c047ca0cbc9 100644 (file)
@@ -2,7 +2,7 @@
 
 add_executable(ceph_test_lazy_omap_stats
   main.cc
-  lazy-omap-stats-test.cc)
+  lazy_omap_stats_test.cc)
 target_link_libraries(ceph_test_lazy_omap_stats
   librados ${UNITTEST_LIBS} Boost::system)
 install(TARGETS
diff --git a/src/test/lazy-omap-stats/lazy-omap-stats-test.cc b/src/test/lazy-omap-stats/lazy-omap-stats-test.cc
deleted file mode 100644 (file)
index 6d78926..0000000
+++ /dev/null
@@ -1,567 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2019 Red Hat
- *
- * 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 <algorithm>
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/process.hpp>
-#include <boost/tokenizer.hpp>
-#include <boost/uuid/uuid.hpp>             // uuid class
-#include <boost/uuid/uuid_generators.hpp>  // generators
-#include <boost/uuid/uuid_io.hpp>          // streaming operators etc.
-#include <chrono>
-#include <iostream>
-#include <thread>
-#include <vector>
-
-#include "lazy-omap-stats-test.h"
-
-using namespace std;
-namespace bp = boost::process;
-
-void LazyOmapStatsTest::init(const int argc, const char** argv)
-{
-  int ret = rados.init("admin");
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to initialise rados! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-
-  ret = rados.conf_parse_argv(argc, argv);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to parse command line config options! Error: " << ret << " "
-         << strerror(ret) << endl;
-    exit(ret);
-  }
-
-  rados.conf_parse_env(NULL);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to parse environment! Error: " << ret << " "
-         << strerror(ret) << endl;
-    exit(ret);
-  }
-
-  rados.conf_read_file(NULL);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to read config file! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-
-  ret = rados.connect();
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to connect to running cluster! Error: " << ret << " "
-         << strerror(ret) << endl;
-    exit(ret);
-  }
-
-  string command = R"(
-    {
-      "prefix": "osd pool create",
-      "pool": ")" + conf.pool_name +
-                   R"(",
-      "pool_type": "replicated",
-      "size": )" + to_string(conf.replica_count) +
-                   R"(
-    })";
-  librados::bufferlist inbl;
-  string output;
-  ret = rados.mon_command(command, inbl, nullptr, &output);
-  if (output.length()) cout << output << endl;
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to create pool! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-
-  ret = rados.ioctx_create(conf.pool_name.c_str(), io_ctx);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to create ioctx! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-}
-
-void LazyOmapStatsTest::shutdown()
-{
-  rados.pool_delete(conf.pool_name.c_str());
-  rados.shutdown();
-}
-
-void LazyOmapStatsTest::write_omap(const string& object_name)
-{
-  librados::bufferlist bl;
-  int ret = io_ctx.write_full(object_name, bl);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to create object! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-  ret = io_ctx.omap_set(object_name, payload);
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to write omap payload! Error: " << ret << " "
-         << strerror(ret) << endl;
-    exit(ret);
-  }
-  cout << "Wrote " << conf.keys << " omap keys of " << conf.payload_size
-       << " bytes to "
-       << "the " << object_name << " object" << endl;
-}
-
-const string LazyOmapStatsTest::get_name() const
-{
-  boost::uuids::uuid uuid = boost::uuids::random_generator()();
-  return boost::uuids::to_string(uuid);
-}
-
-void LazyOmapStatsTest::write_many(uint how_many)
-{
-  for (uint i = 0; i < how_many; i++) {
-    write_omap(get_name());
-  }
-}
-
-void LazyOmapStatsTest::create_payload()
-{
-  librados::bufferlist Lorem;
-  Lorem.append(
-      "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
-      "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
-      "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
-      "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
-      "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
-      "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
-      "sunt in culpa qui officia deserunt mollit anim id est laborum.");
-  conf.payload_size = Lorem.length();
-  conf.total_bytes = conf.keys * conf.payload_size * conf.how_many;
-  conf.total_keys = conf.keys * conf.how_many;
-  uint i = 0;
-  for (i = 1; i < conf.keys + 1; ++i) {
-    payload[get_name()] = Lorem;
-  }
-  cout << "Created payload with " << conf.keys << " keys of "
-       << conf.payload_size
-       << " bytes each. Total size in bytes = " << conf.keys * conf.payload_size
-       << endl;
-}
-
-void LazyOmapStatsTest::scrub() const
-{
-  // Use CLI because we need to block
-
-  cout << "Scrubbing" << endl;
-  error_code ec;
-  bp::ipstream is;
-  bp::system("ceph osd deep-scrub all --block", bp::std_out > is, ec);
-  if (ec) {
-    cout << "Deep scrub command failed! Error: " << ec.value() << " "
-         << ec.message() << endl;
-    exit(ec.value());
-  }
-  cout << is.rdbuf() << endl;
-}
-
-const int LazyOmapStatsTest::find_matches(string& output, regex& reg) const
-{
-  sregex_iterator cur(output.begin(), output.end(), reg);
-  uint x = 0;
-  for (auto end = std::sregex_iterator(); cur != end; ++cur) {
-    cout << (*cur)[1].str() << endl;
-    x++;
-  }
-  return x;
-}
-
-const string LazyOmapStatsTest::get_output(const string command,
-                                           const bool silent)
-{
-  librados::bufferlist inbl, outbl;
-  string output;
-  int ret = rados.mgr_command(command, inbl, &outbl, &output);
-  if (output.length() && !silent) {
-    cout << output << endl;
-  }
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to get " << command << "! Error: " << ret << " "
-         << strerror(ret) << endl;
-    exit(ret);
-  }
-  return string(outbl.c_str(), outbl.length());
-}
-
-void LazyOmapStatsTest::check_one()
-{
-  string full_output = get_output();
-  cout << full_output << endl;
-  regex reg(
-      "\n"
-      R"((PG_STAT[\s\S]*)"
-      "\n)OSD_STAT"); // Strip OSD_STAT table so we don't find matches there
-  smatch match;
-  regex_search(full_output, match, reg);
-  auto truncated_output = match[1].str();
-  cout << truncated_output << endl;
-  reg = regex(
-      "\n"
-      R"(([0-9,s].*\s)" +
-      to_string(conf.keys) +
-      R"(\s.*))"
-      "\n");
-
-  cout << "Checking number of keys " << conf.keys << endl;
-  cout << "Found the following lines" << endl;
-  cout << "*************************" << endl;
-  uint result = find_matches(truncated_output, reg);
-  cout << "**********************" << endl;
-  cout << "Found " << result << " matching line(s)" << endl;
-  uint total = result;
-
-  reg = regex(
-      "\n"
-      R"(([0-9,s].*\s)" +
-      to_string(conf.payload_size * conf.keys) +
-      R"(\s.*))"
-      "\n");
-  cout << "Checking number of bytes "
-       << conf.payload_size * conf.keys << endl;
-  cout << "Found the following lines" << endl;
-  cout << "*************************" << endl;
-  result = find_matches(truncated_output, reg);
-  cout << "**********************" << endl;
-  cout << "Found " << result << " matching line(s)" << endl;
-
-  total += result;
-  if (total != 6) {
-    cout << "Error: Found " << total << " matches, expected 6! Exiting..."
-         << endl;
-    exit(22);  // EINVAL
-  }
-  cout << "check_one successful. Found " << total << " matches as expected"
-       << endl;
-}
-
-const int LazyOmapStatsTest::find_index(string& haystack, regex& needle,
-                                        string label) const
-{
-  smatch match;
-  regex_search(haystack, match, needle);
-  auto line = match[1].str();
-  boost::algorithm::trim(line);
-  boost::char_separator<char> sep{" "};
-  boost::tokenizer<boost::char_separator<char>> tok(line, sep);
-  vector<string> tokens(tok.begin(), tok.end());
-  auto it = find(tokens.begin(), tokens.end(), label);
-  if (it != tokens.end()) {
-    return distance(tokens.begin(), it);
-  }
-
-  cerr << "find_index failed to find index for " << label << endl;
-  exit(2);    // ENOENT
-  return -1;  // Unreachable
-}
-
-const uint LazyOmapStatsTest::tally_column(const uint omap_bytes_index,
-                                           const string& table,
-                                           bool header) const
-{
-  istringstream buffer(table);
-  string line;
-  uint64_t total = 0;
-  while (std::getline(buffer, line)) {
-    if (header) {
-      header = false;
-      continue;
-    }
-    boost::char_separator<char> sep{" "};
-    boost::tokenizer<boost::char_separator<char>> tok(line, sep);
-    vector<string> tokens(tok.begin(), tok.end());
-    total += stoi(tokens.at(omap_bytes_index));
-  }
-
-  return total;
-}
-
-void LazyOmapStatsTest::check_column(const int index, const string& table,
-                                     const string& type, bool header) const
-{
-  uint expected;
-  string errormsg;
-  if (type.compare("bytes") == 0) {
-    expected = conf.total_bytes;
-    errormsg = "Error. Got unexpected byte count!";
-  } else {
-    expected = conf.total_keys;
-    errormsg = "Error. Got unexpected key count!";
-  }
-  uint sum = tally_column(index, table, header);
-  cout << "Got: " << sum << " Expected: " << expected << endl;
-  if (sum != expected) {
-    cout << errormsg << endl;
-    exit(22);  // EINVAL
-  }
-}
-
-index_t LazyOmapStatsTest::get_indexes(regex& reg, string& output) const
-{
-  index_t indexes;
-  indexes.byte_index = find_index(output, reg, "OMAP_BYTES*");
-  indexes.key_index = find_index(output, reg, "OMAP_KEYS*");
-
-  return indexes;
-}
-
-const string LazyOmapStatsTest::get_pool_id(string& pool)
-{
-  cout << R"(Querying pool id)" << endl;
-
-  string command = R"({"prefix": "osd pool ls", "detail": "detail"})";
-  librados::bufferlist inbl, outbl;
-  string output;
-  int ret = rados.mon_command(command, inbl, &outbl, &output);
-  if (output.length()) cout << output << endl;
-  if (ret < 0) {
-    ret = -ret;
-    cerr << "Failed to get pool id! Error: " << ret << " " << strerror(ret)
-         << endl;
-    exit(ret);
-  }
-  string dump_output(outbl.c_str(), outbl.length());
-  cout << dump_output << endl;
-
-  string poolregstring = R"(pool\s(\d+)\s')" + pool + "'";
-  regex reg(poolregstring);
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto pool_id = match[1].str();
-  cout << "Found pool ID: " << pool_id << endl;
-
-  return pool_id;
-}
-
-void LazyOmapStatsTest::check_pg_dump()
-{
-  cout << R"(Checking "pg dump" output)" << endl;
-
-  string dump_output = get_output();
-  cout << dump_output << endl;
-
-  regex reg(
-      "\n"
-      R"((PG_STAT\s.*))"
-      "\n");
-  index_t indexes = get_indexes(reg, dump_output);
-
-  reg =
-      "\n"
-      R"((PG_STAT[\s\S]*))"
-      "\n +\n[0-9]";
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto table = match[1].str();
-
-  cout << "Checking bytes" << endl;
-  check_column(indexes.byte_index, table, string("bytes"));
-
-  cout << "Checking keys" << endl;
-  check_column(indexes.key_index, table, string("keys"));
-
-  cout << endl;
-}
-
-void LazyOmapStatsTest::check_pg_dump_summary()
-{
-  cout << R"(Checking "pg dump summary" output)" << endl;
-
-  string command = R"({"prefix": "pg dump", "dumpcontents": ["summary"]})";
-  string dump_output = get_output(command);
-  cout << dump_output << endl;
-
-  regex reg(
-      "\n"
-      R"((PG_STAT\s.*))"
-      "\n");
-  index_t indexes = get_indexes(reg, dump_output);
-
-  reg =
-      "\n"
-      R"((sum\s.*))"
-      "\n";
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto table = match[1].str();
-
-  cout << "Checking bytes" << endl;
-  check_column(indexes.byte_index, table, string("bytes"), false);
-
-  cout << "Checking keys" << endl;
-  check_column(indexes.key_index, table, string("keys"), false);
-  cout << endl;
-}
-
-void LazyOmapStatsTest::check_pg_dump_pgs()
-{
-  cout << R"(Checking "pg dump pgs" output)" << endl;
-
-  string command = R"({"prefix": "pg dump", "dumpcontents": ["pgs"]})";
-  string dump_output = get_output(command);
-  cout << dump_output << endl;
-
-  regex reg(R"(^(PG_STAT\s.*))"
-            "\n");
-  index_t indexes = get_indexes(reg, dump_output);
-
-  reg = R"(^(PG_STAT[\s\S]*))"
-        "\n\n";
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto table = match[1].str();
-
-  cout << "Checking bytes" << endl;
-  check_column(indexes.byte_index, table, string("bytes"));
-
-  cout << "Checking keys" << endl;
-  check_column(indexes.key_index, table, string("keys"));
-  cout << endl;
-}
-
-void LazyOmapStatsTest::check_pg_dump_pools()
-{
-  cout << R"(Checking "pg dump pools" output)" << endl;
-
-  string command = R"({"prefix": "pg dump", "dumpcontents": ["pools"]})";
-  string dump_output = get_output(command);
-  cout << dump_output << endl;
-
-  regex reg(R"(^(POOLID\s.*))"
-            "\n");
-  index_t indexes = get_indexes(reg, dump_output);
-
-  auto pool_id = get_pool_id(conf.pool_name);
-
-  reg =
-      "\n"
-      R"(()" +
-      pool_id +
-      R"(\s.*))"
-      "\n";
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto line = match[1].str();
-
-  cout << "Checking bytes" << endl;
-  check_column(indexes.byte_index, line, string("bytes"), false);
-
-  cout << "Checking keys" << endl;
-  check_column(indexes.key_index, line, string("keys"), false);
-  cout << endl;
-}
-
-void LazyOmapStatsTest::check_pg_ls()
-{
-  cout << R"(Checking "pg ls" output)" << endl;
-
-  string command = R"({"prefix": "pg ls"})";
-  string dump_output = get_output(command);
-  cout << dump_output << endl;
-
-  regex reg(R"(^(PG\s.*))"
-            "\n");
-  index_t indexes = get_indexes(reg, dump_output);
-
-  reg = R"(^(PG[\s\S]*))"
-        "\n\n";
-  smatch match;
-  regex_search(dump_output, match, reg);
-  auto table = match[1].str();
-
-  cout << "Checking bytes" << endl;
-  check_column(indexes.byte_index, table, string("bytes"));
-
-  cout << "Checking keys" << endl;
-  check_column(indexes.key_index, table, string("keys"));
-  cout << endl;
-}
-
-void LazyOmapStatsTest::wait_for_active_clean()
-{
-  cout << "Waiting for active+clean" << endl;
-
-  int index = -1;
-  regex reg(
-      "\n"
-      R"((PG_STAT[\s\S]*))"
-      "\n +\n[0-9]");
-  string command = R"({"prefix": "pg dump"})";
-  int num_not_clean;
-  do {
-    string dump_output = get_output(command, true);
-    if (index == -1) {
-      regex ireg(
-          "\n"
-          R"((PG_STAT\s.*))"
-          "\n");
-      index = find_index(dump_output, ireg, "STATE");
-    }
-    smatch match;
-    regex_search(dump_output, match, reg);
-    istringstream buffer(match[1].str());
-    string line;
-    num_not_clean = 0;
-    while (std::getline(buffer, line)) {
-      if (line.compare(0, 1, "P") == 0) continue;
-      boost::char_separator<char> sep{" "};
-      boost::tokenizer<boost::char_separator<char>> tok(line, sep);
-      vector<string> tokens(tok.begin(), tok.end());
-      num_not_clean += tokens.at(index).compare("active+clean");
-    }
-    cout << "." << flush;
-    this_thread::sleep_for(chrono::milliseconds(250));
-  } while (num_not_clean);
-
-  cout << endl;
-}
-
-const int LazyOmapStatsTest::run(const int argc, const char** argv)
-{
-  init(argc, argv);
-  create_payload();
-  wait_for_active_clean();
-  write_omap(get_name());
-  scrub();
-  check_one();
-
-  write_many(conf.how_many - 1);  // Since we already wrote one
-  scrub();
-  check_pg_dump();
-  check_pg_dump_summary();
-  check_pg_dump_pgs();
-  check_pg_dump_pools();
-  check_pg_ls();
-  cout << "All tests passed. Success!" << endl;
-
-  shutdown();
-
-  return 0;
-}
diff --git a/src/test/lazy-omap-stats/lazy-omap-stats-test.h b/src/test/lazy-omap-stats/lazy-omap-stats-test.h
deleted file mode 100644 (file)
index 5399012..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2019 Red Hat
- *
- * 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.
- *
- */
-
-#ifndef CEPH_LAZY_OMAP_STATS_TEST_H
-#define CEPH_LAZY_OMAP_STATS_TEST_H
-
-#include <map>
-#include <regex>
-#include <string>
-
-#include "include/rados/librados.hpp"
-
-struct index_t {
-  uint byte_index = 0;
-  uint key_index = 0;
-};
-
-class LazyOmapStatsTest
-{
-  librados::IoCtx io_ctx;
-  librados::Rados rados;
-  std::map<std::string, librados::bufferlist> payload;
-
-  struct lazy_omap_test_t {
-    uint payload_size = 0;
-    uint replica_count = 3;
-    uint keys = 2000;
-    uint how_many = 50;
-    std::string pool_name = "lazy_omap_test_pool";
-    uint total_bytes = 0;
-    uint total_keys = 0;
-  } conf;
-
-  LazyOmapStatsTest(LazyOmapStatsTest&) = delete;
-  void operator=(LazyOmapStatsTest) = delete;
-  void init(const int argc, const char** argv);
-  void shutdown();
-  void write_omap(const std::string& object_name);
-  const std::string get_name() const;
-  void create_payload();
-  void write_many(const uint how_many);
-  void scrub() const;
-  const int find_matches(std::string& output, std::regex& reg) const;
-  void check_one();
-  const int find_index(std::string& haystack, std::regex& needle,
-                       std::string label) const;
-  const uint tally_column(const uint omap_bytes_index,
-                          const std::string& table, bool header) const;
-  void check_column(const int index, const std::string& table,
-                    const std::string& type, bool header = true) const;
-  index_t get_indexes(std::regex& reg, std::string& output) const;
-  const std::string get_pool_id(std::string& pool);
-  void check_pg_dump();
-  void check_pg_dump_summary();
-  void check_pg_dump_pgs();
-  void check_pg_dump_pools();
-  void check_pg_ls();
-  const std::string get_output(
-      const std::string command = R"({"prefix": "pg dump"})",
-      const bool silent = false);
-  void wait_for_active_clean();
-
- public:
-  LazyOmapStatsTest() = default;
-  const int run(const int argc, const char** argv);
-};
-
-#endif // CEPH_LAZY_OMAP_STATS_TEST_H
diff --git a/src/test/lazy-omap-stats/lazy_omap_stats_test.cc b/src/test/lazy-omap-stats/lazy_omap_stats_test.cc
new file mode 100644 (file)
index 0000000..dd46142
--- /dev/null
@@ -0,0 +1,567 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * 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 <algorithm>
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/process.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/uuid/uuid.hpp>             // uuid class
+#include <boost/uuid/uuid_generators.hpp>  // generators
+#include <boost/uuid/uuid_io.hpp>          // streaming operators etc.
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include <vector>
+
+#include "lazy_omap_stats_test.h"
+
+using namespace std;
+namespace bp = boost::process;
+
+void LazyOmapStatsTest::init(const int argc, const char** argv)
+{
+  int ret = rados.init("admin");
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to initialise rados! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+
+  ret = rados.conf_parse_argv(argc, argv);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to parse command line config options! Error: " << ret << " "
+         << strerror(ret) << endl;
+    exit(ret);
+  }
+
+  rados.conf_parse_env(NULL);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to parse environment! Error: " << ret << " "
+         << strerror(ret) << endl;
+    exit(ret);
+  }
+
+  rados.conf_read_file(NULL);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to read config file! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+
+  ret = rados.connect();
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to connect to running cluster! Error: " << ret << " "
+         << strerror(ret) << endl;
+    exit(ret);
+  }
+
+  string command = R"(
+    {
+      "prefix": "osd pool create",
+      "pool": ")" + conf.pool_name +
+                   R"(",
+      "pool_type": "replicated",
+      "size": )" + to_string(conf.replica_count) +
+                   R"(
+    })";
+  librados::bufferlist inbl;
+  string output;
+  ret = rados.mon_command(command, inbl, nullptr, &output);
+  if (output.length()) cout << output << endl;
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to create pool! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+
+  ret = rados.ioctx_create(conf.pool_name.c_str(), io_ctx);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to create ioctx! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+}
+
+void LazyOmapStatsTest::shutdown()
+{
+  rados.pool_delete(conf.pool_name.c_str());
+  rados.shutdown();
+}
+
+void LazyOmapStatsTest::write_omap(const string& object_name)
+{
+  librados::bufferlist bl;
+  int ret = io_ctx.write_full(object_name, bl);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to create object! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+  ret = io_ctx.omap_set(object_name, payload);
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to write omap payload! Error: " << ret << " "
+         << strerror(ret) << endl;
+    exit(ret);
+  }
+  cout << "Wrote " << conf.keys << " omap keys of " << conf.payload_size
+       << " bytes to "
+       << "the " << object_name << " object" << endl;
+}
+
+const string LazyOmapStatsTest::get_name() const
+{
+  boost::uuids::uuid uuid = boost::uuids::random_generator()();
+  return boost::uuids::to_string(uuid);
+}
+
+void LazyOmapStatsTest::write_many(uint how_many)
+{
+  for (uint i = 0; i < how_many; i++) {
+    write_omap(get_name());
+  }
+}
+
+void LazyOmapStatsTest::create_payload()
+{
+  librados::bufferlist Lorem;
+  Lorem.append(
+      "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+      "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
+      "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
+      "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
+      "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
+      "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
+      "sunt in culpa qui officia deserunt mollit anim id est laborum.");
+  conf.payload_size = Lorem.length();
+  conf.total_bytes = conf.keys * conf.payload_size * conf.how_many;
+  conf.total_keys = conf.keys * conf.how_many;
+  uint i = 0;
+  for (i = 1; i < conf.keys + 1; ++i) {
+    payload[get_name()] = Lorem;
+  }
+  cout << "Created payload with " << conf.keys << " keys of "
+       << conf.payload_size
+       << " bytes each. Total size in bytes = " << conf.keys * conf.payload_size
+       << endl;
+}
+
+void LazyOmapStatsTest::scrub() const
+{
+  // Use CLI because we need to block
+
+  cout << "Scrubbing" << endl;
+  error_code ec;
+  bp::ipstream is;
+  bp::system("ceph osd deep-scrub all --block", bp::std_out > is, ec);
+  if (ec) {
+    cout << "Deep scrub command failed! Error: " << ec.value() << " "
+         << ec.message() << endl;
+    exit(ec.value());
+  }
+  cout << is.rdbuf() << endl;
+}
+
+const int LazyOmapStatsTest::find_matches(string& output, regex& reg) const
+{
+  sregex_iterator cur(output.begin(), output.end(), reg);
+  uint x = 0;
+  for (auto end = std::sregex_iterator(); cur != end; ++cur) {
+    cout << (*cur)[1].str() << endl;
+    x++;
+  }
+  return x;
+}
+
+const string LazyOmapStatsTest::get_output(const string command,
+                                           const bool silent)
+{
+  librados::bufferlist inbl, outbl;
+  string output;
+  int ret = rados.mgr_command(command, inbl, &outbl, &output);
+  if (output.length() && !silent) {
+    cout << output << endl;
+  }
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to get " << command << "! Error: " << ret << " "
+         << strerror(ret) << endl;
+    exit(ret);
+  }
+  return string(outbl.c_str(), outbl.length());
+}
+
+void LazyOmapStatsTest::check_one()
+{
+  string full_output = get_output();
+  cout << full_output << endl;
+  regex reg(
+      "\n"
+      R"((PG_STAT[\s\S]*)"
+      "\n)OSD_STAT"); // Strip OSD_STAT table so we don't find matches there
+  smatch match;
+  regex_search(full_output, match, reg);
+  auto truncated_output = match[1].str();
+  cout << truncated_output << endl;
+  reg = regex(
+      "\n"
+      R"(([0-9,s].*\s)" +
+      to_string(conf.keys) +
+      R"(\s.*))"
+      "\n");
+
+  cout << "Checking number of keys " << conf.keys << endl;
+  cout << "Found the following lines" << endl;
+  cout << "*************************" << endl;
+  uint result = find_matches(truncated_output, reg);
+  cout << "**********************" << endl;
+  cout << "Found " << result << " matching line(s)" << endl;
+  uint total = result;
+
+  reg = regex(
+      "\n"
+      R"(([0-9,s].*\s)" +
+      to_string(conf.payload_size * conf.keys) +
+      R"(\s.*))"
+      "\n");
+  cout << "Checking number of bytes "
+       << conf.payload_size * conf.keys << endl;
+  cout << "Found the following lines" << endl;
+  cout << "*************************" << endl;
+  result = find_matches(truncated_output, reg);
+  cout << "**********************" << endl;
+  cout << "Found " << result << " matching line(s)" << endl;
+
+  total += result;
+  if (total != 6) {
+    cout << "Error: Found " << total << " matches, expected 6! Exiting..."
+         << endl;
+    exit(22);  // EINVAL
+  }
+  cout << "check_one successful. Found " << total << " matches as expected"
+       << endl;
+}
+
+const int LazyOmapStatsTest::find_index(string& haystack, regex& needle,
+                                        string label) const
+{
+  smatch match;
+  regex_search(haystack, match, needle);
+  auto line = match[1].str();
+  boost::algorithm::trim(line);
+  boost::char_separator<char> sep{" "};
+  boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+  vector<string> tokens(tok.begin(), tok.end());
+  auto it = find(tokens.begin(), tokens.end(), label);
+  if (it != tokens.end()) {
+    return distance(tokens.begin(), it);
+  }
+
+  cerr << "find_index failed to find index for " << label << endl;
+  exit(2);    // ENOENT
+  return -1;  // Unreachable
+}
+
+const uint LazyOmapStatsTest::tally_column(const uint omap_bytes_index,
+                                           const string& table,
+                                           bool header) const
+{
+  istringstream buffer(table);
+  string line;
+  uint64_t total = 0;
+  while (std::getline(buffer, line)) {
+    if (header) {
+      header = false;
+      continue;
+    }
+    boost::char_separator<char> sep{" "};
+    boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+    vector<string> tokens(tok.begin(), tok.end());
+    total += stoi(tokens.at(omap_bytes_index));
+  }
+
+  return total;
+}
+
+void LazyOmapStatsTest::check_column(const int index, const string& table,
+                                     const string& type, bool header) const
+{
+  uint expected;
+  string errormsg;
+  if (type.compare("bytes") == 0) {
+    expected = conf.total_bytes;
+    errormsg = "Error. Got unexpected byte count!";
+  } else {
+    expected = conf.total_keys;
+    errormsg = "Error. Got unexpected key count!";
+  }
+  uint sum = tally_column(index, table, header);
+  cout << "Got: " << sum << " Expected: " << expected << endl;
+  if (sum != expected) {
+    cout << errormsg << endl;
+    exit(22);  // EINVAL
+  }
+}
+
+index_t LazyOmapStatsTest::get_indexes(regex& reg, string& output) const
+{
+  index_t indexes;
+  indexes.byte_index = find_index(output, reg, "OMAP_BYTES*");
+  indexes.key_index = find_index(output, reg, "OMAP_KEYS*");
+
+  return indexes;
+}
+
+const string LazyOmapStatsTest::get_pool_id(string& pool)
+{
+  cout << R"(Querying pool id)" << endl;
+
+  string command = R"({"prefix": "osd pool ls", "detail": "detail"})";
+  librados::bufferlist inbl, outbl;
+  string output;
+  int ret = rados.mon_command(command, inbl, &outbl, &output);
+  if (output.length()) cout << output << endl;
+  if (ret < 0) {
+    ret = -ret;
+    cerr << "Failed to get pool id! Error: " << ret << " " << strerror(ret)
+         << endl;
+    exit(ret);
+  }
+  string dump_output(outbl.c_str(), outbl.length());
+  cout << dump_output << endl;
+
+  string poolregstring = R"(pool\s(\d+)\s')" + pool + "'";
+  regex reg(poolregstring);
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto pool_id = match[1].str();
+  cout << "Found pool ID: " << pool_id << endl;
+
+  return pool_id;
+}
+
+void LazyOmapStatsTest::check_pg_dump()
+{
+  cout << R"(Checking "pg dump" output)" << endl;
+
+  string dump_output = get_output();
+  cout << dump_output << endl;
+
+  regex reg(
+      "\n"
+      R"((PG_STAT\s.*))"
+      "\n");
+  index_t indexes = get_indexes(reg, dump_output);
+
+  reg =
+      "\n"
+      R"((PG_STAT[\s\S]*))"
+      "\n +\n[0-9]";
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto table = match[1].str();
+
+  cout << "Checking bytes" << endl;
+  check_column(indexes.byte_index, table, string("bytes"));
+
+  cout << "Checking keys" << endl;
+  check_column(indexes.key_index, table, string("keys"));
+
+  cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_summary()
+{
+  cout << R"(Checking "pg dump summary" output)" << endl;
+
+  string command = R"({"prefix": "pg dump", "dumpcontents": ["summary"]})";
+  string dump_output = get_output(command);
+  cout << dump_output << endl;
+
+  regex reg(
+      "\n"
+      R"((PG_STAT\s.*))"
+      "\n");
+  index_t indexes = get_indexes(reg, dump_output);
+
+  reg =
+      "\n"
+      R"((sum\s.*))"
+      "\n";
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto table = match[1].str();
+
+  cout << "Checking bytes" << endl;
+  check_column(indexes.byte_index, table, string("bytes"), false);
+
+  cout << "Checking keys" << endl;
+  check_column(indexes.key_index, table, string("keys"), false);
+  cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_pgs()
+{
+  cout << R"(Checking "pg dump pgs" output)" << endl;
+
+  string command = R"({"prefix": "pg dump", "dumpcontents": ["pgs"]})";
+  string dump_output = get_output(command);
+  cout << dump_output << endl;
+
+  regex reg(R"(^(PG_STAT\s.*))"
+            "\n");
+  index_t indexes = get_indexes(reg, dump_output);
+
+  reg = R"(^(PG_STAT[\s\S]*))"
+        "\n\n";
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto table = match[1].str();
+
+  cout << "Checking bytes" << endl;
+  check_column(indexes.byte_index, table, string("bytes"));
+
+  cout << "Checking keys" << endl;
+  check_column(indexes.key_index, table, string("keys"));
+  cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_dump_pools()
+{
+  cout << R"(Checking "pg dump pools" output)" << endl;
+
+  string command = R"({"prefix": "pg dump", "dumpcontents": ["pools"]})";
+  string dump_output = get_output(command);
+  cout << dump_output << endl;
+
+  regex reg(R"(^(POOLID\s.*))"
+            "\n");
+  index_t indexes = get_indexes(reg, dump_output);
+
+  auto pool_id = get_pool_id(conf.pool_name);
+
+  reg =
+      "\n"
+      R"(()" +
+      pool_id +
+      R"(\s.*))"
+      "\n";
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto line = match[1].str();
+
+  cout << "Checking bytes" << endl;
+  check_column(indexes.byte_index, line, string("bytes"), false);
+
+  cout << "Checking keys" << endl;
+  check_column(indexes.key_index, line, string("keys"), false);
+  cout << endl;
+}
+
+void LazyOmapStatsTest::check_pg_ls()
+{
+  cout << R"(Checking "pg ls" output)" << endl;
+
+  string command = R"({"prefix": "pg ls"})";
+  string dump_output = get_output(command);
+  cout << dump_output << endl;
+
+  regex reg(R"(^(PG\s.*))"
+            "\n");
+  index_t indexes = get_indexes(reg, dump_output);
+
+  reg = R"(^(PG[\s\S]*))"
+        "\n\n";
+  smatch match;
+  regex_search(dump_output, match, reg);
+  auto table = match[1].str();
+
+  cout << "Checking bytes" << endl;
+  check_column(indexes.byte_index, table, string("bytes"));
+
+  cout << "Checking keys" << endl;
+  check_column(indexes.key_index, table, string("keys"));
+  cout << endl;
+}
+
+void LazyOmapStatsTest::wait_for_active_clean()
+{
+  cout << "Waiting for active+clean" << endl;
+
+  int index = -1;
+  regex reg(
+      "\n"
+      R"((PG_STAT[\s\S]*))"
+      "\n +\n[0-9]");
+  string command = R"({"prefix": "pg dump"})";
+  int num_not_clean;
+  do {
+    string dump_output = get_output(command, true);
+    if (index == -1) {
+      regex ireg(
+          "\n"
+          R"((PG_STAT\s.*))"
+          "\n");
+      index = find_index(dump_output, ireg, "STATE");
+    }
+    smatch match;
+    regex_search(dump_output, match, reg);
+    istringstream buffer(match[1].str());
+    string line;
+    num_not_clean = 0;
+    while (std::getline(buffer, line)) {
+      if (line.compare(0, 1, "P") == 0) continue;
+      boost::char_separator<char> sep{" "};
+      boost::tokenizer<boost::char_separator<char>> tok(line, sep);
+      vector<string> tokens(tok.begin(), tok.end());
+      num_not_clean += tokens.at(index).compare("active+clean");
+    }
+    cout << "." << flush;
+    this_thread::sleep_for(chrono::milliseconds(250));
+  } while (num_not_clean);
+
+  cout << endl;
+}
+
+const int LazyOmapStatsTest::run(const int argc, const char** argv)
+{
+  init(argc, argv);
+  create_payload();
+  wait_for_active_clean();
+  write_omap(get_name());
+  scrub();
+  check_one();
+
+  write_many(conf.how_many - 1);  // Since we already wrote one
+  scrub();
+  check_pg_dump();
+  check_pg_dump_summary();
+  check_pg_dump_pgs();
+  check_pg_dump_pools();
+  check_pg_ls();
+  cout << "All tests passed. Success!" << endl;
+
+  shutdown();
+
+  return 0;
+}
diff --git a/src/test/lazy-omap-stats/lazy_omap_stats_test.h b/src/test/lazy-omap-stats/lazy_omap_stats_test.h
new file mode 100644 (file)
index 0000000..5399012
--- /dev/null
@@ -0,0 +1,79 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2019 Red Hat
+ *
+ * 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.
+ *
+ */
+
+#ifndef CEPH_LAZY_OMAP_STATS_TEST_H
+#define CEPH_LAZY_OMAP_STATS_TEST_H
+
+#include <map>
+#include <regex>
+#include <string>
+
+#include "include/rados/librados.hpp"
+
+struct index_t {
+  uint byte_index = 0;
+  uint key_index = 0;
+};
+
+class LazyOmapStatsTest
+{
+  librados::IoCtx io_ctx;
+  librados::Rados rados;
+  std::map<std::string, librados::bufferlist> payload;
+
+  struct lazy_omap_test_t {
+    uint payload_size = 0;
+    uint replica_count = 3;
+    uint keys = 2000;
+    uint how_many = 50;
+    std::string pool_name = "lazy_omap_test_pool";
+    uint total_bytes = 0;
+    uint total_keys = 0;
+  } conf;
+
+  LazyOmapStatsTest(LazyOmapStatsTest&) = delete;
+  void operator=(LazyOmapStatsTest) = delete;
+  void init(const int argc, const char** argv);
+  void shutdown();
+  void write_omap(const std::string& object_name);
+  const std::string get_name() const;
+  void create_payload();
+  void write_many(const uint how_many);
+  void scrub() const;
+  const int find_matches(std::string& output, std::regex& reg) const;
+  void check_one();
+  const int find_index(std::string& haystack, std::regex& needle,
+                       std::string label) const;
+  const uint tally_column(const uint omap_bytes_index,
+                          const std::string& table, bool header) const;
+  void check_column(const int index, const std::string& table,
+                    const std::string& type, bool header = true) const;
+  index_t get_indexes(std::regex& reg, std::string& output) const;
+  const std::string get_pool_id(std::string& pool);
+  void check_pg_dump();
+  void check_pg_dump_summary();
+  void check_pg_dump_pgs();
+  void check_pg_dump_pools();
+  void check_pg_ls();
+  const std::string get_output(
+      const std::string command = R"({"prefix": "pg dump"})",
+      const bool silent = false);
+  void wait_for_active_clean();
+
+ public:
+  LazyOmapStatsTest() = default;
+  const int run(const int argc, const char** argv);
+};
+
+#endif // CEPH_LAZY_OMAP_STATS_TEST_H
index 2c71394e5af70e3cedcf8fca6ad5482c71a8556f..d379e8fbd2679e5ee409830c26442a2a8167d62d 100644 (file)
@@ -12,7 +12,7 @@
  *
  */
 
-#include "lazy-omap-stats-test.h"
+#include "lazy_omap_stats_test.h"
 
 int main(const int argc, const char** argv)
 {