Module: check_mk
Branch: master
Commit: d4576e482a9a680b73efaf3748183013b9bc4531
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=d4576e482a9a68…
Author: Andreas Boesl <ab(a)mathias-kettner.de>
Date: Mon Oct 29 13:24:43 2018 +0100
6675 Datasource programs are now able to receive their configuration from stdin
Change-Id: I24e91aa3f581a453124f9a09189f7cf8f93d1798
---
.werks/6675 | 10 ++++++
cmk_base/data_sources/programs.py | 44 +++++++++++++++--------
tests/unit/cmk_base/test_data_sources_unit.py | 52 +++++++++++++++++++++++++--
3 files changed, 89 insertions(+), 17 deletions(-)
diff --git a/.werks/6675 b/.werks/6675
new file mode 100644
index 0000000..a58da37
--- /dev/null
+++ b/.werks/6675
@@ -0,0 +1,10 @@
+Title: Datasource programs may also receive their configuration through stdin
+Level: 1
+Component: checks
+Compatible: compat
+Edition: cre
+Version: 1.6.0i1
+Date: 1541508999
+Class: feature
+
+
diff --git a/cmk_base/data_sources/programs.py b/cmk_base/data_sources/programs.py
index 73d0955..f197547 100644
--- a/cmk_base/data_sources/programs.py
+++ b/cmk_base/data_sources/programs.py
@@ -27,6 +27,7 @@
import os
import signal
import subprocess
+import collections
import cmk.paths
@@ -56,10 +57,10 @@ class ProgramDataSource(CheckMKAgentDataSource):
return "ds"
def _execute(self):
- command_line = self._get_command_line()
- return self._get_agent_info_program(command_line)
+ command_line, command_stdin = self._get_command_line_and_stdin()
+ return self._get_agent_info_program(command_line, command_stdin)
- def _get_agent_info_program(self, commandline):
+ def _get_agent_info_program(self, commandline, command_stdin):
exepath = commandline.split()[0] # for error message, hide options!
self._logger.debug("Calling external program %r" % (commandline))
@@ -69,7 +70,7 @@ class ProgramDataSource(CheckMKAgentDataSource):
p = subprocess.Popen( # nosec
commandline,
shell=True,
- stdin=open(os.devnull),
+ stdin=subprocess.PIPE if command_stdin else open(os.devnull),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
preexec_fn=os.setsid,
@@ -81,11 +82,11 @@ class ProgramDataSource(CheckMKAgentDataSource):
p = subprocess.Popen( # nosec
commandline,
shell=True,
- stdin=open(os.devnull),
+ stdin=subprocess.PIPE if command_stdin else open(os.devnull),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
- stdout, stderr = p.communicate()
+ stdout, stderr = p.communicate(input=command_stdin)
exitstatus = p.returncode
except MKTimeout:
# On timeout exception try to stop the process to prevent child process
"leakage"
@@ -109,13 +110,18 @@ class ProgramDataSource(CheckMKAgentDataSource):
return stdout
- def _get_command_line(self):
+ def _get_command_line_and_stdin(self):
"""Returns the final command line to be
executed"""
raise NotImplementedError()
def describe(self):
"""Return a short textual description of the
agent"""
- return "Program: %s" % self._get_command_line()
+ command_line, command_stdin = self._get_command_line_and_stdin()
+ response = ["Program: %s" % command_line]
+ if command_stdin:
+ response.extend([" Program stdin:", command_stdin])
+ return "\n".join(response)
+
class DSProgramDataSource(ProgramDataSource):
@@ -128,16 +134,17 @@ class DSProgramDataSource(ProgramDataSource):
def name(self):
"""Return a unique (per host) textual identification of the data
source"""
- program = self._get_command_line().split(" ")[0]
+ command_line, _command_stdin = self._get_command_line_and_stdin()
+ program = command_line.split(" ")[0]
return os.path.basename(program)
- def _get_command_line(self):
+ def _get_command_line_and_stdin(self):
cmd = self._command_template
cmd = self._translate_legacy_macros(cmd)
cmd = self._translate_host_macros(cmd)
- return cmd
+ return cmd, None
def _translate_legacy_macros(self, cmd):
# Make "legacy" translation. The users should use the $...$ macros in
future
@@ -155,6 +162,8 @@ class DSProgramDataSource(ProgramDataSource):
return core_config.replace_macros(cmd, macros)
+SpecialAgentConfiguration = collections.namedtuple("SpecialAgentConfiguration",
["args", "stdin"])
+
class SpecialAgentDataSource(ProgramDataSource):
def __init__(self, hostname, ipaddress, special_agent_id, params):
self._special_agent_id = special_agent_id
@@ -175,10 +184,17 @@ class SpecialAgentDataSource(ProgramDataSource):
def _gather_check_plugin_names(self):
return config.discoverable_tcp_checks()
- def _get_command_line(self):
+ def _get_command_line_and_stdin(self):
"""Create command line using the
special_agent_info"""
info_func = config.special_agent_info[self._special_agent_id]
- cmd_arguments = info_func(self._params, self._hostname, self._ipaddress)
+ agent_configuration = info_func(self._params, self._hostname, self._ipaddress)
+ if isinstance(agent_configuration, SpecialAgentConfiguration):
+ cmd_arguments = agent_configuration.args
+ command_stdin = agent_configuration.stdin
+ else:
+ cmd_arguments = agent_configuration
+ command_stdin = None
+
final_arguments = config.prepare_check_command(
cmd_arguments, self._hostname, service_description=None)
@@ -190,4 +206,4 @@ class SpecialAgentDataSource(ProgramDataSource):
else:
path = special_agents_dir + "/agent_" + self._special_agent_id
- return path + " " + final_arguments
+ return path + " " + final_arguments, command_stdin
diff --git a/tests/unit/cmk_base/test_data_sources_unit.py
b/tests/unit/cmk_base/test_data_sources_unit.py
index d1a06bd..a97572f 100644
--- a/tests/unit/cmk_base/test_data_sources_unit.py
+++ b/tests/unit/cmk_base/test_data_sources_unit.py
@@ -1,12 +1,14 @@
import pytest
import cmk_base.data_sources.abstract
+import cmk_base.data_sources.programs
import cmk_base.data_sources.snmp
import cmk_base.data_sources.tcp
import cmk_base.ip_lookup as ip_lookup
import cmk_base.config as config
import cmk_base.exceptions
+
def test_data_source_cache_default():
source = cmk_base.data_sources.snmp.SNMPDataSource("hostname",
"ipaddress")
assert not source.is_agent_cache_disabled()
@@ -57,14 +59,17 @@ def test_mgmt_board_data_source_is_ip_address():
("127.0.1.1", "lolo", True),
(None, "lolo", False),
])
-def test_mgmt_board_data_source_management_board_ipaddress(monkeypatch, result, address,
resolvable):
+def test_mgmt_board_data_source_management_board_ipaddress(monkeypatch, result, address,
+ resolvable):
source =
cmk_base.data_sources.snmp.SNMPManagementBoardDataSource("hostname",
"ipaddress")
if resolvable:
monkeypatch.setattr(ip_lookup, "lookup_ip_address", lambda h:
"127.0.1.1")
else:
+
def raise_exc(h):
raise cmk_base.exceptions.MKIPAddressLookupError("Failed to...")
+
monkeypatch.setattr(ip_lookup, "lookup_ip_address", raise_exc)
monkeypatch.setattr(config, "management_address_of", lambda h: address)
@@ -85,9 +90,9 @@ def test_normalize_ip(ip_in, ips_out):
((0, ''), None, "127.0.0.1"),
((0, ', allowed IP ranges: 1.2.3.4'), "1.2.3.4",
"1.2.3.4"),
((1, ', invalid access configuration:'
- ' agent allows extra: 1.2.4.6 1.2.5.6(!)'), "1.2.{3,4,5}.6",
"1.2.3.6"),
+ ' agent allows extra: 1.2.4.6 1.2.5.6(!)'), "1.2.{3,4,5}.6",
"1.2.3.6"),
((1, ', invalid access configuration:'
- ' agent blocks: 1.2.3.5 1.2.3.4(!)'), "1.2.3.6",
"1.2.3.{4,5,6}"),
+ ' agent blocks: 1.2.3.5 1.2.3.4(!)'), "1.2.3.6",
"1.2.3.{4,5,6}"),
])
def test_tcpdatasource_only_from(monkeypatch, result, reported, rule):
source = cmk_base.data_sources.tcp.TCPDataSource("hostname",
"ipaddress")
@@ -97,3 +102,44 @@ def test_tcpdatasource_only_from(monkeypatch, result, reported,
rule):
assert source._sub_result_only_from({"onlyfrom": reported}) == result
+
+(a)pytest.mark.parametrize("info_func_result,expected"cted", [
+ (
+ "arg0 arg1",
+ ("arg0 arg1", None),
+ ),
+ (
+ ["arg0", "arg1"],
+ ("'arg0' 'arg1'", None),
+ ),
+ (
+ cmk_base.data_sources.programs.SpecialAgentConfiguration("arg0",
None),
+ ("arg0", None),
+ ),
+ (
+ cmk_base.data_sources.programs.SpecialAgentConfiguration("arg0 arg1",
None),
+ ("arg0 arg1", None),
+ ),
+ (
+ cmk_base.data_sources.programs.SpecialAgentConfiguration(["list0",
"list1"], None),
+ ("'list0' 'list1'", None),
+ ),
+ (
+ cmk_base.data_sources.programs.SpecialAgentConfiguration("arg0 arg1",
"stdin_blob"),
+ ("arg0 arg1", "stdin_blob"),
+ ),
+ (
+ cmk_base.data_sources.programs.SpecialAgentConfiguration(["list0",
"list1"], "stdin_blob"),
+ ("'list0' 'list1'", "stdin_blob"),
+ ),
+])
+def test_get_command_line_and_stdin(monkeypatch, info_func_result, expected):
+ special_agent_id = "bi"
+ agent_prefix = "share/check_mk/agents/special/agent_%s " %
special_agent_id
+ ds = cmk_base.data_sources.programs.SpecialAgentDataSource("testhost",
"127.0.0.1",
+ special_agent_id, None)
+ monkeypatch.setattr(config, "special_agent_info",
+ {special_agent_id: lambda a, b, c: info_func_result})
+ command_line, command_stdin = ds._get_command_line_and_stdin()
+ assert command_line == agent_prefix + expected[0]
+ assert command_stdin == expected[1]