Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further fixes to min/max handling and quotes in names #1833

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions shinken/misc/perfdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -101,3 +108,4 @@ def __getitem__(self, key):

def __contains__(self, key):
return key in self.metrics

142 changes: 112 additions & 30 deletions test/test_parse_perfdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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()

94 changes: 94 additions & 0 deletions test/test_string_perfdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2009-2014:
# Gabes Jean, [email protected]
# Gerhard Lausser, [email protected]
#
# 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 <http://www.gnu.org/licenses/>.

#
# 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()