Module: check_mk
Branch: master
Commit: 7aca4178dbe09584ce5483f6718e145b8141a5ac
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=7aca4178dbe095…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Fri Oct 19 20:37:24 2018 +0200
Special agents can now use the password store
* The special agent arguments need to be produced as list by the
special_agent_info function.
* The function passwordstore_get_cmdline() can be used to add a
password from the password store to the command line arguments.
Have a look at the 3par special agent related files for an example.
CMK-531
Change-Id: I686c0f7923eba8b3839a2624321a2af899b4c83e
---
cmk_base/config.py | 55 +++++++++++++++++++++++++++++++++
cmk_base/core_config.py | 41 ++----------------------
cmk_base/data_sources/programs.py | 3 +-
tests/unit/cmk_base/test_config.py | 34 ++++++++++++++++++++
tests/unit/cmk_base/test_core_config.py | 16 ++++++++++
5 files changed, 110 insertions(+), 39 deletions(-)
diff --git a/cmk_base/config.py b/cmk_base/config.py
index 86bfb08..da49014 100644
--- a/cmk_base/config.py
+++ b/cmk_base/config.py
@@ -1527,6 +1527,61 @@ def get_service_translations(hostname):
translations_cache[hostname] = translations
return translations
+
+def prepare_check_command(command_spec, hostname, service_description):
+ """Prepares a check command for execution by Check_MK.
+
+ This function either accepts a string or a list of arguments as
+ command_spec. In case a list is given it quotes the single elements. It
+ also prepares password store entries for the command line. These entries
+ will be completed by the executed program later to get the password from
+ the password store.
+ """
+ if isinstance(command_spec, basestring):
+ return command_spec
+
+ if not isinstance(command_spec, list):
+ raise NotImplementedError()
+
+ passwords, formated = [], []
+ for arg in command_spec:
+ arg_type = type(arg)
+
+ if arg_type in [ int, float ]:
+ formated.append("%s" % arg)
+
+ elif arg_type in [ str, unicode ]:
+ formated.append(cmk_base.utils.quote_shell_string(arg))
+
+ elif arg_type == tuple and len(arg) == 3:
+ pw_ident, preformated_arg = arg[1:]
+ try:
+ password = stored_passwords[pw_ident]["password"]
+ except KeyError:
+ if hostname and service_description:
+ descr = " used by service \"%s\" on host
\"%s\"" % (service_description, hostname)
+ elif hostname:
+ descr = " used by host host \"%s\"" % (hostname)
+ else:
+ descr = ""
+
+ console.warning("The stored password \"%s\"%s does not
exist (anymore)." %
+ (pw_ident, descr))
+ password = "%%%"
+
+ pw_start_index = str(preformated_arg.index("%s"))
+ formated.append(cmk_base.utils.quote_shell_string(preformated_arg %
("*" * len(password))))
+ passwords.append((str(len(formated)), pw_start_index, pw_ident))
+
+ else:
+ raise MKGeneralException("Invalid argument for command line: %r" %
(arg,))
+
+ if passwords:
+ formated = [ "--pwstore=%s" % ",".join([
"(a)".join(p) for p in passwords ]) ] + formated
+
+ return " ".join(formated)
+
+
#.
# .--Service rules-------------------------------------------------------.
# | ____ _ _ |
diff --git a/cmk_base/core_config.py b/cmk_base/core_config.py
index 1c6e1f3..e1dc126 100644
--- a/cmk_base/core_config.py
+++ b/cmk_base/core_config.py
@@ -33,7 +33,6 @@ import cmk.tty as tty
import cmk.password_store
from cmk.exceptions import MKGeneralException
-import cmk_base.utils
import cmk_base.console as console
import cmk_base.config as config
import cmk_base.ip_lookup as ip_lookup
@@ -303,46 +302,12 @@ def do_update(core, with_precompile):
# '----------------------------------------------------------------------'
def active_check_arguments(hostname, description, args):
- if type(args) in [ str, unicode ]:
- return args
-
- elif type(args) == list:
- passwords, formated = [], []
- for arg in args:
- arg_type = type(arg)
-
- if arg_type in [ int, float ]:
- formated.append("%s" % arg)
-
- elif arg_type in [ str, unicode ]:
- formated.append(cmk_base.utils.quote_shell_string(arg))
-
- elif arg_type == tuple and len(arg) == 3:
- pw_ident, preformated_arg = arg[1:]
- try:
- password = config.stored_passwords[pw_ident]["password"]
- except KeyError:
- warning("The stored password \"%s\" used by service
\"%s\" on host "
- "\"%s\" does not exist
(anymore)." %
- (pw_ident, description, hostname))
- password = "%%%"
-
- pw_start_index = str(preformated_arg.index("%s"))
- formated.append(cmk_base.utils.quote_shell_string(preformated_arg %
("*" * len(password))))
- passwords.append((str(len(formated)), pw_start_index, pw_ident))
-
- else:
- raise MKGeneralException("Invalid argument for command line:
%s" % arg)
-
- if passwords:
- formated = [ "--pwstore=%s" % ",".join([
"(a)".join(p) for p in passwords ]) ] + formated
-
- return " ".join(formated)
-
- else:
+ if not isinstance(args, (str, unicode, list)):
raise MKGeneralException("The check argument function needs to return either
a list of arguments or a "
"string of the concatenated arguments (Host: %s,
Service: %s)." % (hostname, description))
+ return config.prepare_check_command(args, hostname, description)
+
#.
# .--HostAttributes------------------------------------------------------.
diff --git a/cmk_base/data_sources/programs.py b/cmk_base/data_sources/programs.py
index db33c54..3fa555c 100644
--- a/cmk_base/data_sources/programs.py
+++ b/cmk_base/data_sources/programs.py
@@ -185,6 +185,7 @@ class SpecialAgentDataSource(ProgramDataSource):
"""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)
+ final_arguments = config.prepare_check_command(cmd_arguments, self._hostname,
service_description=None)
special_agents_dir = cmk.paths.agents_dir + "/special"
local_special_agents_dir = cmk.paths.local_agents_dir + "/special"
@@ -194,4 +195,4 @@ class SpecialAgentDataSource(ProgramDataSource):
else:
path = special_agents_dir + "/agent_" + self._special_agent_id
- return path + " " + cmd_arguments
+ return path + " " + final_arguments
diff --git a/tests/unit/cmk_base/test_config.py b/tests/unit/cmk_base/test_config.py
index ffc2283..419fe9b 100644
--- a/tests/unit/cmk_base/test_config.py
+++ b/tests/unit/cmk_base/test_config.py
@@ -1,3 +1,5 @@
+# encoding: utf-8
+
import pytest
import cmk_base.config as config
@@ -91,3 +93,35 @@ def test_is_all_agents_host(monkeypatch, hostname, tags, result):
def test_is_all_special_agents_host(monkeypatch, hostname, tags, result):
monkeypatch.setattr(config, "tags_of_host", lambda h: {hostname: tags}[h])
assert config.is_all_special_agents_host(hostname) == result
+
+
+def test_prepare_check_command_basics():
+ assert config.prepare_check_command(u"args 123 -x 1 -y 2", "bla",
"blub") \
+ == u"args 123 -x 1 -y 2"
+
+ assert config.prepare_check_command(["args", "123",
"-x", "1", "-y", "2"], "bla",
"blub") \
+ == "'args' '123' '-x' '1' '-y'
'2'"
+
+ assert config.prepare_check_command(["args", "1 2 3",
"-d=2", "--hallo=eins", 9], "bla", "blub") \
+ == "'args' '1 2 3' '-d=2' '--hallo=eins'
9"
+
+ with pytest.raises(NotImplementedError):
+ config.prepare_check_command((1, 2), "bla", "blub")
+
+
+(a)pytest.mark.parametrize("pw"t;pw", ["abc", "123",
"x'äd!?", u"aädg"])
+def test_prepare_check_command_password_store(monkeypatch, pw):
+ monkeypatch.setattr(config, "stored_passwords", {
+ "pw-id": {
+ "password": pw,
+ }
+ })
+ assert config.prepare_check_command(["arg1", ("store",
"pw-id", "--password=%s"), "arg3"], "bla",
"blub") \
+ == "--pwstore=2@11@pw-id 'arg1' '--password=%s'
'arg3'" % ("*" * len(pw))
+
+
+def test_prepare_check_command_not_existing_password(capsys):
+ assert config.prepare_check_command(["arg1", ("store",
"pw-id", "--password=%s"), "arg3"], "bla",
"blub") \
+ == "--pwstore=2@11@pw-id 'arg1' '--password=***'
'arg3'"
+ stderr = capsys.readouterr().err
+ assert "The stored password \"pw-id\" used by service
\"blub\" on host \"bla\"" in stderr
diff --git a/tests/unit/cmk_base/test_core_config.py
b/tests/unit/cmk_base/test_core_config.py
new file mode 100644
index 0000000..e4d0236
--- /dev/null
+++ b/tests/unit/cmk_base/test_core_config.py
@@ -0,0 +1,16 @@
+import pytest
+
+from cmk.exceptions import MKGeneralException
+import cmk_base.config as config
+import cmk_base.core_config as core_config
+
+def test_active_check_arguments(mocker):
+ with pytest.raises(MKGeneralException):
+ core_config.active_check_arguments("bla", "blub", 1)
+
+ with pytest.raises(MKGeneralException):
+ core_config.active_check_arguments("bla", "blub", (1, 2))
+
+ prepare_check_command = mocker.patch.object(config,
"prepare_check_command")
+ core_config.active_check_arguments("bla", "blub", u"args 123
-x 1 -y 2")
+ assert prepare_check_command.called_once()