Hi everyone,
I know there are not many options to make the topic even more generous, but I did that purposefully.
I am now working multiple days on the first baby steps of writing a CheckMK plugin, but I do not even get to a point where I succeed even with hardcoded scripts; not even starting to think about appropriate WATO integration …
All resources I have found so far are anything between the lines of …
- lookup examples in CheckMK Exchange
- look at the Sphinx-docs (shipped with any instance of CheckMK), which are very sparse
- lookup something here in the forum
And that’s what CheckMK calls “Developer Docs” - so far! Recent Live-Stream events have shown, that even the CheckMK internal Devs struggle by missing straightforward docs (see Plug-in Migration Livestream Announcement or Developer Hour: New Plug-in Developer APIs Announcement).
What I’d love to see in this topic is an up-to-date, end-to-end summary of what must be done to develop a CheckMK plugin; there are no references to some 2017 Articles or reverse-engineering from CheckMK Exchange.
It’s not that I am too lazy to read; it’s that I can’t get to any result and am constantly wondering how exactly multiple things are meant.
I’d like to achieve the following with this thread: Learning to write a Check Plugin, which sends an HTTP Query to some Web-API and returns the number of lines returned from that API as a metric. The required Authentication Token to access the resource should come from CheckMK’s Password-Store and apart from that, only the URL of the remote API should be configurable in the WebUI.
Doesn’t sound too exotic, does it?
Pure-Python code, without any CheckMK would look like this:
#!/usr/bin/env python3
import httpx
from typing import Tuple
from argparse import ArgumentParser, Namespace
def parse_args() -> Namespace:
parser = ArgumentParser(
description="Part of CheckMK Plugin 'dhcpac_api'.",
)
parser.add_argument(
"-u", "--url",
help="The URL of the API to query.",
action="store",
required=True,
)
parser.add_argument(
"-t", "--token",
help="The Token to use to auth against the API.",
action="store",
required=True,
)
return parser.parse_args()
def get_records_from_api(url: str, token: str) -> Tuple[int, int]:
headers = {"Authorization": f"Bearer {token}"}
response = httpx.get(url, headers=headers)
status_code = response.status_code
record_count = 0
if status_code == 200:
try:
# Remove first line, since this is a CVS Header and not a real record
record_count = len(response.content.decode().split()[1:])
except (UnicodeDecodeError, IndexError, UnicodeError):
pass
return status_code, record_count
def main():
args = parse_args()
status_code, record_count = get_records_from_api(url=args.url, token=args.token)
print(f"{status_code};{record_count}")
if __name__ == "__main__":
main()
Nothing too exotic.
From what I think I learned from the Developing extensions for Checkmk resource, is that a Special Agent would be the best fit, since these are executed on the CheckMK server and don’t require an agent to be enrolled anywhere.
Here comes the first question already: Is that correct? Or is a " Native agent-based check plug-ins" or “Local check” a better match? If so: Please explain why.
Based on the new CheckMK DevAPI v2, I understood that such a 3rd party Special Agent would have to be placed in ~/local/lib/python3/cmk_addons/plugins/<FAMILY_NAME>
as it’s main path.
This is already the 2nd question: Is that the correct location? If not: Which is?
Studying the Sphinx-Docs, a Special Agent has to be put in the subfolder of that main path, named server_side_calls
and within that, a script, which needs to define an object that has a name beginning with special_agent_
and needs to be an instance of either SpecialAgentConfig()
or SpecialAgentCommand()
.
Both expect a script in the sub-folder libexec
of the main path, which is named agent_
+“name”-field of SpecialAgentConfig()
/SpecialAgentCommand()
; so, for example:
special_agent_dhcpac_api = SpecialAgentConfig(
name="dhcpac_api",
...
)
would expect an executable script at ~/local/lib/python3/cmk_addons/plugins/<FAMILY_NAME>/libexec/agent_dhcpac_api
.
Even more questions so far: Is all of this correct?
Before I make this Monster of a post even more complicated, I’ll pause here and wait for any feedback on what I wrote so far, to not brabble more nonsense, in case anything was wrong up until here.
I’m looking forward to your answers! After I have sorted everything out, I will make a blog post from the summary, hopefully helping others as well.