MKP 1.2.0, added version check

This commit is contained in:
mellis 2024-01-26 15:05:03 +01:00
parent 276e67d473
commit 3ae427459a
9 changed files with 165 additions and 16 deletions

View File

@ -8,13 +8,6 @@ Email domains with quota usage, number of mailboxes, number of aliases
Mailboxes with quota usage, number of messages, last logins via IMAP/POP3/SMTP Mailboxes with quota usage, number of messages, last logins via IMAP/POP3/SMTP
You have to create an API Key for the MKP to work. Login to your Mailcow instance and go to
System|Configuration
Take a note of the section API, expand it and activate "Activate API" in the section "Read-Only Access"
Don't forget to insert the IP of your CheckMK instance into the textbox "Allow API access from these IPs/CIDR networks".
## Caveats: ## Caveats:
Tested only with dockerized versions of Mailcow Tested only with dockerized versions of Mailcow
@ -23,6 +16,19 @@ Tested with Mailcow version 2022-07a and higher
Please feel free to add other working environments via review/email. Please feel free to add other working environments via review/email.
## Upgrade from older MKPs (before 1.2.0):
If you upgrade from a already installed version before 1.2.0, you have to re-create your rules for the "Mailcow Server Information" (reason: massive parameter changes).
## Steps to accomplish this without problems:
1. Take a screenshot of your settings in the above mentioned ruleset
2. Assure that you have access to the API keys you used within the current rules
3. Delete all rules for "Mailcow Server Information"
4. Install and enable the new MKP
5. Re-create your rules with the previously saved information from steps 1 and 2
6. Apply your changes
## General installation instructions: ## General installation instructions:
1. Upload and enable the MKP 1. Upload and enable the MKP
@ -38,8 +44,21 @@ Please feel free to add other working environments via review/email.
11. Execute a service discovery on your Mailcow host(s) and accept the newly detected services 11. Execute a service discovery on your Mailcow host(s) and accept the newly detected services
12. Feel free to adjust the various parameters for thresholds within the available parameter sections (Setup, "Service monitoring rules" after searching for "Mailcow") 12. Feel free to adjust the various parameters for thresholds within the available parameter sections (Setup, "Service monitoring rules" after searching for "Mailcow")
## How to create the API Key:
1. Login to your Mailcow instance with an administrative user
2. Got to "System|Configuration"
3. Take a note of the Section "API" and expand it
4. Within the section "Read-Only Access":
4.1 Activate the checkbox "Activate API"
4.2 Take care that the IP your CheckMK server ist listed in the input field "Allow API access from these IPs/CIDR network notations"
5. Save your changes and transfer the show API key to your CheckMK instance
## Version history: ## Version history:
2024/01/26 1.2.0 Added password store option for the API key, added version check
2023/09/22: 1.1.4 Bugfix for the last bugfix :-) 2023/09/22: 1.1.4 Bugfix for the last bugfix :-)
2023/09/22: 1.1.3 Bugfix for usage calculation if quota is not set (mailboxes) 2023/09/22: 1.1.3 Bugfix for usage calculation if quota is not set (mailboxes)

View File

