Module: check_mk
Branch: master
Commit: 482e439fa06475a83bd3034b5ffab6c04479702a
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=482e439fa06475…
Author: Tom Baerwinkel <tb(a)mathias-kettner.de>
Date: Mon Sep 3 10:44:50 2018 +0200
wmi.include: fix two possible exceptions during parsing and inventory
Change-Id: Ice8cfb9cab3730f1a689ac3df78172881f977857
---
checks/wmi.include | 12 ++++++------
.../datasets/wmi_include_empty_info_regression.py | 12 ++++++++++++
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/checks/wmi.include b/checks/wmi.include
index 7ddede6..1f6e986 100644
--- a/checks/wmi.include
+++ b/checks/wmi.include
@@ -167,6 +167,8 @@ class WMITable(object):
def parse_wmi_table(info, key="Name"):
+ parsed = {}
+
try:
# read input line by line. rows with [] start the table name.
# Each table has to start with a header line
@@ -181,7 +183,6 @@ def parse_wmi_table(info, key="Name"):
timestamp, frequency = int(line[1]), int(line[2])
line = info_iter.next()
- parsed = {}
while line is not None:
check_wmi_timeout(line)
@@ -249,7 +250,7 @@ def inventory_wmi_table(tables, **kwargs):
# only happen if required_tables was set
return []
- potential_instances = None
+ potential_instances = set()
if kwargs.get("skip_intersection", False):
# Unfortunately there are tables which do NOT provide any
# names. In this case we fallback to old behaviour.
@@ -263,10 +264,10 @@ def inventory_wmi_table(tables, **kwargs):
# inventarize one item per instance that exists in all tables
for required_table in required_tables:
table_rows = tables[required_table].row_labels()
- if potential_instances is None:
- potential_instances = set(table_rows)
- else:
+ if potential_instances:
potential_instances &= set(table_rows)
+ else:
+ potential_instances = set(table_rows)
# if there are multiple instances, don't include the summary line as it is redundant
if len(potential_instances) > 1:
@@ -479,4 +480,3 @@ def wmi_yield_raw_fraction(table, row, column, label, perfvar, levels=None):
)
#.
-
diff --git a/tests/unit/checks/generictests/datasets/wmi_include_empty_info_regression.py b/tests/unit/checks/generictests/datasets/wmi_include_empty_info_regression.py
new file mode 100644
index 0000000..ccd9730
--- /dev/null
+++ b/tests/unit/checks/generictests/datasets/wmi_include_empty_info_regression.py
@@ -0,0 +1,12 @@
+
+
+checkname = 'wmi_webservices'
+
+
+info = []
+
+
+discovery = {}
+
+
+checks = {}
Module: check_mk
Branch: master
Commit: a36eb3a6c9b9bc107b9eeaf12a1cfa71a3795dd9
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=a36eb3a6c9b9bc…
Author: Tom Baerwinkel <tb(a)mathias-kettner.de>
Date: Tue Sep 4 09:24:47 2018 +0200
6409 FIX wmi.include: don't throw exepctions in the parse function
In previous versions the parse function provided in wmi.include
(used in different WMI checks like e.g. wmi_cpuload) raised
a MKCounterWrapped exception if a WMI timeout occured. This
leads to a crash during the service discovery when a timeout
occurs.
Now the parse function always returns valid data. If a timeout
occurs an empty table is added to the parsed data. The inventory
function can then easily check for empty tables. If on the other
hand the check function tries to access an empty table a
MKCounterWrapped exception is raised.
CMK-868
Change-Id: I22bb9470feb3e4aa9ed47df2c068f4fa78258290
---
.werks/6409 | 20 ++++++++++++++++++++
checks/wmi.include | 41 ++++++++++++++++++++++++++---------------
2 files changed, 46 insertions(+), 15 deletions(-)
diff --git a/.werks/6409 b/.werks/6409
new file mode 100644
index 0000000..cafb750
--- /dev/null
+++ b/.werks/6409
@@ -0,0 +1,20 @@
+Title: wmi.include: don't throw exepctions in the parse function
+Level: 1
+Component: checks
+Compatible: compat
+Edition: cre
+Version: 1.6.0i1
+Date: 1536044950
+Class: fix
+
+In previous versions the parse function provided in wmi.include
+(used in different WMI checks like e.g. wmi_cpuload) raised
+a MKCounterWrapped exception if a WMI timeout occured. This
+leads to a crash during the service discovery when a timeout
+occurs.
+
+Now the parse function always returns valid data. If a timeout
+occurs an empty table is added to the parsed data. The inventory
+function can then easily check for empty tables. If on the other
+hand the check function tries to access an empty table a
+MKCounterWrapped exception is raised.
diff --git a/checks/wmi.include b/checks/wmi.include
index da04cd4..adb8e13 100644
--- a/checks/wmi.include
+++ b/checks/wmi.include
@@ -47,6 +47,12 @@
# '----------------------------------------------------------------------'
+class NullTable(object):
+ """Represents an empty table caused by a WMI timeout."""
+ def __getattr__(self, name):
+ raise MKCounterWrapped('WMI query timed out')
+
+
class WMITable(object):
"""
Represents a 2-dimensional table of performance metrics.
@@ -168,55 +174,56 @@ class WMITable(object):
def parse_wmi_table(info, key="Name"):
parsed = {}
+ info_iter = iter(info)
try:
# read input line by line. rows with [] start the table name.
# Each table has to start with a header line
- info_iter = iter(info)
line = info_iter.next()
- # Did main section get WMI timeout?
- check_wmi_timeout(line)
-
timestamp, frequency = None, None
if line[0] == "sampletime":
timestamp, frequency = int(line[1]), int(line[2])
line = info_iter.next()
- while line is not None:
- check_wmi_timeout(line)
-
+ while True:
if len(line) == 1 and line[0].startswith("["):
# multi-table input
tablename = regex(r"\[(.*)\]").search(line[0]).group(1)
# Did subsection get WMI timeout?
line = info_iter.next()
- check_wmi_timeout(line)
else:
# single-table input
tablename = ""
+ if is_wmi_timeout(line):
+ parsed[tablename] = NullTable()
+ line = info_iter.next()
+ continue
+
if tablename not in parsed:
parsed[tablename] = WMITable(tablename, line, key, timestamp, frequency)
current = parsed[tablename]
# read table content
line = info_iter.next()
- while line is not None and not line[0].startswith("["):
- check_wmi_timeout(line)
+ while not line[0].startswith("["):
+ if is_wmi_timeout(line):
+ parsed[tablename] = NullTable()
+ line = info_iter.next()
+ continue
current.add_row(line)
line = info_iter.next()
- except StopIteration:
+ except (StopIteration, ValueError):
# regular end of block
pass
return parsed
-def check_wmi_timeout(line):
- if line[0] == "WMItimeout":
- raise MKCounterWrapped("WMI query timed out")
+def is_wmi_timeout(line):
+ return line[0] == "WMItimeout"
#.
@@ -229,6 +236,7 @@ def check_wmi_timeout(line):
# | |
# '----------------------------------------------------------------------'
+
def wmi_filter_global_only(tables, row):
return all(table.get(row, "Name") == "_Global_" for table in tables.values())
@@ -243,9 +251,11 @@ def wmi_filter_global_only(tables, row):
# | |___/ |
# '----------------------------------------------------------------------'
+
def inventory_wmi_table(tables, **kwargs):
required_tables = kwargs.get('required_tables', tables.keys())
- if not set(required_tables) <= set(tables.keys()):
+ if (not set(required_tables) <= set(tables.keys()) or
+ any(isinstance(table, NullTable) for table in tables.itervalues())):
# if table is missing, nothing gets inventorized. This can
# only happen if required_tables was set
return []
@@ -291,6 +301,7 @@ def inventory_wmi_table(tables, **kwargs):
# | |
# '----------------------------------------------------------------------'
+
# determine time at which a sample was taken
def get_wmi_time(table, row):
timestamp = table.timestamp() or table.get(row, "Timestamp_PerfTime")
Module: check_mk
Branch: master
Commit: ba4877b1b4447e7dca5c3665d2554a501b5cb030
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=ba4877b1b4447e…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Sep 10 09:54:09 2018 +0200
6554 FIX Raw Edition: Improved handling of configuration errors
While creating a configuration for the Nagios core (e.g. using "cmk -U" or during "omd update")
exceptions may be raised which are caused by configuration issues.
When using the Nagios core Check_MK does not produce a half written object file anymore. In case
of such an error Check_MK throws away everything and keeps the old file.
You can then start the site with the old configuration and fix the configuration issue while the
monitoring is running with the old config.
Change-Id: Ie46ea15fec29875e7d949ba1f56b16dc4801ba71
---
.werks/6554 | 19 +++++++++++++++++++
cmk_base/core_nagios.py | 33 +++++++++++++++++++++++++++++++--
omd/packages/omd/CORE.hook | 2 +-
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/.werks/6554 b/.werks/6554
new file mode 100644
index 0000000..d772d59
--- /dev/null
+++ b/.werks/6554
@@ -0,0 +1,19 @@
+Title: Raw Edition: Improved handling of configuration errors
+Level: 2
+Component: core
+Class: fix
+Compatible: compat
+Edition: cre
+State: unknown
+Version: 1.6.0i1
+Date: 1536324895
+
+
+While creating a configuration for the Nagios core (e.g. using "cmk -U" or during "omd update")
+exceptions may be raised which are caused by configuration issues.
+
+When using the Nagios core Check_MK does not produce a half written object file anymore. In case
+of such an error Check_MK throws away everything and keeps the old file.
+
+You can then start the site with the old configuration and fix the configuration issue while the
+monitoring is running with the old config.
diff --git a/cmk_base/core_nagios.py b/cmk_base/core_nagios.py
index 5bc77c9..13fa92d 100644
--- a/cmk_base/core_nagios.py
+++ b/cmk_base/core_nagios.py
@@ -30,6 +30,8 @@ import os
import subprocess
import sys
import py_compile
+import tempfile
+import errno
import cmk.paths
import cmk.tty as tty
@@ -47,8 +49,34 @@ import cmk_base.check_api_utils as check_api_utils
class NagiosCore(core_config.MonitoringCore):
def create_config(self):
- with file(cmk.paths.nagios_objects_file, "w") as out:
- create_config(out, None)
+ """Tries to create a new Check_MK object configuration file for the Nagios core
+
+ During create_config() exceptions may be raised which are caused by configuration issues.
+ Don't produce a half written object file. Simply throw away everything and keep the old file.
+
+ The user can then start the site with the old configuration and fix the configuration issue
+ while the monitoring is running.
+ """
+ tmp_path = None
+ try:
+ with tempfile.NamedTemporaryFile("w", dir=os.path.dirname(cmk.paths.nagios_objects_file),
+ prefix=".%s.new" % os.path.basename(cmk.paths.nagios_objects_file),
+ delete=False) as tmp:
+ tmp_path = tmp.name
+ os.chmod(tmp.name, 0660)
+ create_config(tmp, hostnames=None)
+ os.rename(tmp.name, cmk.paths.nagios_objects_file)
+
+ except Exception, e:
+ # In case an exception happens cleanup the tempfile created for writing
+ try:
+ if tmp_path:
+ os.unlink(tmp_path)
+ except IOError, e:
+ if e.errno == errno.ENOENT: # No such file or directory
+ pass
+
+ raise
def precompile(self):
console.output("Precompiling host checks...")
@@ -56,6 +84,7 @@ class NagiosCore(core_config.MonitoringCore):
console.output(tty.ok + "\n")
+
# .--Create config-------------------------------------------------------.
# | ____ _ __ _ |
# | / ___|_ __ ___ __ _| |_ ___ ___ ___ _ __ / _(_) __ _ |
diff --git a/omd/packages/omd/CORE.hook b/omd/packages/omd/CORE.hook
index cbe1345..9b488d9 100755
--- a/omd/packages/omd/CORE.hook
+++ b/omd/packages/omd/CORE.hook
@@ -67,7 +67,7 @@ case "$1" in
make_link nagios $OMD_ROOT/etc/init.d/core
# Refresh Check_MK Configuration after changing back from CMC
if [ -e $OMD_ROOT/var/check_mk/core/config ] ; then
- cmk -U >/dev/null 2>&1
+ cmk -U >/dev/null
fi
elif [ "$2" == "icinga" ]
then