summaryrefslogtreecommitdiffstats
path: root/lib/base/perfdatavalue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/base/perfdatavalue.cpp')
-rw-r--r--lib/base/perfdatavalue.cpp395
1 files changed, 395 insertions, 0 deletions
diff --git a/lib/base/perfdatavalue.cpp b/lib/base/perfdatavalue.cpp
new file mode 100644
index 0000000..60a39e4
--- /dev/null
+++ b/lib/base/perfdatavalue.cpp
@@ -0,0 +1,395 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "base/perfdatavalue.hpp"
+#include "base/perfdatavalue-ti.cpp"
+#include "base/convert.hpp"
+#include "base/exception.hpp"
+#include "base/logger.hpp"
+#include "base/function.hpp"
+#include <boost/algorithm/string.hpp>
+#include <cmath>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+using namespace icinga;
+
+REGISTER_TYPE(PerfdataValue);
+REGISTER_FUNCTION(System, parse_performance_data, PerfdataValue::Parse, "perfdata");
+
+struct UoM
+{
+ double Factor;
+ const char* Out;
+};
+
+typedef std::unordered_map<std::string /* in */, UoM> UoMs;
+typedef std::unordered_multimap<std::string /* in */, UoM> DupUoMs;
+
+static const UoMs l_CsUoMs (([]() -> UoMs {
+ DupUoMs uoms ({
+ // Misc:
+ { "", { 1, "" } },
+ { "%", { 1, "percent" } },
+ { "c", { 1, "" } },
+ { "C", { 1, "degrees-celsius" } }
+ });
+
+ {
+ // Data (rate):
+
+ struct { const char* Char; int Power; } prefixes[] = {
+ { "k", 1 }, { "K", 1 },
+ { "m", 2 }, { "M", 2 },
+ { "g", 3 }, { "G", 3 },
+ { "t", 4 }, { "T", 4 },
+ { "p", 5 }, { "P", 5 },
+ { "e", 6 }, { "E", 6 },
+ { "z", 7 }, { "Z", 7 },
+ { "y", 8 }, { "Y", 8 }
+ };
+
+ struct { const char* Char; double Factor; } siIecs[] = {
+ { "", 1000 },
+ { "i", 1024 }, { "I", 1024 }
+ };
+
+ struct { const char *In, *Out; } bases[] = {
+ { "b", "bits" },
+ { "B", "bytes" }
+ };
+
+ for (auto base : bases) {
+ uoms.emplace(base.In, UoM{1, base.Out});
+ }
+
+ for (auto prefix : prefixes) {
+ for (auto siIec : siIecs) {
+ auto factor (pow(siIec.Factor, prefix.Power));
+
+ for (auto base : bases) {
+ uoms.emplace(
+ std::string(prefix.Char) + siIec.Char + base.In,
+ UoM{factor, base.Out}
+ );
+ }
+ }
+ }
+ }
+
+ {
+ // Energy:
+
+ struct { const char* Char; int Power; } prefixes[] = {
+ { "n", -3 }, { "N", -3 },
+ { "u", -2 }, { "U", -2 },
+ { "m", -1 },
+ { "", 0 },
+ { "k", 1 }, { "K", 1 },
+ { "M", 2 },
+ { "g", 3 }, { "G", 3 },
+ { "t", 4 }, { "T", 4 },
+ { "p", 5 }, { "P", 5 },
+ { "e", 6 }, { "E", 6 },
+ { "z", 7 }, { "Z", 7 },
+ { "y", 8 }, { "Y", 8 }
+ };
+
+ {
+ struct { const char* Ins[2]; const char* Out; } bases[] = {
+ { { "a", "A" }, "amperes" },
+ { { "o", "O" }, "ohms" },
+ { { "v", "V" }, "volts" },
+ { { "w", "W" }, "watts" }
+ };
+
+ for (auto prefix : prefixes) {
+ auto factor (pow(1000.0, prefix.Power));
+
+ for (auto base : bases) {
+ for (auto b : base.Ins) {
+ uoms.emplace(std::string(prefix.Char) + b, UoM{factor, base.Out});
+ }
+ }
+ }
+ }
+
+ struct { const char* Char; double Factor; } suffixes[] = {
+ { "s", 1 }, { "S", 1 },
+ { "m", 60 }, { "M", 60 },
+ { "h", 60 * 60 }, { "H", 60 * 60 }
+ };
+
+ struct { const char* Ins[2]; double Factor; const char* Out; } bases[] = {
+ { { "a", "A" }, 1, "ampere-seconds" },
+ { { "w", "W" }, 60 * 60, "watt-hours" }
+ };
+
+ for (auto prefix : prefixes) {
+ auto factor (pow(1000.0, prefix.Power));
+
+ for (auto suffix : suffixes) {
+ auto timeFactor (factor * suffix.Factor);
+
+ for (auto& base : bases) {
+ auto baseFactor (timeFactor / base.Factor);
+
+ for (auto b : base.Ins) {
+ uoms.emplace(
+ std::string(prefix.Char) + b + suffix.Char,
+ UoM{baseFactor, base.Out}
+ );
+ }
+ }
+ }
+ }
+ }
+
+ UoMs uniqUoms;
+
+ for (auto& uom : uoms) {
+ if (!uniqUoms.emplace(uom).second) {
+ throw std::logic_error("Duplicate case-sensitive UoM detected: " + uom.first);
+ }
+ }
+
+ return uniqUoms;
+})());
+
+static const UoMs l_CiUoMs (([]() -> UoMs {
+ DupUoMs uoms ({
+ // Time:
+ { "ns", { 1.0 / 1000 / 1000 / 1000, "seconds" } },
+ { "us", { 1.0 / 1000 / 1000, "seconds" } },
+ { "ms", { 1.0 / 1000, "seconds" } },
+ { "s", { 1, "seconds" } },
+ { "m", { 60, "seconds" } },
+ { "h", { 60 * 60, "seconds" } },
+ { "d", { 60 * 60 * 24, "seconds" } },
+
+ // Mass:
+ { "ng", { 1.0 / 1000 / 1000 / 1000, "grams" } },
+ { "ug", { 1.0 / 1000 / 1000, "grams" } },
+ { "mg", { 1.0 / 1000, "grams" } },
+ { "g", { 1, "grams" } },
+ { "kg", { 1000, "grams" } },
+ { "t", { 1000 * 1000, "grams" } },
+
+ // Volume:
+ { "ml", { 1.0 / 1000, "liters" } },
+ { "l", { 1, "liters" } },
+ { "hl", { 100, "liters" } },
+
+ // Misc:
+ { "packets", { 1, "packets" } },
+ { "lm", { 1, "lumens" } },
+ { "dbm", { 1, "decibel-milliwatts" } },
+ { "f", { 1, "degrees-fahrenheit" } },
+ { "k", { 1, "degrees-kelvin" } }
+ });
+
+ UoMs uniqUoms;
+
+ for (auto& uom : uoms) {
+ if (!uniqUoms.emplace(uom).second) {
+ throw std::logic_error("Duplicate case-insensitive UoM detected: " + uom.first);
+ }
+ }
+
+ for (auto& uom : l_CsUoMs) {
+ auto input (uom.first);
+ boost::algorithm::to_lower(input);
+
+ auto pos (uoms.find(input));
+
+ if (pos != uoms.end()) {
+ throw std::logic_error("Duplicate case-sensitive/case-insensitive UoM detected: " + pos->first);
+ }
+ }
+
+ return uniqUoms;
+})());
+
+PerfdataValue::PerfdataValue(const String& label, double value, bool counter,
+ const String& unit, const Value& warn, const Value& crit, const Value& min,
+ const Value& max)
+{
+ SetLabel(label, true);
+ SetValue(value, true);
+ SetCounter(counter, true);
+ SetUnit(unit, true);
+ SetWarn(warn, true);
+ SetCrit(crit, true);
+ SetMin(min, true);
+ SetMax(max, true);
+}
+
+PerfdataValue::Ptr PerfdataValue::Parse(const String& perfdata)
+{
+ size_t eqp = perfdata.FindLastOf('=');
+
+ if (eqp == String::NPos)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid performance data value: " + perfdata));
+
+ String label = perfdata.SubStr(0, eqp);
+
+ if (label.GetLength() > 2 && label[0] == '\'' && label[label.GetLength() - 1] == '\'')
+ label = label.SubStr(1, label.GetLength() - 2);
+
+ size_t spq = perfdata.FindFirstOf(' ', eqp);
+
+ if (spq == String::NPos)
+ spq = perfdata.GetLength();
+
+ String valueStr = perfdata.SubStr(eqp + 1, spq - eqp - 1);
+ std::vector<String> tokens = valueStr.Split(";");
+
+ if (valueStr.FindFirstOf(',') != String::NPos || tokens.empty()) {
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid performance data value: " + perfdata));
+ }
+
+ // Find the position where to split value and unit. Possible values of tokens[0] include:
+ // "1000", "1.0", "1.", "-.1", "+1", "1e10", "1GB", "1e10GB", "1e10EB", "1E10EB", "1.5GB", "1.GB", "+1.E-1EW"
+ // Consider everything up to and including the last digit or decimal point as part of the value.
+ size_t pos = tokens[0].FindLastOf("0123456789.");
+ if (pos != String::NPos) {
+ pos++;
+ }
+
+ double value = Convert::ToDouble(tokens[0].SubStr(0, pos));
+
+ bool counter = false;
+ String unit;
+ Value warn, crit, min, max;
+
+ if (pos != String::NPos)
+ unit = tokens[0].SubStr(pos, String::NPos);
+
+ double base;
+
+ {
+ auto uom (l_CsUoMs.find(unit.GetData()));
+
+ if (uom == l_CsUoMs.end()) {
+ auto ciUnit (unit.ToLower());
+ auto uom (l_CiUoMs.find(ciUnit.GetData()));
+
+ if (uom == l_CiUoMs.end()) {
+ Log(LogDebug, "PerfdataValue")
+ << "Invalid performance data unit: " << unit;
+
+ unit = "";
+ base = 1.0;
+ } else {
+ unit = uom->second.Out;
+ base = uom->second.Factor;
+ }
+ } else {
+ unit = uom->second.Out;
+ base = uom->second.Factor;
+ }
+ }
+
+ if (unit == "c") {
+ counter = true;
+ }
+
+ warn = ParseWarnCritMinMaxToken(tokens, 1, "warning");
+ crit = ParseWarnCritMinMaxToken(tokens, 2, "critical");
+ min = ParseWarnCritMinMaxToken(tokens, 3, "minimum");
+ max = ParseWarnCritMinMaxToken(tokens, 4, "maximum");
+
+ value = value * base;
+
+ if (!warn.IsEmpty())
+ warn = warn * base;
+
+ if (!crit.IsEmpty())
+ crit = crit * base;
+
+ if (!min.IsEmpty())
+ min = min * base;
+
+ if (!max.IsEmpty())
+ max = max * base;
+
+ return new PerfdataValue(label, value, counter, unit, warn, crit, min, max);
+}
+
+static const std::unordered_map<std::string, const char*> l_FormatUoMs ({
+ { "ampere-seconds", "As" },
+ { "amperes", "A" },
+ { "bits", "b" },
+ { "bytes", "B" },
+ { "decibel-milliwatts", "dBm" },
+ { "degrees-celsius", "C" },
+ { "degrees-fahrenheit", "F" },
+ { "degrees-kelvin", "K" },
+ { "grams", "g" },
+ { "liters", "l" },
+ { "lumens", "lm" },
+ { "ohms", "O" },
+ { "percent", "%" },
+ { "seconds", "s" },
+ { "volts", "V" },
+ { "watt-hours", "Wh" },
+ { "watts", "W" }
+});
+
+String PerfdataValue::Format() const
+{
+ std::ostringstream result;
+
+ if (GetLabel().FindFirstOf(" ") != String::NPos)
+ result << "'" << GetLabel() << "'";
+ else
+ result << GetLabel();
+
+ result << "=" << Convert::ToString(GetValue());
+
+ String unit;
+
+ if (GetCounter()) {
+ unit = "c";
+ } else {
+ auto myUnit (GetUnit());
+ auto uom (l_FormatUoMs.find(myUnit.GetData()));
+
+ if (uom != l_FormatUoMs.end()) {
+ unit = uom->second;
+ }
+ }
+
+ result << unit;
+
+ if (!GetWarn().IsEmpty()) {
+ result << ";" << Convert::ToString(GetWarn());
+
+ if (!GetCrit().IsEmpty()) {
+ result << ";" << Convert::ToString(GetCrit());
+
+ if (!GetMin().IsEmpty()) {
+ result << ";" << Convert::ToString(GetMin());
+
+ if (!GetMax().IsEmpty()) {
+ result << ";" << Convert::ToString(GetMax());
+ }
+ }
+ }
+ }
+
+ return result.str();
+}
+
+Value PerfdataValue::ParseWarnCritMinMaxToken(const std::vector<String>& tokens, std::vector<String>::size_type index, const String& description)
+{
+ if (tokens.size() > index && tokens[index] != "U" && tokens[index] != "" && tokens[index].FindFirstNotOf("+-0123456789.eE") == String::NPos)
+ return Convert::ToDouble(tokens[index]);
+ else {
+ if (tokens.size() > index && tokens[index] != "")
+ Log(LogDebug, "PerfdataValue")
+ << "Ignoring unsupported perfdata " << description << " range, value: '" << tokens[index] << "'.";
+ return Empty;
+ }
+}