@ -40,6 +40,9 @@ def check_mailcow_info(params, section):
levels_solr_documents = params["levels_solr_documents"] levels_solr_documents = params["levels_solr_documents"]
version = section[key]["version"] version = section[key]["version"]
git_version = section[key]["git_version"]
check_version_enabled = section[key]["check_version_enabled"]
update_available = section[key]["update_available"]
num_domains = section[key]["num_domains"] num_domains = section[key]["num_domains"]
num_mailboxes = section[key]["num_mailboxes"] num_mailboxes = section[key]["num_mailboxes"]
num_global_messages = section[key]["num_global_messages"] num_global_messages = section[key]["num_global_messages"]
@ -58,9 +61,18 @@ def check_mailcow_info(params, section):
yield(Metric("mc_solr_documents", solr_documents, levels=levels_solr_documents)) yield(Metric("mc_solr_documents", solr_documents, levels=levels_solr_documents))
# create overall result # create overall result
summary = f"Version is {version}" if check_version_enabled:
if update_available:
summary = f"Update available: Running version is {version}, Github version is: {git_version}"
state = State.WARN
else:
summary = f"No update available: Running version is {version}, Github version is: {git_version}"
state = State.OK
else:
summary = f"Version is {version}, Update check is disabled"
state = State.OK
details = f"Mailcow version: {version}\nNumber of domains: {num_domains}\nNumber of mailboxes: {num_mailboxes}\nNumber of messages: {num_global_messages}\n\nSolr size: {render.bytes(solr_size)}\nNumber of Solr documents: {solr_documents}" details = f"Mailcow version: {version}\nNumber of domains: {num_domains}\nNumber of mailboxes: {num_mailboxes}\nNumber of messages: {num_global_messages}\n\nSolr size: {render.bytes(solr_size)}\nNumber of Solr documents: {solr_documents}"
yield Result(state=State.OK, summary=summary, details=details) yield Result(state=state, summary=summary, details=details)
# Create result for number of domains # Create result for number of domains
warn, crit = levels_num_domains warn, crit = levels_num_domains
@ -103,6 +115,7 @@ def check_mailcow_info(params, section):
def parse_mailcow_info_section(string_table): def parse_mailcow_info_section(string_table):
#pprint(string_table)
parsed_data = { parsed_data = {
"mailcow" : {}, "mailcow" : {},
} }
@ -125,6 +138,17 @@ def parse_mailcow_info_section(string_table):
elif solr_size_unit == "GB": elif solr_size_unit == "GB":
solr_size = solr_size * 1024.0 * 1024.0 * 1024.0 solr_size = solr_size * 1024.0 * 1024.0 * 1024.0
solr_documents = int(line[6]) solr_documents = int(line[6])
git_version = line[7]
update_available = line[8]
if update_available == "True":
update_available = True
else:
update_available = False
check_version_enabled = line[9]
if check_version_enabled == "True":
check_version_enabled = True
else:
check_version_enabled = False
parsed_data["mailcow"]["version"] = version parsed_data["mailcow"]["version"] = version
parsed_data["mailcow"]["num_domains"] = num_domains parsed_data["mailcow"]["num_domains"] = num_domains
parsed_data["mailcow"]["num_mailboxes"] = num_mailboxes parsed_data["mailcow"]["num_mailboxes"] = num_mailboxes
@ -132,6 +156,10 @@ def parse_mailcow_info_section(string_table):
parsed_data["mailcow"]["solr_enabled"] = solr_enabled parsed_data["mailcow"]["solr_enabled"] = solr_enabled
parsed_data["mailcow"]["solr_size"] = solr_size parsed_data["mailcow"]["solr_size"] = solr_size
parsed_data["mailcow"]["solr_documents"] = solr_documents parsed_data["mailcow"]["solr_documents"] = solr_documents
parsed_data["mailcow"]["git_version"] = git_version
parsed_data["mailcow"]["update_available"] = update_available
parsed_data["mailcow"]["check_version_enabled"] = check_version_enabled
#pprint(parsed_data)
return parsed_data return parsed_data

View File

