diff --git a/shinken/misc/perfdata.py b/shinken/misc/perfdata.py index 6e7a4c7dbb..9ed4c2261e 100755 --- a/shinken/misc/perfdata.py +++ b/shinken/misc/perfdata.py @@ -34,6 +34,8 @@ '^([^=]+)=([\d\.\-\+eE]+)([\w\/%]*)' ';?([\d\.\-\+eE:~@]+)?;?([\d\.\-\+eE:~@]+)?;?([\d\.\-\+eE]+)?;?([\d\.\-\+eE]+)?;?\s*' ) +quote_pattern = re.compile("^'(.+)'$") +replace1_pattern = re.compile('\1') # If we can return an int or a float, or None @@ -54,29 +56,34 @@ def __init__(self, s): # print "Analysis string", s r = metric_pattern.match(s) if r: - # Get the name but remove all ' in it - self.name = r.group(1).replace("'", "") + # Strip outer quotes, and replace double quotes with single + self.name = re.sub(r"^'(.+)'$", r'\1', r.group(1)).replace("''", "'") self.value = guess_int_or_float(r.group(2)) self.uom = r.group(3) self.warning = guess_int_or_float(r.group(4)) self.critical = guess_int_or_float(r.group(5)) - self.min = guess_int_or_float(r.group(6)) - self.max = guess_int_or_float(r.group(7)) + self.min = self.min_supplied = guess_int_or_float(r.group(6)) + self.max = self.max_supplied = guess_int_or_float(r.group(7)) # print 'Name', self.name # print "Value", self.value # print "Res", r # print r.groups() if self.uom == '%': - self.min = 0 - self.max = 100 + self.min = 0 if self.min is None else self.min + self.max = 100 if self.max is None else self.max def __str__(self): - s = "%s=%s%s" % (self.name, self.value, self.uom) - if self.warning: - s = s + ";%s" % (self.warning) - if self.critical: - s = s + ";%s" % (self.critical) - return s + # Restore double quotes in nae + name = self.name.replace("'", "''") + # Quote whole name if it contains a space + if " " in name: + name = "'" + name + "'" + min = self.min_supplied + max = self.max_supplied + components = ["%s=%s%s" % (name, self.value, self.uom), self.warning, self.critical, min, max] + while components[-1] is None: + components.pop() + return ";".join(map(lambda v: "" if v is None else str(v), components)) class PerfDatas: @@ -101,3 +108,4 @@ def __getitem__(self, key): def __contains__(self, key): return key in self.metrics + diff --git a/test/test_parse_perfdata.py b/test/test_parse_perfdata.py index 033a42f9dd..18933dd66c 100644 --- a/test/test_parse_perfdata.py +++ b/test/test_parse_perfdata.py @@ -45,16 +45,6 @@ def test_parsing_perfdata(self): self.assertEqual(0, m.min) self.assertEqual(1982, m.max) - s = 'ramused=90%;85;95;;' - m = Metric(s) - self.assertEqual('ramused', m.name) - self.assertEqual(90, m.value) - self.assertEqual('%', m.uom) - self.assertEqual(85, m.warning) - self.assertEqual(95, m.critical) - self.assertEqual(0, m.min) - self.assertEqual(100, m.max) - s = 'ramused=1009MB;;;0;1982 swapused=540MB;;;; memused=90%' p = PerfDatas(s) p.metrics @@ -78,26 +68,6 @@ def test_parsing_perfdata(self): self.assertEqual(3, len(p)) - s = "'Physical Memory Used'=12085620736Bytes; 'Physical Memory Utilisation'=94%;80;90;" - p = PerfDatas(s) - p.metrics - m = p['Physical Memory Used'] - self.assertEqual('Physical Memory Used', m.name) - self.assertEqual(12085620736, m.value) - self.assertEqual('Bytes', m.uom) - self.assertIs(None, m.warning) - self.assertIs(None, m.critical) - self.assertIs(None, m.min) - self.assertIs(None, m.max) - - m = p['Physical Memory Utilisation'] - self.assertEqual('Physical Memory Utilisation', m.name) - self.assertEqual(94, m.value) - self.assertEqual('%', m.uom) - self.assertEqual(80, m.warning) - self.assertEqual(90, m.critical) - self.assertEqual(0, m.min) - self.assertEqual(100, m.max) s = "'C: Space'=35.07GB; 'C: Utilisation'=87.7%;90;95;" p = PerfDatas(s) @@ -147,6 +117,118 @@ def test_parsing_perfdata(self): p = PerfDatas(s) self.assertEqual(len(p), 0) + def test_parsing_perfdata_percentages(self): + # Test default min and max automatically supplied + s = 'ramused=90%;85;95;;' + m = Metric(s) + self.assertEqual('ramused', m.name) + self.assertEqual(90, m.value) + self.assertEqual('%', m.uom) + self.assertEqual(85, m.warning) + self.assertEqual(95, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(100, m.max) + # + # Test non-default min and max are handled + s = 'ramused=90%;85;95;10;95' + m = Metric(s) + self.assertEqual('ramused', m.name) + self.assertEqual(90, m.value) + self.assertEqual('%', m.uom) + self.assertEqual(85, m.warning) + self.assertEqual(95, m.critical) + self.assertEqual(10, m.min) + self.assertEqual(95, m.max) + # + # Test non-default min and max as floats are handled + s = 'ramused=90%;85;95;10.5;95.3' + m = Metric(s) + self.assertEqual('ramused', m.name) + self.assertEqual(90, m.value) + self.assertEqual('%', m.uom) + self.assertEqual(85, m.warning) + self.assertEqual(95, m.critical) + self.assertEqual(10.5, m.min) + self.assertEqual(95.3, m.max) + + def test_parsing_perfdata_spaces(self): + s = "'Physical Memory Used'=12085620736Bytes; 'Physical Memory Utilisation'=94%;80;90;" + p = PerfDatas(s) + p.metrics + m = p['Physical Memory Used'] + self.assertEqual('Physical Memory Used', m.name) + self.assertEqual(12085620736, m.value) + self.assertEqual('Bytes', m.uom) + self.assertIs(None, m.warning) + self.assertIs(None, m.critical) + self.assertIs(None, m.min) + self.assertIs(None, m.max) + + m = p['Physical Memory Utilisation'] + self.assertEqual('Physical Memory Utilisation', m.name) + self.assertEqual(94, m.value) + self.assertEqual('%', m.uom) + self.assertEqual(80, m.warning) + self.assertEqual(90, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(100, m.max) + + def test_parsing_perfdata_quotes(self): + # Make sure outer quuotes get stripped, with or without space in name + s = "'ramused'=1009MB;;;0;1982 'ram is used'=1009MB;;;0;1982" + p = PerfDatas(s) + m = p['ramused'] + self.assertEqual('ramused', m.name) + self.assertEqual(1009, m.value) + self.assertEqual('MB', m.uom) + self.assertEqual(None, m.warning) + self.assertEqual(None, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(1982, m.max) + + m = p['ram is used'] + self.assertEqual('ram is used', m.name) + self.assertEqual(1009, m.value) + self.assertEqual('MB', m.uom) + self.assertEqual(None, m.warning) + self.assertEqual(None, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(1982, m.max) + + # Confirm double quotes replace by single + s = "ram''used=1009MB;;;0;1982" + p = PerfDatas(s) + m = p["ram'used"] + self.assertEqual("ram'used", m.name) + self.assertEqual(1009, m.value) + self.assertEqual('MB', m.uom) + self.assertEqual(None, m.warning) + self.assertEqual(None, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(1982, m.max) + + s = "ram''used''=1009MB;;;0;1982" + p = PerfDatas(s) + m = p["ram'used'"] + self.assertEqual("ram'used'", m.name) + self.assertEqual(1009, m.value) + self.assertEqual('MB', m.uom) + self.assertEqual(None, m.warning) + self.assertEqual(None, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(1982, m.max) + + s = "ram'''used''=1009MB;;;0;1982" + p = PerfDatas(s) + m = p["ram''used'"] + self.assertEqual("ram''used'", m.name) + self.assertEqual(1009, m.value) + self.assertEqual('MB', m.uom) + self.assertEqual(None, m.warning) + self.assertEqual(None, m.critical) + self.assertEqual(0, m.min) + self.assertEqual(1982, m.max) + if __name__ == '__main__': unittest.main() diff --git a/test/test_string_perfdata.py b/test/test_string_perfdata.py new file mode 100644 index 0000000000..629138ed50 --- /dev/null +++ b/test/test_string_perfdata.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2009-2014: +# Gabes Jean, naparuba@gmail.com +# Gerhard Lausser, Gerhard.Lausser@consol.de +# +# This file is part of Shinken. +# +# Shinken is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Shinken is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Shinken. If not, see . + +# +# This file is used to test reading and processing of config files +# + +from shinken_test import * +from shinken.misc.perfdata import Metric, PerfDatas + + +class TestStringPerfdata(ShinkenTest): + # Uncomment this is you want to use a specific configuration + # for your test + #def setUp(self): + # self.setup_with_file('etc/shinken_parse_perfdata.cfg') + + def test_string_all_four(self): + self.assertEqual('ramused=1009MB;1;2;3;4', str(Metric('ramused=1009MB;1;2;3;4'))) + + def test_string_drop_empty_from_end(self): + self.assertEqual('ramused=1009MB', str(Metric('ramused=1009MB'))) + self.assertEqual('ramused=1009MB;1', str(Metric('ramused=1009MB;1'))) + self.assertEqual('ramused=1009MB;1;2', str(Metric('ramused=1009MB;1;2'))) + self.assertEqual('ramused=1009MB;1;2;3', str(Metric('ramused=1009MB;1;2;3'))) + self.assertEqual('ramused=1009MB;1;2;3;4', str(Metric('ramused=1009MB;1;2;3;4'))) + + def test_string_empty_for_None(self): + self.assertEqual('ramused=1009MB', str(Metric('ramused=1009MB;'))) + self.assertEqual('ramused=1009MB', str(Metric('ramused=1009MB;;'))) + self.assertEqual('ramused=1009MB', str(Metric('ramused=1009MB;;;'))) + self.assertEqual('ramused=1009MB', str(Metric('ramused=1009MB;;;;'))) + + self.assertEqual('ramused=1009MB;;2', str(Metric('ramused=1009MB;;2'))) + self.assertEqual('ramused=1009MB;;;3', str(Metric('ramused=1009MB;;;3'))) + self.assertEqual('ramused=1009MB;;;;4', str(Metric('ramused=1009MB;;;;4'))) + + self.assertEqual('ramused=1009MB;;2;;4', str(Metric('ramused=1009MB;;2;;4'))) + + def test_string_zero_preserved(self): + self.assertEqual('ramused=1009MB;0', str(Metric('ramused=1009MB;0'))) + self.assertEqual('ramused=1009MB;;0', str(Metric('ramused=1009MB;;0'))) + self.assertEqual('ramused=1009MB;;;0', str(Metric('ramused=1009MB;;;0'))) + self.assertEqual('ramused=1009MB;;;;0', str(Metric('ramused=1009MB;;;;0'))) + + def test_string_percent_minmaxdefault_0_100(self): + # If not specified, defaults of 0 and 100 for min/max should not come back + self.assertEqual('utilization=80%;90;95', str(Metric('utilization=80%;90;95'))) + self.assertEqual('utilization=80%;90;95;;', str(Metric('utilization=80%;90;95')) + ";;") + + def test_string_percent_minmax_echo(self): + # Defined values of min max should come back always, even if defaults + self.assertEqual('utilization=80%;50;75;0;100', str(Metric('utilization=80%;50;75;0;100'))) + self.assertEqual('utilization=80%;50;75;0', str(Metric('utilization=80%;50;75;0'))) + self.assertEqual('utilization=80%;50;75;;100', str(Metric('utilization=80%;50;75;;100'))) + + # Same tests with non-default values + self.assertEqual('utilization=80%;50;75;85;95', str(Metric('utilization=80%;50;75;85;95'))) + self.assertEqual('utilization=80%;50;75;85', str(Metric('utilization=80%;50;75;85'))) + self.assertEqual('utilization=80%;50;75;;95', str(Metric('utilization=80%;50;75;;95'))) + + def test_string_quoted_names(self): + self.assertEqual("ram''used''=10", str(Metric("ram''used''=10"))) + self.assertEqual("ram''used=10", str(Metric("ram''used=10"))) + # Outer quotes present but not necessary should be stripped on format + self.assertEqual("ram''used=10", str(Metric("'ram''used'=10"))) + # But not so if there is also a space + self.assertEqual("'ram was ''used'=10", str(Metric("'ram was ''used'=10"))) + + # String missing required outer quotes, should come back quoted + # This is debatable as it basically is an invalid metric/perfdata + self.assertEqual("'ram ''used'''=10", str(Metric("ram ''used''=10"))) + +if __name__ == '__main__': + unittest.main() +