Skip to content

Commit

Permalink
Read metric names from XML file (was filenames)
Browse files Browse the repository at this point in the history
CMK-7247
FEED-5784

Change-Id: I0f4929e6c74dd604a6bc5c3e16ab876cdba7123c
  • Loading branch information
Synss committed Apr 6, 2021
1 parent d5a5008 commit f01c463
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 48 deletions.
45 changes: 25 additions & 20 deletions livestatus/src/Metric.cc
Expand Up @@ -5,36 +5,41 @@

#include "Metric.h"

#include <fstream>
#include <regex>
#include <sstream>
#include <system_error>

#include "Logger.h"
#include "StringUtils.h"

namespace {
const std::regex label_regex{
R"(\s+<LABEL>(.+)</LABEL>)",
std::regex_constants::ECMAScript | std::regex_constants::icase};
} // namespace

Metric::Names scan_rrd(const std::filesystem::path& basedir,
const std::string& desc, Logger* logger) {
Informational(logger) << "scanning for metrics of " << desc << " in "
<< basedir;
std::string base = pnp_cleanup(desc + " ");
Metric::Names names;
try {
for (const auto& entry : std::filesystem::directory_iterator(basedir)) {
if (entry.path().extension() == ".rrd") {
auto stem = entry.path().filename().stem().string();
if (mk::starts_with(stem, base)) {
// NOTE: This is the main reason for mangling: The part of
// the file name after the stem is considered a mangled
// metric name.
names.emplace_back(stem.substr(base.size()));
}
}
}
} catch (const std::filesystem::filesystem_error& e) {
if (e.code() == std::errc::no_such_file_or_directory) {
Debug(logger) << "directory " << basedir << " does not exist yet";
} else {
Warning(logger) << "scanning directory for metrics: " << e.what();
std::string line;
auto path = basedir / (desc + ".xml");
auto infile = std::ifstream{path};
if (!infile.is_open()) {
const auto ge = generic_error{"cannot open " + path.string()};
Debug(logger) << ge;
return {};
}
while (std::getline(infile, line)) {
std::smatch label;
std::regex_search(line, label, label_regex);
if (!label.empty()) {
names.emplace_back(label[1]);
}
}
if (infile.bad()) {
const auto ge = generic_error{"cannot read " + path.string()};
Warning(logger) << ge;
}
return names;
}
66 changes: 38 additions & 28 deletions livestatus/src/test/test_Metric.cc
Expand Up @@ -22,49 +22,59 @@ bool operator<(const Metric::MangledName& x, const Metric::MangledName& y) {

class MetricFixture : public ::testing::Test {
public:
const std::string ext = ".xml";
const std::string desc = "Service_Description";
const std::string other_d = "Other_Service_Description";
Metric::Names metrics = {Metric::MangledName{"abc"},
Metric::MangledName{"def"},
Metric::MangledName{"ghi"}};
Metric::Names other_m = {Metric::MangledName{"jkl"},
Metric::MangledName{"mno"},
Metric::MangledName{"pqr"}};
const std::string ext = ".rrd";
const Metric::Names metrics = {Metric::MangledName{"abc"},
Metric::MangledName{"def"},
Metric::MangledName{"ghi"}};
const std::string desc_other = "Service_Description_Other";
const Metric::Names metrics_other = {Metric::MangledName{"jkl"},
Metric::MangledName{"mno"},
Metric::MangledName{"pqr"}};
const fs::path basepath{fs::temp_directory_path() / "metric_tests"};
fs::path filename(const std::string& d, const Metric::MangledName& metric) {
return basepath / (d + "_" + metric.string() + ext);

static void dump(fs::path&& path, const Metric::Names& metrics) {
auto out = std::ofstream{path};
out << "<?xml version=\"1.0\">\n"
"<NAGIOS>\n";
for (auto&& m : metrics) {
out << " <DATASOURCE>\n";
out << " <TEMPLATE>template</TEMPLATE>\n";
out << " <NAME>" + m.string() + "</NAME>\n";
out << " <LABEL>" + m.string() + "</LABEL>\n";
out << " <UNIT></UNIT>\n";
out << " </DATASOURCE>\n";
}
out << " <XML>\n"
" <VERSION>4</VERSION>"
"\n </XML>\n"
"</NAGIOS>\n";
}

void SetUp() override {
std::sort(metrics.begin(), metrics.end());
fs::create_directories(basepath);
// Create the metrics we use for the test.
std::for_each(std::begin(metrics), std::end(metrics),
[&](auto&& m) { std::ofstream{filename(desc, m)}; });
dump(basepath / (desc + ext), metrics);
// Add non-matching metrics to the directory.
std::for_each(
std::begin(other_m), std::end(other_m),
[&](const auto& m) { std::ofstream(filename(other_d, m)); });
dump(basepath / (desc_other + ext), metrics_other);
}
void TearDown() override { fs::remove_all(basepath); }
};

TEST_F(MetricFixture, DirectoryAndFileExist) {
ASSERT_TRUE(fs::exists(basepath));
EXPECT_FALSE(fs::is_empty(basepath));
for (auto&& m : metrics) {
EXPECT_TRUE(fs::exists(filename(desc, m)));
}
for (auto&& m : other_m) {
EXPECT_TRUE(fs::exists(filename(other_d, m)));
}
/// Return sorted string vectors to make the diff readable.
std::vector<std::string> human_readable(const Metric::Names& in) {
std::vector<std::string> out(in.size());
std::transform(std::begin(in), std::end(in), std::begin(out),
[](auto&& elem) { return elem.string(); });
std::sort(std::begin(out), std::end(out));
return out;
}

TEST_F(MetricFixture, ScanRRDFindsMetrics) {
ASSERT_TRUE(fs::exists(basepath));
ASSERT_FALSE(fs::is_empty(basepath));
Logger* const logger{Logger::getLogger("test")};
auto names = scan_rrd(basepath, desc, logger);
std::sort(names.begin(), names.end());
ASSERT_EQ(metrics, names);

const auto names = scan_rrd(basepath, desc, logger);
EXPECT_EQ(human_readable(metrics), human_readable(names));
}

0 comments on commit f01c463

Please sign in to comment.