@ -24,6 +24,7 @@ OPTIONS:
-H, --hostname Hostname (FQDN or IP) of Nextcloud server -H, --hostname Hostname (FQDN or IP) of Nextcloud server
-k, --apikey API Key -k, --apikey API Key
-P, --port Port -P, --port Port
--check-version True|False If "True": Running version will be checked against official Github repo
--no-https True|False If "True": Disable HTTPS, use HTTP (not recommended!) --no-https True|False If "True": Disable HTTPS, use HTTP (not recommended!)
--no-cert-check True|False If "True": Disable TLS certificate check (not recommended!) --no-cert-check True|False If "True": Disable TLS certificate check (not recommended!)
-h, --help Show this help message and exit -h, --help Show this help message and exit
@ -40,12 +41,13 @@ mc_api_base = "api/v1/get"
opt_hostname = "" opt_hostname = ""
opt_apikey = "" opt_apikey = ""
opt_port = "" opt_port = ""
opt_check_version = False
opt_no_https = False opt_no_https = False
opt_no_cert_check = False opt_no_cert_check = False
short_options = 'hH:k:P:' short_options = 'hH:k:P:'
long_options = [ long_options = [
'hostname=', 'apikey=', 'port=', 'no-https=', 'no-cert-check=', 'help' 'hostname=', 'apikey=', 'port=', 'check-version=', 'no-https=', 'no-cert-check=', 'help'
] ]
domain_data = {} domain_data = {}
@ -121,6 +123,7 @@ def getOptions() -> None:
global opt_hostname global opt_hostname
global opt_apikey global opt_apikey
global opt_port global opt_port
global opt_check_version
global opt_no_https global opt_no_https
global opt_no_cert_check global opt_no_cert_check
@ -132,6 +135,11 @@ def getOptions() -> None:
opt_apikey = arg opt_apikey = arg
elif opt in ['-P', '--port']: elif opt in ['-P', '--port']:
opt_port = arg opt_port = arg
elif opt in ['--check-version']:
if arg == 'True':
opt_check_version = True
else:
opt_check_version = False
elif opt in ['--no-https']: elif opt in ['--no-https']:
if arg == 'True': if arg == 'True':
opt_no_https = True opt_no_https = True
@ -158,6 +166,7 @@ def showOptions() -> None:
print(f"Hostname: {opt_hostname}") print(f"Hostname: {opt_hostname}")
print(f"Username: {opt_apikey}") print(f"Username: {opt_apikey}")
print(f"Port: {opt_port}") print(f"Port: {opt_port}")
print(f"Check Version: {opt_check_version}")
print(f"No HTTPS: {opt_no_https}") print(f"No HTTPS: {opt_no_https}")
print(f"No TLS Check: {opt_no_cert_check}") print(f"No TLS Check: {opt_no_cert_check}")
home_path = os.getenv("HOME") home_path = os.getenv("HOME")
@ -272,8 +281,60 @@ def getMailboxInfo(headers: str, verify: bool, base_url: str) -> tuple:
sys.stderr.write(f"Request response code is {response.status_code} with URL {url}\n") sys.stderr.write(f"Request response code is {response.status_code} with URL {url}\n")
sys.exit(1) sys.exit(1)
def getGitVersion() -> str:
url = "https://api.github.com/repos/mailcow/mailcow-dockerized/releases/latest"
git_version = ""
response = requests.get(url)
if (response.status_code == 200):
jsdata = response.text
data = json.loads(jsdata) # returns a dictionary
git_version = data["tag_name"]
#print(git_version)
else:
sys.stderr.write(f"Request response code is {response.status_code} with URL {url}\n")
sys.exit(1)
return git_version
def compareVersions(mc_ver: str, git_ver: str) -> bool:
update_available = False
try:
mc_year, mc_month = mc_ver.split("-")
git_year, git_month = git_ver.split("-")
#print(mc_year, mc_month, git_year, git_month)
mc_year_int = int(mc_year)
git_year_int = int(git_year)
if git_year_int > mc_year_int:
update_available = True
else:
len_mc_month = len(mc_month)
len_git_month = len(git_month)
if len_mc_month == 2:
mc_month_int = int(mc_month)
elif len_mc_month == 3:
mc_month_int = int(mc_month[0:2])
mc_month_ver = mc_month[-1]
else:
pass
if len_git_month == 2:
git_month_int = int(git_month)
elif len_git_month == 3:
git_month_int = int(git_month[0:2])
git_month_ver = git_month[-1]
else:
pass
if git_month_int > mc_month_int:
update_available = True
elif len_mc_month == 2 and len_git_month == 3:
update_available = True
elif git_month_ver > mc_month_ver:
update_available = True
except:
pass
return update_available
#@debugLog #@debugLog
def getMailcowInfo(headers: str, verify: bool, base_url: str) -> str: def getMailcowInfo(headers: str, verify: bool, base_url: str) -> dict:
info_data = {}
url = f"{base_url}/status/version" url = f"{base_url}/status/version"
response = requests.get(url, headers=headers, verify=verify) response = requests.get(url, headers=headers, verify=verify)
if (response.status_code == 200): if (response.status_code == 200):
@ -282,7 +343,20 @@ def getMailcowInfo(headers: str, verify: bool, base_url: str) -> str:
#pprint(data) #pprint(data)
# get Mailcow version # get Mailcow version
mc_version = data["version"] mc_version = data["version"]
return mc_version # if enabled, check this version against the official Mailcow repo on Github
if opt_check_version:
git_version = getGitVersion()
update_available = compareVersions(mc_version, git_version)
#update_available = compareVersions("2023-01a", "2023-02")
#print(update_available)
info_data["git_version"] = git_version
info_data["update_available"] = update_available
else:
info_data["git_version"] = "Version check disabled"
info_data["update_available"] = False
info_data["mc_version"] = mc_version
info_data["check_version_enabled"] = opt_check_version
return info_data
else: else:
sys.stderr.write(f"Request response code is {response.status_code} with URL {url}\n") sys.stderr.write(f"Request response code is {response.status_code} with URL {url}\n")
sys.exit(1) sys.exit(1)
@ -346,11 +420,15 @@ def doCmkOutputMailboxes() -> None:
print(f"{mb};{active};{created};{modified};{name};{num_messages};{percent_in_use};{quota};{quota_used};{last_imap_login};{last_pop3_login};{last_smtp_login}") print(f"{mb};{active};{created};{modified};{name};{num_messages};{percent_in_use};{quota};{quota_used};{last_imap_login};{last_pop3_login};{last_smtp_login}")
#@debugLog #@debugLog
def doCmkOutputMailcow(version: str, num_domains: int, num_mailboxes: int, num_global_messages: int, solr_enabled: bool, solr_size: float, solr_documents: int) -> None: def doCmkOutputMailcow(version: str,
num_domains: int, num_mailboxes: int, num_global_messages: int,
solr_enabled: bool, solr_size: float, solr_documents: int,
git_version: str, update_available: bool, check_version_enabled: bool
) -> None:
print("<<<mailcow_info:sep(59)>>>") print("<<<mailcow_info:sep(59)>>>")
# strip semicolons, if present, since we use it as delimiter # strip semicolons, if present, since we use it as delimiter
version = version.replace(";", "") version = version.replace(";", "")
print(f"{version};{num_domains};{num_mailboxes};{num_global_messages};{solr_enabled};{solr_size};{solr_documents}") print(f"{version};{num_domains};{num_mailboxes};{num_global_messages};{solr_enabled};{solr_size};{solr_documents};{git_version};{update_available};{check_version_enabled}")
''' '''
@ -449,11 +527,19 @@ def main():
num_mailboxes, num_global_messages = getMailboxInfo(headers, verify, base_url) num_mailboxes, num_global_messages = getMailboxInfo(headers, verify, base_url)
# get global Mailcow info # get global Mailcow info
solr_enabled, solr_size, solr_documents = getSolrInfo(headers, verify, base_url) solr_enabled, solr_size, solr_documents = getSolrInfo(headers, verify, base_url)
mailcow_version = getMailcowInfo(headers, verify, base_url) mailcow_info = getMailcowInfo(headers, verify, base_url)
mailcow_version = mailcow_info["mc_version"]
github_version = mailcow_info["git_version"]
update_available = mailcow_info["update_available"]
check_version_enabled = mailcow_info["check_version_enabled"]
# create agent output # create agent output
doCmkOutputDomains() doCmkOutputDomains()
doCmkOutputMailboxes() doCmkOutputMailboxes()
doCmkOutputMailcow(mailcow_version, num_domains, num_mailboxes, num_global_messages, solr_enabled, solr_size, solr_documents) doCmkOutputMailcow(mailcow_version,
num_domains, num_mailboxes, num_global_messages,
solr_enabled, solr_size, solr_documents,
github_version, update_available, check_version_enabled
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -10,6 +10,7 @@ description:
Got to configuration settings and create a new API key (read-only access is sufficient). Got to configuration settings and create a new API key (read-only access is sufficient).
Allow API access to at least your CheckMK server IP address. Allow API access to at least your CheckMK server IP address.
Shows several information about a Mailcow instance, e.g. number of domains/mailboxes/messages. Shows several information about a Mailcow instance, e.g. number of domains/mailboxes/messages.
The check will raise WARN if an update is available (and if this check is enabled)
The check will raise WARN/CRIT if the number of domains is above the configurable levels. The check will raise WARN/CRIT if the number of domains is above the configurable levels.
The check will raise WARN/CRIT if the number of mailboxes is above the configurable levels. The check will raise WARN/CRIT if the number of mailboxes is above the configurable levels.
The check will raise WARN/CRIT if the number of messages is above the configurable levels. The check will raise WARN/CRIT if the number of messages is above the configurable levels.

View File

@ -3,6 +3,7 @@ def agent_mailcow_arguments(params, hostname, ipaddress):
"--hostname", params["hostname"], "--hostname", params["hostname"],
"--apikey", passwordstore_get_cmdline("%s", params["apikey"]), "--apikey", passwordstore_get_cmdline("%s", params["apikey"]),
"--port", params["port"], "--port", params["port"],
"--check-version", params["check_version"],
"--no-https", params["no_https"], "--no-https", params["no_https"],
"--no-cert-check", params["no_cert_check"], "--no-cert-check", params["no_cert_check"],
ipaddress, ipaddress,

View File

@ -22,10 +22,12 @@ def _parameter_spec_mailcow_info():
elements=[ elements=[
Integer( Integer(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=100, default_value=100,
), ),
Integer( Integer(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=200, default_value=200,
) )
], ],
@ -35,10 +37,12 @@ def _parameter_spec_mailcow_info():
elements=[ elements=[
Integer( Integer(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=500, default_value=500,
), ),
Integer( Integer(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=1000, default_value=1000,
) )
], ],
@ -48,10 +52,12 @@ def _parameter_spec_mailcow_info():
elements=[ elements=[
Integer( Integer(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=100000, default_value=100000,
), ),
Integer( Integer(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=250000, default_value=250000,
) )
], ],
@ -61,11 +67,13 @@ def _parameter_spec_mailcow_info():
elements=[ elements=[
Float( Float(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=4096.0, default_value=4096.0,
unit="MBytes", unit="MBytes",
), ),
Float( Float(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=8192.0, default_value=8192.0,
unit="MBytes", unit="MBytes",
) )
@ -76,10 +84,12 @@ def _parameter_spec_mailcow_info():
elements=[ elements=[
Integer( Integer(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=20000, default_value=20000,
), ),
Integer( Integer(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=40000, default_value=40000,
) )
], ],

View File

@ -40,10 +40,12 @@ def _parameter_spec_mailcow_mailboxes():
elements=[ elements=[
Integer( Integer(
title=_("Warning at"), title=_("Warning at"),
size=32,
default_value=1000, default_value=1000,
), ),
Integer( Integer(
title=_("Critical at"), title=_("Critical at"),
size=32,
default_value=2500, default_value=2500,
) )
], ],

View File

@ -43,6 +43,8 @@ def _valuespec_special_agent_mailcow():
("port", TextAscii(title=_("Port"), ("port", TextAscii(title=_("Port"),
allow_empty=True, allow_empty=True,
help=_("Specify port if not listening to HTTPS/HTTP, optional"))), help=_("Specify port if not listening to HTTPS/HTTP, optional"))),
("check_version", Checkbox(title=_("Check version"),
help=_("Checks the running version against the public Github repo"))),
("no_https", Checkbox(title=_("Disable HTTPS"), ("no_https", Checkbox(title=_("Disable HTTPS"),
help=_("Activate to disable TLS encryption (not recommended), optional"))), help=_("Activate to disable TLS encryption (not recommended), optional"))),
("no_cert_check", Checkbox(title=_("Disable certificate validation"), ("no_cert_check", Checkbox(title=_("Disable certificate validation"),

BIN
mkp/Mailcow-1.2.0.mkp Normal file

Binary file not shown.